《Python 编程》笔记(十七)
来源:互联网 发布:男生不追女生 知乎 编辑:程序博客网 时间:2024/05/23 20:34
引言
本节笔记记录的是在 Python 中处理文本的一些方法。本节重点是学习如何在 Python 中使用正则表达式。
Python 内置字符串方法
常用字符串方法
以下是一些常用的字符串方法,更多的方法使用 dir(str)
查看。如今在 Python 中,所有常用的字符串方法都可以用于 str
和 bytes
字符串。
str.find(substr)
:查找子字符串;str.replace(old, new)
:替换子字符串;str.split(delimiter)
:分割字符串;str.join(iterable)
:用分隔符连接字符串;str.strip()
:删除两端空白字符;str.lstrip()
和str.rstrip()
:分别删除左边和右边的空白字符;str.rjust(width)
:宽度固定的区域内将字符串向右对齐(使用format
也可以控制);str.upper()
:转为大写字母;str.isupper()
str.isdigit()
str.endswith(sub-or-tuple)
:测试是否以某个子串或者多个子串中的某个结尾;str.startswith(sub-or-tuple)
。
简单实践
简单的字符串分割和连接操作:
>>> str1 = 'spam foo bar temp Good'>>> str1.split(' ')['spam', 'foo', 'bar', 'temp', 'Good']>>> ', '.join(str1.split(' '))'spam, foo, bar, temp, Good'
计算文件中数据的和(完整的代码文件参见 ch19/str_tools):
首先使用
gen_dummy_data
函数生成需要计算的数据文件,得到的文件如下图所示:然后使用
cal_sum
函数计算文件中所有行数据的和以及所有列数据的和。这里使用的重要函数是str.split('\t')
分割得到列数据,再进行转换后即可计算。以下为核心代码:
with open(filename, 'r') as infile: # 按行快速读取,然后进行计算 for row in infile: # 数据使用 tab 分割 data = [int(x.strip()) for x in row.split('\t') if x.strip() != ''] # 计算行和 rows_sum.append(sum(data)) # 累积列和 if len(cols_sum): for i, x in enumerate(data): cols_sum[i] += x else: cols_sum = data
注意
- 尽量使用字符串对象方法来避免如正则表达式这样的方法,使用内置字符串方法性能会更好。
正则表达式
简要地说,正则表达式只是字符串,它们定义了在其它字符串中进行匹配的模式。给定一个字符串和匹配模式,如果匹配成功,将会得到相应的回答,并可以挑选出匹配的子串。
正则表达式可以写得非常复杂,它们可以代替手动编写大段的文本处理代码,解放双手!
Python 中,正则表达式是由
re
模块提供支持的。re
模块包含了很多函数,可以执行匹配等操作。re
同时支持bytes
和str
字符串,但二者不可混用。推荐书籍:Mastering Regular Expressions,掌握高级的正则表达式技巧。
两种方式开始匹配:
- 直接调用顶级方法开始执行匹配;
- 预先编译好模式对象,后期多次调用时效率会更高。
示例:
>>> email_a = 'hello@foo.com'# `.` 表示任意字符,`*` 表示重复0次或更多# `()` 表示要把匹配到的子串保留下来>>> pattern = '.*@(.*).com'>>> obj = re.match(pattern, email_a)# 获取到保留的子串>>> obj.groups()('foo',)# 下面的正则表达式解释为:# 忽略字符串前面的0个或多个空格或 tab,并且`Hello` 和 `hello` 都可以匹配,同时忽略`hello`后面的# 1个以上的空格或 tab,然后获取中间的字符 `(.*)`,同样地,`world` 中的 `w` 或 `W` 都可以匹配。>>> patt = '[ \t]*[Hh]ello[ \t]+(.*)[Ww]orld'>>> line = ' Hello fooworld'>>> obj = re.match(patt, line)>>> obj<_sre.SRE_Match object; span=(0, 18), match='\tHello fooworld'>>>> obj.groups()('foo',)# 应用在字节字符串上>>> patt = b'[ \t]*[Hh]ello[ \t]+(.*)[Ww]orld'>>> line = b' hello barWorld'>>> obj = re.match(patt, line)>>> obj<_sre.SRE_Match object; span=(0, 18), match=b' hello barWorld'>>>> obj.group(0)b' hello barWorld'>>> obj.group(1)b'bar'
文本分割:事实上,有些情况下,内置字符串方法无法完成一些分割或替换的操作,如
aaa---bbb===ccc
,如果想要分割后的结果为aaa, bbb, ccc
,使用str.split()
可能需要多次才能做到,但正则表达式则会更灵活!正则表示进行分割、替换和匹配等操作示例:
>>> import re# 简单的字符串分割>>> re.split('---', 'aaa---bbb---ccc')['aaa', 'bbb', 'ccc']# 简单的字符串替换>>> re.sub('---', '...', 'aaa---bbb---ccc')'aaa...bbb...ccc'# 多分隔符>>> text = 'foo---bar===spam'>>> re.split('---|===', text)['foo', 'bar', 'spam']>>> re.sub('---|===', '***', text)'foo***bar***spam'# 多种单个字符>>> text = 'foo-bar=spam'>>> re.split('[-=]', text)['foo', 'bar', 'spam']>>> re.sub('[=-]', '&', text)'foo&bar&spam'# 包括组合>>> re.split('(--)|(==)', 'foo--bar==spam')['foo', '--', None, 'bar', None, '==', 'spam']# 显示匹配的部分,但不显示组合>>> re.split('(?:--)|(?:==)', 'foo--bar==spam')['foo', 'bar', 'spam']# 匹配示例>>> re.match('(.*)/(.*)/(.*)', 'path/to/home').groups()('path', 'to', 'home')>>> re.match('<(.*)>/<(.*)>/<(.*)>', '<path>/<to>/<home>').groups()('path', 'to', 'home')# 忽略前面0到多个泛空格符号>>> re.match('\s*<(.*)>/<(.*)>/<(.*)>', ' <path>/<to>/<home>').groups()('path', 'to', 'home')# 一个复杂点的匹配>>> re.match('\s*(path)\s*([a-zA-Z]*)\s+(.*?)\s*!\s*', ' pathTo home ! ').groups()('path', 'To', 'home')
re.findall
方法可以在文本中找到所有匹配的子串。re.search
方法与re.findall
类似,不过它在首次匹配成功后停止。下面来看看一些使用的示例:# 找到所有的匹配组合并返回>>> re.findall('<(.*?)>', '<html><head></head><body></body></html>')['html', 'head', '/head', 'body', '/body', '/html']>>> re.findall('<(.*?)>', ' <html> <head></head>/ <body> </body></html> ')['html', 'head', '/head', 'body', '/body', '/html']>>> re.findall('<(.*?)>/?<(.*?)>', '<path>/<to>/<my><home>')[('path', 'to'), ('my', 'home')]# 返回第一个匹配的结果,然后停止匹配>>> re.search('<(.*?)>/?<(.*?)>', '<path>/<to>/<my><home>').groups()('path', 'to')# `.` 匹配除了行为符以外的任何字符,使用 `(?s)` 来匹配多行文本>>> re.findall('<(.*?)>.*<(.*?)>', '<path> \n <to>\n<home>')[]# 贪婪匹配>>> re.findall('(?s)<(.*?)>.*<(.*?)>', '<path> \n <to>\n<home>')[('path', 'home')]# 非贪婪匹配>>> re.findall('(?s)<(.*?)>.*?<(.*?)>', '<path> \n <to>\n<home>')[('path', 'to')]
为了能够方便记忆,可以使用
(?P<name>)
的模式将匹配的子串组合与特定的名称关联,并在匹配后的通过名称提取它们。>>> re.search('(?P<part1>\w*)/(?P<part2>\w*)', 'xxxx/abc/dddd}').groups()('xxxx', 'abc')>>> re.search('(?P<part1>\w*)/(?P<part2>\w*)', 'xxxx/abc/dddd}').groupdict(){'part2': 'abc', 'part1': 'xxxx'}# 根据键名提取,更加容易记忆>>> re.search('(?P<part1>\w*)/(?P<part2>\w*)', 'xxxx/abc/dddd}').group('part1')'xxxx'# 这样就可以使用字典的键来访问每个匹配项!参见下方的参考。书上的例子给的不好,该用下面这个说明用法。>>> for x in re.finditer('(?P<part1>\w*)/(?P<part2>\w*)', 'xyz/abc ddd/fff'): x.groupdict(){'part2': 'abc', 'part1': 'xyz'}{'part2': 'fff', 'part1': 'ddd'}
[^ ]
和\-
用法示例:# 在范围更广的界定符两侧做分割>>> re.split(' +', text)'foo', 'bar', 'bala']# 另外一种:提取非分隔符的字符串>>> re.findall('[^ ]+', text)['foo', 'bar', 'bala']# 更复杂点的例子>>> re.findall('[^ .\-^&\*]+', text)['xxx', 'yyy', 'b', 'jfld', 'e', 'e']
re 模块
- 该模块有多种函数可以使用。同样,也可以编译好一个模式对象,再调用相关函数执行匹配。
模块级别函数:
compile
:编译生成正则表达式对象;match
:若字符串起始处有 0 到多个匹配模式的字符串,则返回相应的匹配对象,否则返回 None;search
findall
finditer
split
sub
subn
:和sub
相同,它返回一个元组:(新字符串, 替换次数);escape
:返回一个所有非字母或数字字符都转义过的字符串,该字符串可以作为文字字符串来编译。
此外,编译好的模式对象也有很多类似的方法,具体参见官方文档。
匹配对象自身的属性:
group(g)
group(g1, g2, ...)
:返回与模式中单个或数个圆括号中的组合匹配的子串。接手组合名称。组合编号从 1 开始,组合 0 表示与模式匹配的整个字符串。传入多个组合编号时返回一个元组,如果省略则默认为 0;groups()
:返回一个由所有组合的匹配子串组成的元组;groupdict()
:返回一个字典,包含匹配对象中所有名称的组合;start([group])
,end([group])
:返回与 group 相匹配的子串的起始与结束位置索引;span([group])
:返回一个带有两个元素的元组:(start([group]), end([group])
;expand([template])
:进行反斜杠组合替换。
正则表达式模式
- 通常匹配得到最长的匹配字符串,除非使用了非贪婪匹配操作符。
正则表达式相关资源
- Regular Expression HOWTO;
- 维基百科:正则表达式;
- 正则表达式在线测试;
- 菜鸟教程:正则表达式教程。
参考
- Stack Overflow: re.findall which returns a dict of named capturing groups?
结语
本篇笔记也是整个《Python 编程》系列的最后一篇。实际上,由 Mark Luttz 编著的《Python 编程》(Programming Python) 中文版分为上下两册,涉及到的内容也是非常多的。我只是挑选了其中比较感兴趣的部分进行了学习,并做了这些笔记。总的来说,我觉得还是从高手那儿学到不少知识,虽然有些凌乱,不过相信在今后的实践中不断使用和总结定会有所提高的。
- 《Python 编程》笔记(十七)
- python 学习笔记(十七)
- linux下python学习笔记(十七)
- python 学习笔记(二十七)
- Android基础笔记(十七)- 多媒体编程
- Windows核心编程笔记(十七) 堆
- Python学习笔记(十七)----Python 元组
- Python学习笔记(十七):异常-----最后一篇
- pytorch学习笔记(十七):python 端扩展 pytorch
- 学习Python(十七)
- python练习(十七)
- Windows核心编程笔记(十七) DLL高级技术
- Windows核心编程笔记(十七) 线程局部储存
- 学习笔记(十七)
- Python编程基础之十七XML解析
- 孙鑫MFC笔记之十七--HOOK编程
- Python import机制 (十七)
- OpenGL入门笔记(十七)
- iOS JSPatch打补丁包
- 1 线程的基础知识(上)
- springSecurity安全框架配置详解
- NameError: name 'exception' is not defined
- 二叉排序树
- 《Python 编程》笔记(十七)
- HttpClient的post和get请求
- 字符串,字符串数组,字符串指针!!
- C# 基元类型 引用类型和值类型
- 数据结构与算法学习笔记——动态规划的入门与编程实现
- Excel自定义文档生成
- Go 根据字符串调用指定函数
- 10天精通Sass 之 Sass基本语法
- Hive安装及遇到的问题