字符串和正则
更多:【Python3学习笔记】之【Python基础——字符串】【Python3学习笔记】之【Python高级——正则表达式方法】【Python3学习笔记】之【Python高级——正则表达式对象】
1 反转字符串
st = 'python'
# 方法一
print(''.join(reversed(st)))
# 方法二
print(st[::-1])
2 字符串切片操作
字符串切片操作——查找替换3或5的倍数
>>> [str("java"[i%3*4:]+"python"[i%5*6:] ori) foriinrange(1,15)]
['1','2','java','4','python','java','7','8','java','python','11','java','13','14']
3 join串联字符串
lst = ['1', 'python', '2', 'java', '3', 'c']
print(','.join(lst))
# 1,python,2,java,3,c
4 字符串的字节长度
myStr = '你好'
print(len(myStr.encode('utf-8')))
# 6
5 查找第一个匹配串
import re
MyStr = 'i love python forever!'
pat = 'python'
print(re.search(pat, MyStr))
# <re.Match object; span=(7, 13), match='python'>
6 查找所有字符的的索引
import re
MyStr = 'i love python forever!'
pat = 'o'
r = re.finditer(pat, MyStr)
for i in r:
print(i)
# <re.Match object; span=(3, 4), match='o'>
# <re.Match object; span=(11, 12), match='o'>
# <re.Match object; span=(15, 16), match='o'>
7 \d 匹配数字[0-9]
import re
string = '不要998!只要99!'
pat = r'\d+' # +表示匹配数字(\d表示数字的通用字符)1次或多次
r = re.findall(pat, string)
print(r)
# ['998', '99']
8 匹配浮点数和整数
import re
string = '不要998!只要9.9!'
pat = r'\d+\.?\d+|\d+' # ?表示匹配小数点(\.)0次或1次,A|B,匹配A失败才匹配B
r = re.findall(pat, string)
print(r)
# ['998', '9.9']
9 ^匹配字符串的开头
import re
string1 = 'hello'
string2 = 'apple'
string3 = 'email'
pat = r'^[abcde].*' # 查找以字符a,b,c,d或e开始的字符串
r1 = re.findall(pat, string1)
r2 = re.findall(pat, string2)
r3 = re.findall(pat, string3)
print(r1)
print(r2)
print(r3)
# []
# ['apple']
# ['email']
10 re.I 忽略大小写
import re
string1 = 'apple'
string2 = 'Apple'
pat = r'^[abcde].*' # 查找以字符a,b,c,d或e开始的字符串
r1 = re.findall(pat, string1, re.I)
r2 = re.findall(pat, string2, re.I)
print(r1)
print(r2)
# ['apple']
# ['Apple']
11 理解compile的作用
如果要做很多次匹配,可以先编译匹配串:
import re
pat = re.compile(r'\w+') # \W 匹配不是数字和字母的字符
target1 = pat.findall('1!2@3#4$abc')
target2 = pat.search('1!2@3#4$abc')
if target1:
print(target1)
# ['1', '2', '3', '4', 'abc']
if target2:
print(target2.group(0))
# 1
12 使用()捕获单词,不想带空格
使用()捕获
import re
s = 'i love python'
pat = r'\s?([a-zA-Z]+)' # \s表示匹配任意空白字符,括号表示要提取的内容
r = re.findall(pat, s)
print(r)
# ['i', 'love', 'python']
13 split分割单词
使用以上方法分割单词不是简洁的,仅仅是为了演示。分割单词最简单还是使用split函数。
import re
s = 'i love python'
pat = r'\s'
r = re.split(pat, s)
print(r)
# ['i', 'love', 'python']
14 match与search
注意match,search等的不同:
import re
s = 'Hello World!'
pat = re.compile('World')
print(pat.match(s)) # match只从起始位置匹配
# None
print(pat.search(s)) # search会搜索整个字符串,
# <re.Match object; span=(6, 11), match='World'>
# 两者匹配成功都是返回的一个match对象
15 替换匹配的子串
sub函数实现对匹配子串的替换
import re
s = 'i am 123,he is 456'
pat = re.compile(r'\d+') # 要替换内容的正则,这里为数字
r = pat.sub('pig', s) # 要替换成的内容,与目标字符串
print(r)
# i am pig,he is pig
16 贪心捕获
(.*)表示捕获任意多个字符,尽可能多的匹配字符
import re
s = r'<h>abc</h><div>def</div>ghi<div>jkl</div>'
pat = re.compile(r'<div>(.*)</div>') # 贪婪模式
r = pat.findall(s)
print(r)
# ['def</div>ghi<div>jkl']
非贪心捕获
仅添加一个问号(?),得到结果完全不同,这是非贪心匹配,通过这个例子体会贪心和非贪心的匹配的不同。
import re
s = r'<h>abc</h><div>def</div>ghi<div>jkl</div>'
pat = re.compile(r'<div>(.*?)</div>') # 非贪婪模式
r = pat.findall(s)
print(r)
# ['def', 'jkl']
18 常用元字符总结
字符 | 含义 |
. | 匹配任意字符 |
^ | 匹配字符串开始位置 |
$ | 匹配字符串中结束的位置 |
* | 前面的原子重复0次、1次、多次 |
? | 前面的原子重复0次或者1次 |
+ | 前面的原子重复1次或多次 |
{n} | 前面的原子出现了 n 次 |
{n,} | 前面的原子至少出现 n 次 |
{n,m} | 前面的原子出现次数介于 n-m 之间 |
() | 分组,需要输出的部分 |
19 常用通用字符总结
字符 | 含义 |
\s | 匹配空白字符 |
\w | 匹配任意字母/数字/下划线 |
\W | 和小写 w 相反,匹配任意字母/数字/下划线以外的字符 |
\d | 匹配十进制数字 |
\D | 匹配除了十进制数以外的值 |
[0-9] | 匹配一个0-9之间的数字 |
[a-z] | 匹配小写英文字母 |
[A-Z] | 匹配大写英文字母 |
20 密码安全检查
密码安全要求:1)要求密码为6到20位; 2)密码只包含英文字母和数字
import re
s1 = 'asdf!@'
s2 = 'asd123'
pat = re.compile(r'[\da-zA-Z]{6,20}')
r1 = pat.findall(s1)
r2 = pat.findall(s2)
print(r1)
print(r2)
# []
# ['asd123']
选用最保险的fullmatch方法,查看是否整个字符串都匹配:
import re
s1 = 'asdf!@'
s2 = 'asd123'
pat = re.compile(r'[\da-zA-Z]{6,20}')
r1 = pat.fullmatch(s1)
r2 = pat.fullmatch(s2)
print(r1)
print(r2)
# None
# <re.Match object; span=(0, 6), match='asd123'>
21 爬取百度首页标题
import re
from urllib import request
# 爬取首页内容
data = request.urlopen('http://www.baidu.com').read().decode()
# 标题正则
pat = r'<title>(.*?)</title>'
r = re.search(pat, data)
print(r.group())
# <title>百度一下,你就知道</title>
22 批量转化为驼峰格式(Camel)
分析过程:
# \s 指匹配: [ \t\n\r\f\v]
# A|B:表示匹配A串或B串
# re.sub(pattern, newchar, string):
# substitue代替,用newchar字符替代与pattern匹配的字符所有
# title(): 将单词首字母转化为大写,例子:
# 'Hello world'.title() # 'Hello World'
初步尝试:
import re
s = 'some_database_file_name'
pat = r'(\s|_|-)+'
r = re.sub(pat, ' ', s) # some database file name
r = r.title().replace(' ', '') # SomeDatabaseFileName
r = r[0].lower() + r[1:]
print(r)
# someDatabaseFileName
批量转化:
import re
# 转换方法
def camel(s):
r = re.sub(r'(\s|_|-)+', ' ', s).title().replace(' ', '') # some database file name
return r[0].lower() + r[1:]
# 批量转换
def database_lst_to_camel(lst):
return [camel(s) for s in lst]
database_names = ['student_id', 'student\tname', 'student-add']
print(database_lst_to_camel(database_names))
# ['studentId', 'studentName', 'studentAdd']
23 str1是否为str2的permutation
排序词(permutation):两个字符串含有相同字符,但字符顺序不同。
from collections import defaultdict
def is_permutation(str1, str2):
if str1 is None or str2 is None:
return False
if len(str1) != len(str2):
return False
unq_s1 = defaultdict(int)
unq_s2 = defaultdict(int)
# 将每个字母计数
for c1 in str1:
unq_s1[c1] += 1
for c2 in str2:
unq_s2[c2] += 1
return unq_s1 == unq_s2
s1 = 'nice'
s2 = 'cine'
s3 = 'eleven+two'
s4 = 'twelve+one'
print(is_permutation(s1, s2)) # True
print(is_permutation(s3, s4)) # True
print(is_permutation(s1, s3)) # False
当然,方法仅供学习,更简单的方法可以直接用:
from collections import Counter
# 检查两个字符串是否相同字母异序词,简称:互为变位词
def anagram(str1, str2):
return Counter(str1) == Counter(str2)
anagram('eleven+two', 'twelve+one') # True 这是一对神器的变位词
anagram('eleven', 'twelve') # False
24 str1是否由str2旋转而来
stringbook旋转后得到bookstring,写一段代码验证str1是否为str2旋转得到。
- 思路:
转化为判断:str1是否为str2+str2的子串
def is_rotation(s1, s2) -> bool:
if s1 is None or s2 is None:
return False
elif len(s1) != len(s2):
return False
return s1 in s2 * 2
print(is_rotation('stringbook', 'bookstring')) # True
print(is_rotation('stringbook', 'koobstring')) # False
25 正浮点数
从一系列字符串中,挑选出所有正浮点数。
- 思路:正则
^[1-9]\d*\.\d*$
^表示字符串开始
[1-9]表示数字1,2,3,4,5,6,7,8,9
^[1-9]连起来表示以数字1-9作为开头
\d表示一位0-9的数字
*表示前一位字符出现 0 次,1 次或多次
\d*表示数字出现 0 次,1 次或多次
\.表示小数点
$表示字符串以前一位的字符结束
但是这样只能匹配大于1.0的浮点数,因为当数字为000.2时,他也能匹配到,可是它不是浮点数。所以还需要写0.0到1.0之间的浮点数:
^0\.\d*[1-9]\d*$
综合:
^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$