使用Python进行文本处理
在Python2中,类型 str 和 unicode 分别用于表示单字节字符串和Unicode字符串;在Python3中,所有字符串都使用Unicode表示,类型为str。
字符串可以使用单引号、双引号、三引号包围, 三引号中的字符串原样保留,可以方便的编写多行文本。
字符串的引号开始前,可以增加 u 、 r 修饰符,分别表示目标字符串是Unicode类型、不启用字符转义。
Python没有单独的字符类型,字符只是长度为1的字符串
在Python语言中,字符串属于(不可变)序列,支持有限的切片操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
s = "一二三四五六七" # 取子串,起始索引包含,结束索引不包含 assert s[1:3] == '二三' assert s[-3:] == '五六七' i = 3 assert s[:i] + s[i:] == s # 带步进的切片,默认1,即下一个元素的索引比上一个大1 assert s[1:4:1] == '二三四' assert s[1:4:2] == '二四' assert s[1:4:3] == '二' # 负数的步进,下面这个用于生成倒序的串 assert s[::-1] == '七六五四三二一' assert s[4:1:-1] == '五四三' |
在Python中,此操作符可以用于成员关系测试,或者迭代:
1 2 3 |
s = '01234567' assert '3' in s for c in s: print(c) |
适用于任意切片的运算符还包括 + 和 * ,分别可以用于连接、重复字符串:
1 2 |
assert '123' + '4567' == '1234567' assert '123' * 3 == '123123123' |
很多内置函数、方法可以用于操控字符串:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
s = "01234567" # 计算序列长度 assert len(s) == 8 # 获取序列元素的最小值 assert min(s) == '0' # 获取序列元素的最大值 assert max(s) == '7' # 查找,获得子串的索引,两个方法类似,但是index()在无法子串不存在时抛出ValueError assert s.index('67') == 6 assert s.find('67') == 6 # 查找,子串判断 assert s.startswith('012') and s.endswith('4567') # 替换,注意字符串是不可变的,因此返回的是副本 assert s.replace('3', 'x') == "012x4567" # 分割 assert s.split('3') == ['012', '4567'] # 连接 assert ','.join(['1', '2', '3']) == '1,2,3' # 对齐和填充 assert s.ljust(10, '0') == '0123456700' # 去除空白,左侧 assert s.lstrip('0') == '1234567' # 特性检查:is*()方法 assert s.isalnum() # 是否仅包含字母数字 |
Python支持两种风格的字符串格式化操作,包括旧式的 % 操作符,以及新式的 format() 方法。
可以使用 % 操作符来进行字符串格式化, s % d 左侧为格式模板,右边为占位符元组或字典,这与C语言的sprintf()函数类似。一个简单的例子如下:
1 2 |
'%s %s' % ('one', 'two') '%(k1)s %(k2)s' % {'k1':'one','k2':'two'} |
格式模板由普通字符、转换说明符组成,转换说明符以%开头,具体含义如下:
格式说明符 | 输出格式 |
d, i | 十进制整数、长整数 |
u | 无符号整数、长整数 |
o | 八进制整数、长整数 |
x | 十六进制整数、长整数 |
X | 十六进制整数(大写字母) |
f | 浮点数,如[-]m.dddddd |
e | 浮点数,如[-]m.dddddde+xx |
E | 浮点数,如[-]m.ddddddE+xx |
g, G | 指数小于-4或更高精度时使用%e或%E,否则格式化为浮点数 保留N位有效数字,N为下表的修饰符字段,采取四舍五入方式 |
s | 字符串或任意对象。格式化代码使用str()生成字符串 |
r | 同repr ()生成的字符串 |
c | 单个字符 |
% | 字面值% |
在字符%与上表列出的转换字符之间,可以按顺序出现以下修饰符:
修饰符 | 说明 |
(KEY) | 表示从字典中取值的键 |
- | 左对齐标志位,默认右对齐 |
+ | 保留数字的符号(即使正数) |
0 | 表示一个零填充 |
M | 表示输出的最小宽度 |
. | 小数点,用于按精度分割字段宽度 |
N | 要打印字符串的最大字符数、浮点数小数点后的位数、整数的最小位数 |
* | 从元组中读取字段的宽度数字 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
a = 42 b = 13.142783 c = "hello" d = {'x':13, 'y':l.54321, 'z':'world'} e = 5628398123741234 r = "a is %d" % a # r = "a is 42" r = "%10d %f" %(a,b) # r = " 42 13.142783" 整数宽度10,右对齐 r = "%+010d %E" %(a,b) # r = "+000000042 1.314278E+01" 整数宽度10,右对齐,不足补0 r = "%(x)-10d %(y)0.3g" % d # r = "13 1.54" 从字典d里取出值并对其执行转换 r = "%0.4s %s" %(c, d['z']) # r = "hell world" 第一个字符串最大打印长度为4 r = "%*.*f" % (5,3,b) # r = "13.143" 元组的前两项作为格式字符串的组成部分 #注意,格式化字符串与字典一起使用,行为类似于字符串插值: p = {'age' : 28, 'name' : 'Alex'} r = "%(name)s's age is %(age)d" % p # r = "Alex's age is 28" |
通过调用作为模板的字符串的format()是Python新式的格式化机制。模板由普通字符和占位符组成,可以使用 {place:format_spec} 的形式来声明一个占位符,它包括两个部分:
- place,位置,可以是数字表示的位置参数、标识符表示的命名参数。默认情况下Python使用 __format__() 得到对象的文本表示,要改变此行为,可以扩展place为 place!type 形式。其中type可以取值s、r、a,分别调用 __str__() 、 __repr__() 和 ascii() 完成文本转换,a仅在Python3中被支持
- format_spec,格式说明符,形式为: [[fill]align][sign][#][0][width][,][.precision][type] ,各字段说明如下:
fill | 当指定align时,可以指定该字段,用于补白,默认空格 |
align |
对齐方式: < 表示左对齐, > 表示右对齐, = 表示在数字左侧补白, ^ 表示居中对齐 |
sign | 符号,可以取值 + 、 - 和空格 |
# | 仅用于二、八、十六进制,如果出现此符号,输出分别添加前缀 0b 、 0o 、 0x |
, | 千位分隔符 |
width | 指定最小宽度 |
precision | 使用f或者F格式化时,小数的位数 使用g或者G格式化时,数字的位数 对于非数字,表示最大字符数 不可用于整数 |
type | 格式化为什么类型,参考上文“格式说明符” |
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# ### 参数占位符 ### # 位置参数 '{0}, {1}, {2}'.format('a', 'b', 'c') # 'a, b, c' '{2}, {1}, {0}'.format('a', 'b', 'c') # 'c, b, a' '{}, {}, {}'.format('a', 'b', 'c') # 'a, b, c' 要求2.7+ 'Hello {0}'.format('Alex') == 'Hello Alex' # 命名参数 'Coordinates: {latitude}, {longitude}'.format(latitude = '37.24N', longitude = '-115.81W') 'Hello {name}'.format(name = 'Alex') == 'Hello Alex' # 访问对象属性 person = lambda: None person.greeting = 'Hello' person.name = 'Alex' assert '{0.greeting} {0.name}'.format(person) == 'Hello Alex' # 访问序列的元素 coord = (3, 5) 'X: {0[0]}; Y: {0[1]}'.format(coord) # ### 格式控制 ### # 对齐和填充 '{:>30}'.format('right aligned') # ' right aligned' '{:*^30}'.format('centered') # '***********centered***********' # 浮点数 '{:+f}; {:+f}'.format(3.14, -6.02) # '+3.140000; -6.020000' # 千分位 '{:,}'.format(1234567890) # '1,234,567,890' # 格式化时间 import datetime d = datetime.datetime(2010, 7, 4, 12, 15, 58) '{:%Y-%m-%d %H:%M:%S}'.format(d) # '2010-07-04 12:15:58' |
re模块可以用于正则表达式的处理。
字符序列 | 说明 |
text | 匹配文字字符串text |
. | 匹配任何字符串,但换行符除外 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结束 |
* | 匹配前面表达式的0个或多个副本,匹配尽可能多的副本 |
+ | 匹配前面表达式的1个或多个副本,匹配尽可能多的副本 |
? | 匹配前面表达式的0个或多个副本,匹配尽可能多的副本 |
*? | 匹配前面表达式的0个或多个副本,匹配尽可能少的副本 |
+? | 匹配前面表达式的1个或多个副本,匹配尽可能少的副本 |
?? | 匹配前面表达式的0个或多个副本,匹配尽可能少的副本 |
{m} | 准确匹配前面表达式的m个副本 |
{m, n} | 匹配前面表达式的第m到n个副本,尽可能匹配多的副本 |
{m, n}? | 匹配前面表达式的第m到n个副本,尽可能匹配少的副本 |
[...] | 匹配一组字符,如 r'[abcde]' 或者 r'a-zA-Z' |
[^...] | 匹配集合中未包含的字符,如 r'[^0-9]' |
A|B | 匹配A或者B,A和B都是正则式 |
(...) | 匹配圆括号中的正则表达式(圆括号中的内容为一个分组)并保存匹配的子字符串 在匹配时,分组中的内容可以使用所获得的 MatchObject 对象的 group() 方法获取 |
(?aiLmsux) | 把字符解释为对应的标记位,例如i表示无视大小写 |
(?:...) | 匹配圆括号中的正则表达式,但丢弃匹配的子字符串 |
(?P<name>...) | 匹配圆括号中的正则表达式并创建一个命名(named)分组。分组名称必须是有效的Python标识符 |
(?P=name) | 引用命名分组:匹配一个前面指定的分组所匹配的文本 |
(?#...) | 一个注释。圆括号中的内容将被忽略 |
(?=...) | 只有在括号中的模式匹配时,才匹配前面的表达式。例如 'Hello(?=World)' 只有在 World 匹配时才匹配 Hello |
(?!...) | 只有在括号中的模式不匹配时,才匹配前面的表达式 |
(?<=...) | 如果括号后面的表达式前面的值与括号中的模式匹配,则匹配该表达式。例如,只有当 'def' 前面是 'abc' 时, r'(?<=abc)def' 才会与它匹配 |
(?<!...) | 如果括号后面的表达式前面的值与括号中的模式不匹配,则匹配该表达式 |
\number | 引用序号分组:匹配与前面的组编号匹配的文本。组编号范围为1到99,从左侧开始为组编号 |
\A | 仅匹配字符串的开始标志 |
\b | 匹配单词开始或结尾处的空字符串。单词是一个字母数字混合的字符序列,以空格或任何其他非字母数字字符结束 |
\B | 匹配不在单词开始或结尾处的空字符串 |
\d | 匹配任何十进制数。等同于 r'[0-9]' |
\D | 匹配任何非数字字符。等同于 r'[^0-9]' |
\s | 匹配任何空格字符。等同于 r'[\t\n\r\f\v] |
\S | 匹配任何非空格字符,等同于 r'[^\t\n\r\f\v] |
\w | 匹配任何字母数字字符 |
\W | 匹配\w定义的集合中不包含的字符 |
\z | 仅匹配字符串的结束标志 |
\\ | 匹配反斜杠本身 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
import re #获取字符串中与模式匹配的所有不重叠值,包括空匹配 #如果模式包含分组,将返回与分组匹配的文本列表,如果包含多个分组,则前述列表的每一项都是一个元组 #finditer与之类似,但是返回的是MatchObject的迭代器 #(\D+)(\d+) 结果:[('a', '0'), ('b', '12'), ('c', '345'), ('d', '6789')] #(\D+)\d+ 结果:['a', 'b', 'c', 'd'] #\D+\d+ 结果:['a0', 'b12', 'c345', 'd6789'] m = re.findall(r'\D+\d+', 'a0b12c345d6789', re.IGNORECASE) # 由于正则式特殊字符很多,故一般使用“原始字符串” print m r = re.compile(r'\D+\d+', re.IGNORECASE) #编译得到一个正则式对象 #search()、match()返回MatcherObject对象 s = 'Iggle Piggle and Makka Pakka are in the night garden' # match、search等方法返回MatchObject: # group(n):返回匹配中第n个分组,如果n==0或者为空,返回整个匹配 # match函数从字符串起始处搜索正则式的匹配,找不到返回None # 贪婪匹配 print re.match( r'.*ggle', s , re.IGNORECASE ).group() # Iggle Piggle # 非贪婪匹配 m = re.match( r'.*?(ggle)', s , re.IGNORECASE ) print m.group() # Iggle print m.group( 1 ) # ggle 返回匹配中的第一个分组 # search函数与match类似,但是不限定匹配从字符串首部开始 print re.search( r'.akka', s , re.IGNORECASE ).group() # Makka # sub(pattern, repl, string, count, flags) 函数用于字符串替换 # pattern 匹配模式 repl替换文本或回调 string被处理字符串 count需要处理的匹配个数 # 这里使用基于序号的分组,()界定了分组范围,分组可以嵌套: # (M(\w+))是第一个分组,(\w+)是第二个分组,(P\2)是第三个分组 # repl可以指定转移字符,\g<name>用于引用匹配的正则式分组 print re.sub( r'(M(\w+))\s+(P\2)', '\g<1> \g<3>', s ) # 去掉Makka Pakka之间多于的空格 # 命名分组,使用(?P<GRP_NAME>)定义命名分组,使用(?P=GRP_NAME)引用命名分组 print re.sub( r'(M(?P<grp1>\w+))\s+(P(?P=grp1))', 'M\g<grp1> \g<3>', s ) # repl还可以是函数 print re.sub( r'(M(?P<grp1>\w+))\s+(P(?P=grp1))', lambda match : match.group().upper(), s ) |
原因:进行字符串格式化时,如果%后面的参数时元组,那么必须对所有元组成员进行格式化,也就是说,前面的占位符数量要和元组长度一致。
Leave a Reply