1. 写在前面
今天这篇文章整理python有关字符串的一些知识, 主要包括字符串的一些基本操作, 像字符串的翻转, 切片, 串联, 分割, 替换等。 然后再整理正则的一些基本知识。依然是从使用的角度, 并后期不断补充。
Ok, let’s go!
2. 字符串的基本操作
python字符串的基本操作还是有必要掌握的, 并且非常有用, 常见的像字符串的反转, 切片, 串联, 分割替换等。
s = 'python'
##反转操作有两种方式
re = ''.join(reversed(s))
re1 = s[::-1] # 'nohtyp'
# 字符串的切片操作
# 在指定的索引处生成字符串
# FizzBuzz是一个简单的小游戏。游戏规则如下:从1开始往上数数,
# 当遇到3的倍数的时候,说fizz,当遇到5的倍数,说buzz,当遇到15的倍数,就说fizzbuzz,其他情况下则正常数数。
java, python = 'fizz', 'buzz'
jl, pl = len(java), len(python)
[str(java[i%3*jl:]+python[i%5*pl:] or i) for i in range(1, 20)] # 这个写法牛逼
字符串的串联与分割, join
和split
可以看做一对互逆操作。
# join串联字符串
mystr = ['I', 'love', 'python']
res = '_'.join(mystr) # 下划线_连接mystr 'I_love_python'
# 字符串分割split 根据指定的字符或者字符串 join和split可以看做一对互逆操作
res.split('_') # ['I', 'love', 'python']
字符串的替换操作replace
# 替换replace
# 把下面的小写o换成大写的O
s = 'i love python'.replace('o', 'O')
s # 'i lOve pythOn'
判断串a是否是串b的子串
# 法一: 使用in
a = 'our'
b = 'flour'
a in b
# 法二: 使用find直接返回a的最小索引
c = 'mo'
b.find(a) # 2
b.find(c) # -1 表示没找到, 就不是子串
strip()
函数去掉字符串两段的空格
# 去空格 清洗字符串时,位于字符串开始和结尾的空格,有时需要去掉,strip 方法能实现。
a = ' \tI love python \b\n'
a.strip() # 'I love python \x08'
# 字符串的字节长度
len('I love python') # 13
len('我爱中国 ') # 5
3. 正则初探
字符串封装的方法,处理一般的字符串操作,还能应付。但是,稍微复杂点的字符串处理任务,需要靠正则表达式,简洁且强大。
首先, 要认识常用的元字符:
-
.
: 匹配除"\n"和"\r"之外的任何单个字符。 -
^
: 匹配字符串开始位置 -
$
: 匹配字符串中结束的位置 -
*
: 前面的原子重复0次、1次、多次 -
?
: 前面的原子重复0次或者1次 -
+
: 前面的原子重复1次或者多次 -
{n}
: 前面的原子出现了n次 -
{n,}
: 前面的原子至少出现n次 -
{n, m}
: 前面原子出现次数介于n-m之间 -
()
分组, 输出需要的部分
再认识常用的通用字符:
-
\s
: 匹配空白字符 -
\w
: 匹配任意字母/数字/下划线 -
\W
: 和小写w相反, 匹配任意字母/数字/下划线以外的字符 -
\d
: 匹配十进制数字 -
\D
: 匹配除了十进制以外的值 -
[0-9]
: 匹配一个0-9之间的数字 -
[a-z]
: 匹配小写英文字母 -
[A-Z]
: 匹配大写英文字母
正则表达式,常会涉及到以上这些元字符或通用字符,下面通过 14 个细分的与正则相关的小功能,讨论正则表达式。
首先先导入包:import re
3.1 search和match
search方法, 找到子串中第一个匹配的位置, 在字符串的任意位置进行匹配, 而match方法啊, 只在原字符串的开始位置匹配。
s = 'flourish'
# 寻找模式串our, 使用match方法
recom = re.compile('our')
m = recom.match(s) # # 返回 None,找不到匹配, 这是因为match只能匹配以our开头的字符串, 比如ourself
type(m)
# search方法
res = recom.search(s)
res.span() # OK, 匹配成功,our 在原字符串的起始索引为 2 (2, 5)
s1 = 'ourselfves'
recom = re.compile('our') # 编译一个正则表达式模式,返回一个模式对象
m = recom.match(s1)
m.span()
3.2 finditer匹配迭代器
使用正则模块,finditer
方法,返回所有子串匹配位置的迭代器。 通过返回的对象 re.Match,使用它的方法 span()
找出匹配位置。
s = 'hello world hello how are you helloool'
pat = 'hello'
r = re.finditer(pat, s)
for i in r:
print(i.span())
## 结果 三个hello的起始和终止字符位置
(0, 5)
(12, 17)
(30, 35)
3.3 findall所有匹配
正则模块,findall
方法能查找出子串的所有匹配。
# 我这里有个字符串, 目标时找出所有数字
s = '一共20行代码运行时间13.59s'
pat = r'\d+' # 这个表示找里面的数字
r = re.findall(pat, s)
print(r) # 找到了['20', '13', '59'], 但是我们期望找到[20, 13.59]
所以上面我们的正则表达式写法有点问题, 可以简单分析一下, 如果找出上面的20和13.50呢?
案例1: 匹配浮点数和整数
-
?
: 表示前一个字符匹配0次或者1次 -
.?
: 表示匹配小数点0次或者1次
匹配浮点数和整数的第一版正则表达式: r'\d+\.?\d+'
# 所以需要修改正则表达式
s = '一共20行代码运行时间13.59s'
pat = r'\d+\.?\d+'
r = re.findall(pat, s)
r # ['20', '13.59']
但是上面这种写法有个问题, 由于出现了两个\d+, 这个表示至少有一位数字, 因此上面的表达式至少匹配两位数。 所以需要把后面的+改成*。
s = '一共2行代码运行时间13.59s'
pat = r'\d+\.?\d+'
r = re.findall(pat, s)
r # 13.59
pat1 = r'\d+\.?\d*'
r1 = re.findall(pat1, s)
r1 # ['2', '13.59']
案例二: 匹配所有正整数
下面三种方式, 先分析一下哪个正确:
-
^\d*$
: 这个会匹配到0 -
^[1-9]
*: 会匹配到1.的1, 不是完全匹配 ^[1-9]\d*$
: 这个是正确的
代码如下:
s = [-16, 1.5, 11.43, 10, 5]
pat = r'^[1-9]\d*$'
[i for i in s if re.match(pat, str(i))] # [10, 5]
3.4 re.I 忽略大小写
查找字符串中字符位置的时候, re.I
是方法的可选参数, 表示忽略大小写
s = 'That'
pat = r't'
r = re.finditer(pat, s, re.I)
for i in r:
print(i.span()) # (0,1) (3,4)
3.5 split分割单词
正则模块中 split
函数强大,能够处理复杂的字符串分割任务。如果一个规则简单的字符串, 直接使用字符串的split
函数
s = 'id\tname\taddress'
# 根据字符串\t分割
s.split('\t') # ['id', 'name', 'address']
字符串中的split函数只能处理一些简单的分割操作, 对于复杂的字符串, 就需要用到正则里面的split。
# 但是对于复杂的字符串, split函数就不行了
s = 'This,,, module ; \t provides|| regular;'
# 这个如果要出单词来的话, 就得研究研究字符串的规则了。 每个单词用了不同的符号进行分割, 并且有的出现还不止一次
# 所以得用个正则表达式匹配这些符号 看一下有[,\s;|]这是那些字符,\s表示空字符。 有的还不止出现一次, 那么就是[,\s;|] +
words = re.split('[,\s;|]+', s)
words # ['This', 'module', 'provides', 'regular', '']
3.6 sub替换字符串
正则模块,sub 方法,替换匹配到的子串。
content = 'hello 1234, hello 1234'
pat = re.compile(r'\d+') # 表示一位或者多位的十进制数
m = pat.sub('555', content)
m # 'hello 555, hello 555'
3.7 compile预编译
如果要用同一匹配模式, 做很多次匹配, 可以使用compile预先编译串。
案例:从一系列字符串中,挑选出所有正浮点数。
正则表达式为:^[1-9]\d.\d|0.\d*[1-9]\d*$
,字符 a|b 表示 a 串匹配失败后,才执行 b 串,正则分解图见下:
代码如下:
s = [-16, 'good', 1.5, 0.2, -0.1, '11.43', 10, '5e10', .8, 8., '0.0']
rec = re.compile(r'^[1-9]*\.\d*|0\.\d*[1-9]\d*$')
[i for i in s if rec.match(str(i))] # [1.5, 0.2, '11.43', 0.8, 8.0]
3.8 贪心捕获
正则模块中,根据某个模式串,匹配到结果。
content = """<h>ddedadsad</h><div>graph</div><div>dafaf</div><div>math</div>"""
# 下面匹配<div>标签里的内容
result = re.findall(r'<div>.*</div>', content)
result # ['<div>graph</div><div>dafaf</div><div>math</div>']
# 如果不想保留开始和结尾的<div></div>, 需要用一对()去捕获
result = re.findall(r'<div>(.*)</div>', content)
result # 少了两头的<div></div>
(.*)
表示捕获任意多个字符, 尽可能多的匹配字符, 也被称为贪心捕获。 .
表示匹配除换行外的任意字符。 所以上面的结果中,会把div、/div
也看成字符
3.9 非贪心捕获
上面如果只想得到<div></div>
中间的内容, 可以使用非贪心捕获。
result = re.findall(r'<div>(.*?)</div', content)
result # ['graph', 'dafaf', 'math']
(.*?)
: 被称为非贪心捕获, 加了个问号, 就表示着要捕捉前面的这种模式0次或者多次, 这时候()
分组的时候,就把div, /div
分成了一组, 里面的内容分成了一组。 可以和上面的贪心对比一下, 上面的贪心在分组的时候, 只把最外面的一组div, /div
单独分成了一组。
下面对比一下贪心捕获和非贪心捕获:
s = 'abcabdecd'
res = re.findall(r'ab.*?c', s) # 非贪婪
res1 = re.findall(r'ab.*c', s) # 贪婪
res # ['abc', 'abdec']
#res1 # ['abcabdc']
感觉贪婪搜索想一遍找完尽可能多的, 而非贪婪搜索多次找尽可能少的。
比如上面这个例子, 贪婪搜索的匹配过程是看到了前两个字符ab, 然后看到了第一个c, 做个标记,这时候其实匹配成功, 但是它不死心, 依然往后找, 想看看还有没有c, 这样能匹配更多的字符, 结果看到了后面的c, 挺高兴,做个标记,但依然不死心,还往后找, 发现后面没有能匹配的了,所以就返回来了上次做标记的那一串。但这样也比单纯的abc长。
非贪婪搜索是这样的: 从头开始看到了ab, 然后又看到了c很知足了,先返回来一个结果abc, 然后再从返回的这个地方再往后找, 又看到了ab,再往后看到了c, 又符合规则, 再返回一个结果来abdc, 再从返回的地方往后找发现没了,结束。
https://regex101.com/ 这个网站上输入字符串, 输入写的正则表达式就可以匹配出字符,这样就能看到自己写的正则表达式是不是正确
3. python的string模块
这里面有一些非常常用的字符长处理函数和方法, 如果知道的话,对于日常的应用还是非常方便的。这里记录一些常用方法和常用的字符串常量。
3.1 常用方法
查找:
-
str.count(s)
返回字符串s在str中出现的次数 -
str.endswith(s)
判断字符串str是否以字符串s结尾 -
str.find(s)
返回字符串s在字符串str中的位置索引,没有则返回-1 -
str.index(s)
和find()方法一样,但是如果s不存在于str中则会抛出异常 -
str.rfind(s)
类似于 find()函数,不过是从右边开始查找 -
str.rindex(s)
类似于 index(),不过是从右边开始
判断:
-
str.isalnum()
如果str至少有一个字符并且都是字母或数字则返回True,否则返回False -
str.isalpha()
如果str至少有一个字符并且都是字母则返回True,否则返回False -
str.isdigit()
如果str只包含数字则返回 True 否则返回 False -
str.islower()
如果str存在区分大小写的字符,并且都是小写则返回True 否则返回False -
str.isupper()
如果str存在区分大小写的字符,并且都是大写则返回True 否则返回False
转换:
-
str.capitalize()
把字符串的首字母大写 -
str.lower()
转换str中所有大写字符为小写 -
str.upper()
返回str所有字符为大写的字符串 -
str.lstrip()
去掉str左边的不可见字符 -
str.strip()
等于同时执行rstrip()和lstrip() -
str.replace(a, b)
将字符串str中的a替换成b
切分:
-
str.split(s)
以s为分隔符切片str -
str.partition(s)
用s将str切分成三个值
3.2 字符串常量
-
string.ascii_lowercase
小写字母’abcdefghijklmnopqrstuvwxyz’ -
string.ascii_uppercase
大写的字母’ABCDEFGHIJKLMNOPQRSTUVWXYZ’ -
string.digits
数字0到9的字符串:’0123456789’