欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

荐 python基础四: python字符串操作与正则初探

程序员文章站 2022-07-03 18:19:31
1. 写在前面今天这篇文章整理python有关字符串的一些知识, 主要包括字符串的一些基本操作, 像字符串的翻转, 切片, 串联, 分割, 替换等。 然后再整理正则的一些基本知识。依然是从使用的角度, 并后期不断补充。Ok, let’s go!2. 字符串的基本操作python字符串的基本操作还是有必要掌握的, 并且非常有用, 常见的像字符串的反转, 切片, 串联, 分割替换等。s = 'python'##反转操作有两种方式re = ''.join(reversed(s))re1 = s[...

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)]    # 这个写法牛逼

字符串的串联与分割, joinsplit可以看做一对互逆操作。

# 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+'
荐
                                                        python基础四: python字符串操作与正则初探

# 所以需要修改正则表达式
s = '一共20行代码运行时间13.59s'

pat = r'\d+\.?\d+'
r = re.findall(pat, s)
r # ['20', '13.59']

但是上面这种写法有个问题, 由于出现了两个\d+, 这个表示至少有一位数字, 因此上面的表达式至少匹配两位数。 所以需要把后面的+改成*。
荐
                                                        python基础四: python字符串操作与正则初探

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*$: 这个是正确的
    荐
                                                        python基础四: python字符串操作与正则初探
    代码如下:
    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 串,正则分解图见下:
荐
                                                        python基础四: python字符串操作与正则初探
代码如下:

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也看成字符
荐
                                                        python基础四: python字符串操作与正则初探

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/ 这个网站上输入字符串, 输入写的正则表达式就可以匹配出字符,这样就能看到自己写的正则表达式是不是正确

本文地址:https://blog.csdn.net/wuzhongqiang/article/details/107303694