Python 字符串匹配、搜索及替换 您所在的位置:网站首页 python替换字符串中的字符for Python 字符串匹配、搜索及替换

Python 字符串匹配、搜索及替换

2024-01-27 02:55| 来源: 网络整理| 查看: 265

文章目录 字符串匹配、搜索及替换字符串开头或结尾匹配str.startswith() 和 str.endswith() 用 Shell 通配符匹配字符串fnmatch() 和 fnmatchcase() 字符串匹配和搜索str.find()正则表达式及 re 模块re.match()re.compile()re.findall()re.finditer() 字符串搜索和替换str.replace()re.sub()re.subn()

字符串匹配、搜索及替换

在处理字符串中,有时会需要定位字符串,然后对字符串进行相应的处理。

字符串开头或结尾匹配

对于字符串的检查,可以通过特定的文本模式进行匹配。在 Python 内置类型中也提供了 str.startswith() 和 str.endswith() 方法对字符串进行开头或结尾的检查。

str.startswith() 和 str.endswith()

str.startswith() 和 str.endswith() 是检查字符串开头或结尾的一个简单方法。例如文件的后缀,URL 跳转协议,示例如下:

>>> filename = "string.txt" >>> filename.endswith('.txt') True >>> filename.startswith('file:') False >>> url = 'http://www.python.org' >>> url.startswith('http:') True

如果要检查多种匹配的可能,可以提供一个元组,将所有的匹配项写入元组中,然后传给 startswith() 和 endswith() 方法的第一个参数中:

>>> import os >>> filenames = os.listdir('.') >>> filenames ['tools', 'list_links.txt', 'access_test.py', 'control_roll.py', 'access.log', 'data.xls'] >>> [name for name in filenames if name.endswith(('.py', '.txt'))] ['list_links.txt', 'access_test.py', 'control_roll.py'] >>> any(name.endswith('.py') for name in filenames) True

这里需要说明的是:startswith() 和 endswith() 两个方法的第一个参数,如果是要传入多种匹配选项,需要传入的类型必须是 tuple,如果有 list 和 set 类型的选项,可以使用 tuple() 方法将已有选项进行转换。

上面提及的 startswith() 和 endswith() 方法有一种替代的方法,就是切片操作,但是代码看起来会相对繁冗。示例如下:

>>> filename = 'string.txt' >>> filename[-4:] == '.txt' True >>> url = 'http://www.python.org' >>> url[:5] == 'http:' or url[:6] == 'https:' or url[:4] == 'ftp:' True

还有另外一种方法是通过正则表达式去实现,示例如下:

>>> import re >>> url = 'http://www.python.org' >>> re.match('http:|https:|ftp:', url)

正则表达式方法放在这里,的确能够解决问题,但是有点大材小用。而且,Python 内置的 startswith() 和 endswith() 方法,对于这种简单匹配运行会更快。

用 Shell 通配符匹配字符串

除了通过你内置类型的方法对字符串进行匹配,还可以使用 Unix Shell 中的常用的通配符(比如 *.py,Dat[0-9]*.csv 等)去匹配字符串。

fnmatch() 和 fnmatchcase()

Python 提供的 fnmatch 模块就有两个函数 fnmatch() 和 fnmatchcase() 可以用来实现这样的匹配。示例如下:

>>> from fnmatch import fnmatch, fnmatchcase >>> fnmatch('string.txt','*.txt') True >>> fnmatch('string.txt','?ing.txt') False >>> fnmatch('string.txt','?tring.txt') True >>> fnmatch('Dat57.csv','Dat[0-9]*') True >>> names = ['Dat1.csv','Dat2.csv','config.ini','test.py'] >>> [name for name in names if fnmatch(name, 'Dat*.csv')] ['Dat1.csv', 'Dat2.csv']

但是需要注意的是 fnmatch() 函数,在使用不同的底层操作系统的大小写敏感规则是不同的。这里是因为这个函数中的形参都会使用 os.path.normcase() 进行大小写正规化,在 Linux,Mac 中,会原样返回;而在 Windows 中,这里会将字符转换为小写返回。示例如下:

>>> # On Mac >>> fnmatch('string.txt', '*.TXT') False >>> # On Windows >>> fnmatch('string.txt', '*.TXT') True

如果对于这个大小写敏感规则很在意的情况下,可以使用 fnmatchcase() 来替代。这个函数完全区分大小写。示例如下:

>>> fnmatchcase('string.txt', '*TXT') False

fnmatch() 函数匹配的能力介于简单字符串方法和正则表达式之间。如果在数据处理操作中,能够用简单通配符匹配的情况下,这是一个可以考虑的方案。

如果需求中需要做文件名的匹配,建议使用 glob 模块中的方法。具体的使用方法可参考下面的官方文档:

https://docs.python.org/3.6/library/glob.html

字符串匹配和搜索

如果需要匹配的字符串比较简单,通常情况下,需要调用基本字符串方法就可以。

str.find()

除了前面介绍的 str.endswith() 和 str.startswith() 的方法,还有 str.find() 方法,这个方法用于查找匹配项第一次出现的位置,这些都能够实现简单的匹配搜索,示例如下:

>>> text = 'yeah, but no, but yeah, but no, but yeah' >>> # 匹配开头或结尾 ... >>> text.startswith('yeah') True >>> text.endswith('no') False >>> # 搜索第一次出现位置 ... >>> text.find('no') 10 正则表达式及 re 模块

对于一些复杂的匹配,则需要借助正则表达式1和 re 模块。

re.match()

下面举例简单介绍匹配数字格式的日期字符串,示例如下:

>>> text1 = '11/27/2012' >>> text2 = 'Nov 27, 2012' >>> import re >>> # 简单匹配一个或多个数字 ... >>> if re.match(r'\d+/\d+/\d+', text1): ... print('yes') ... else: ... print('no') ... yes >>> if re.match(r'\d+/\d+/\d+', text2): ... print('yes') ... else: ... print('no') ... no >>> re.compile()

如果需要使用同个模式进行多次匹配,那么可以考虑将模式字符串编译为模式对象。re.compile() 提供编译正则表达式字符串,示例如下:

>>> pattern = re.compile(r'\d+/\d+/\d+') >>> if re.match(pattern, text1): ... print('yes') ... else: ... print('no') ... yes >>> if re.match(pattern, text2): ... print('yes') ... else: ... print('no') ... no re.findall()

match() 方法总是从字符串开始去匹配,如果查找字符串任意部分出现的位置,建议使用 findall() 方法代替。示例如下:

>>> text = 'Today is 11/27/2012. PyCon starts 3/13/2013.' >>> pattern = r'\d+/\d+/\d+' >>> re.findall(pattern, text) ['11/27/2012', '3/13/2013'] >>>

有时候定义正则表达式的时候,会利用括号进行捕获分组。示例如下:

>>> pattern = re.compile(r'(\d+)/(\d+)/(\d+)') >>>

捕获分组的用处主要是用于将每个组的内容提取出来,也对后续的处理进行备用。示例如下:

>>> import re >>> pattern = re.compile(r'(\d+)/(\d+)/(\d+)') >>> match_data = re.match(pattern,'11/27/2012') >>> match_data >>> match_data.group(0) '11/27/2012' >>> match_data.group(1) '11' >>> match_data.group(2) '27' >>> match_data.group(3) '2012' >>> match_data.groups() ('11', '27', '2012') >>> month, day, year = match_data.groups() >>> text = 'Today is 11/27/2012. PyCon starts 3/13/2013.' >>> # 使用 findall(),注意返回的结果是列表里面包裹元组 ... re.findall(pattern, text) [('11', '27', '2012'), ('3', '13', '2013')] >>> for month, day, year in re.findall(pattern, text): ... print('{}-{}-{}'.format(year, month, day)) ... 2012-11-27 2013-3-13 re.finditer()

findall() 方法会以搜索文本并以列表形式返回所有的匹配。如果想以迭代的方式返回匹配,可以考虑使用 re.finditer() 方法代替。

re.finditer() 方法返回一个迭代器,方法是从左到右扫描字符串,按照找到匹配项的顺序返回,示例如下:

>>> for match_data in re.finditer(pattern, text): ... print(match_data.groups()) ... ('11', '27', '2012') ('3', '13', '2013')

综合上述,re 模块进行匹配和搜索文本,核心的步骤是先使用 re.compile() 将正则表达式字符串进行编译,然后根据需求使用 match(),findall() 或者 finditer() 方法。

一些建议: 编写正则表达式字符串的时候,通常会是 r'' 这种形式,这种使用原始字符串的做法,能够不用去解析反斜杠。如果不这样做的话,需要使用两个反斜杠,例如:(\\d+/\\d+/\\d+)。

注意事项: match() 方法仅仅检查字符串的开始部分, 匹配的结果可能并非期望的结果。示例如下:

>>> pattern = re.compile(r'(\d+)/(\d+)/(\d+)') >>> match_data = re.match(pattern, '11/27/2012abcdef') >>> match_data >>> match_data.group() '11/27/2012'

如果需要精确匹配,得在正则表达式以 $ 结尾,如下示例:

>>> pattern = re.compile(r'(\d+)/(\d+)/(\d+)$') >>> re.match(pattern, '11/27/2012abcdef') >>> re.match(pattern, '11/27/2012') >>>

上面的代码并未匹配 11/27/2012abcdef,因为编译的正则表达式以 $ 结尾,说明必须精确匹配才会返回结果。

如果仅仅是做一次简单的文本匹配/搜索操作的话,可以略过编译部分,但是,需要进行大量的匹配和搜索操作的话,最好先对正则表达式字符串进行编译,然后再重复使用。这样做的原因是不会消耗太多的性能 。

字符串搜索和替换

对于字符串的搜索时,有时候会对搜索的匹配项进行替换操作。

str.replace()

如果是简单的字面模式,可以直接用 str.replace() 方法,示例如下:

>>> text = 'yeah, but no, but yeah, but no, but yeah' >>> text.replace('yeah', 'yep') 'yep, but no, but yep, but no, but yep' >>> re.sub()

对于复杂的模式,请使用 re 模块中的 sub() 函数。现在的需求是这样,将形式为 11/27/2012 的日期字符串改为 2012-11-27,下面的是实现的代码:

>>> text = 'Today is 11/27/2012. PyCon starts 3/13/2013.' >>> import re >>> re.sub(r'(\d+)/(\d+)/(\d+)', r'\3-\1-\2', text) 'Today is 2012-11-27. PyCon starts 2013-3-13.'

sub() 函数中,第一个参数表示的是被匹配的模式,第二个参数表示的是替换模式。反斜杠数字,比如 \3 指向的是前面模式捕获分组的组号。

如果打算用相同的模式做多次替换,同样可以考虑先编译再重复使用。

re.subn()

如果除了替换后的结果外,还需要知道进行了多少次替换,可以使用 re.subn() 来代替。比如:

>>> text = 'Today is 11/27/2012. PyCon starts 3/13/2013.' >>> pattern = re.compile(r'(\d+)/(\d+)/(\d+)') >>> newtext, n = re.subn(pattern, r'\3-\1-\2', text) >>> newtext 'Today is 2012-11-27. PyCon starts 2013-3-13.' >>> n 2 >>>

以上就是本篇的主要内容。

结语: 新年快乐!

可参考【正则表达式 笔记整理】 ↩︎



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有