字符串和文本
一、对任意多的分隔符拆分字符串
字符串对象的split()方法只能处理非常简单的情况,而且不支持多个分隔符,对分隔符周围可能存在的空格也无能为力。
应该使用 re.split(),需要小心正则表达式模式中的捕获组(capture group)是否包含在了括号中。
如果用到了捕获组,那么匹配的文本也会包含在最终结果中。
>>> line = 'asdf fkdk; afed, fdsf,asdf, foo' >>> re.split(r'[;,\s]\s*',line) ['asdf', 'fkdk', 'afed', 'fdsf', 'asdf', 'foo'] >>> re.split(r'(;|,|\s)\s*',line) ['asdf', ' ', 'fkdk', ';', 'afed', ',', 'fdsf', ',', 'asdf', ',', 'foo']
如果不想再结果中看到分割字符,但仍想用括号来对正则表达式模式进行分组,确保用的是非捕获组,以(?: )的形式指定。
>>> re.split(r'(?:;|,|\s)\s*',line) ['asdf', 'fkdk', 'afed', 'fdsf', 'asdf', 'foo']
二、在字符串的开头或结尾处做文本匹配
使用str.startswith()和str.endswith()方法。
如果需要同时对多个选项做检查,只需给startswith()和endswitch()提供包含可能选项的元组 即可!
>>> [name for name in filenames if name.endswith(('.c','.h')) ]
使用re正则表达式作为替代方案:
>>> re.match(r'http:|https:|ftp:|', url)
检查目录有无出现特定文件:
>>> if any(name.endswith(('.c','.h')) for name in listdir(dirname)):
三、利用Shell通配符做字符串匹配
fnmatch模块提供两个函数,fnmatch()和fnmatchcase(),可用来执行这样的匹配。使用起来更加简单:
>>> from fnmatch import fnmatch,fnmatchcase >>> fnmatch('foo.txt','*.txt') >>> fnmatch('foo.txt','?oo.txt') True >>> fnmatch('Dat45.csv','Dat[0-9]*') True >>> fnmatchcase('foo.txt','*.TXT') False
fnmatch()的匹配模式所采用的大小写区分规则和底层文件系统相同(根据操作系统的不同而有所不同)。
使用fnmatchcase()。完全根据提供的大小写方式来匹配
四、文本模式的匹配和查找
如果要匹配的是简单文字,只需要使用基本的字符串方法就可以了,如:str.find()、str.endswith()、str.startswith()或类似的函数。
复杂匹配使用正则表达式以及re模块。
re.match()方法总是尝试在字符串的开头找到匹配项。
如果想针对整个文本搜索出所有的匹配项,那么就应该使用findall()方法。
当定义正则表达式时,用括号抱起来的方式引入捕获组,能简化对匹配文本的处理。
每个组的内容都可以单独提取出来。
finditer()方法,以迭代的方式找出匹配项。
五、查找和替换
简单文本使用:str.replace()即可。复杂模式使用re.sub()函数。
例子:将9/10/2019格式改为2019-9-10。
>>> text = 'Today i 9/10/2019 Pycon start 3/13/2013' >>> import re >>> re.sub(r'(\d+)/(\d+)/(\d+)',r'\3-\1-\2-',text) 'Today i 2019-9-10- Pycon start 2013-3-13-'
对于更加复杂的文本,可以指定一个替换回调函数。
datepat = re.compile(r'(\d+)/(\d+)/(\d+)') from calendar import month_abbr def change_date(m): mon_name = month_abbr[int(m.group(0))] return '{} {} {}'.format(m.group(2), mon_name, m.group(1)) datepat.sub(change_date, text)
替换回调函数的输入参数是一个匹配对象,由match()或find()返回。
用.group()方法来提取匹配中特定的部分。这个函数应该返回替换后的文本。
除了得到替换后文本外,还想知道一共完成了多少次替换,可以使用re.subn()。
六、不区分大小写的方式对文本做查找和替换
使用re模块加上re.IGNORECASE标记。
使用支撑函数,修正待替换文本和匹配文本大小写不吻合情况。
def matchcase(word): def replace(m): text = m.group() if text.isupper(): return word.upper() elif text.islower(): return word.lower() elif text[0].isupper(): return word.capitalize() else: return word return replace re.sub('python',matchcase('snake'),text,flags=re.IGNORECASE)
七、实现最短匹配的正则表达式
*操作符在正则表达式中采用贪心策略,所以匹配过程是基于找出最长的可能匹配来进行的。
只要在*操作符后面加上 ?修饰符就可以了。
>>> text1 = 'Computer says "no." phone says "yes."' >>> re.findall(r'\"(.*)\"',text1) ['no." phone says "yes.'] >>> re.findall(r'\"(.*?)\"',text1) ['no.', 'yes.']
八、多行模式的正则表达式
.句点并不能匹配换行符。需要添加对换行符的支持
>>> comment = re.compile(r'/\*(.*?)\*/') >>> comment = re.compile(r'/\*((?:.|\n)*?)\*/')
(?:.|\n),指定了一个非捕获组(即,这个组只做匹配但不捕获结果,也不会分配组号)
也可以直接使用re.DOTALL模式,句点符号将匹配所有的字符。
九、将unicode文本统一表示为规范形式
>>> t1 = unicodedata.normalize('NFC', s1)
>>> t2 = unicodedata.normalize('NFD', s1)
十、正则表达式处理Unicode字符
能在多个不同的阿拉伯代码页中匹配所有的字符:
>>> arabic = re.compile('[\u0600-\u06ff\u0750-\u077f\u08a0-\u08ff]+ ')
十一、从字符串中去掉不需要的字符
在字符串的开始、结尾、中间去掉不需要的字符。
strip()可用来从字符串的开始、结尾处去掉字符;lstrip()和rstrip()可分别从左或者右侧开始执行去掉字符的操作。
默认情况下这些方法去除的是空格符,但也可以指定其他字符。
with open(filename) as f: lines = (line.strip() for line in f) for line in lines: ...
十二、文本过滤和清理
(1)建立一个转换表,然后使用translate()方法;
remap = { ord('\t') : ' ', ord('\f') : ' ', ord('\r') : None, } a = s.translate(remap)
(2)对文本能做初步的清理,然后结合encode()和decode()操作来修改或清理文本;
t1 = unicodedata.normalize('NFD', s1) t1.encode('ascii', 'ignore').decode('ascii')
十三、对齐文本字符串
(1)使用字符串的ljust()、rjust()、center()方法。所有这些方法都可以接受一个可选的填充字符。
>>> text.ljust(20,'=') 'Hello World========='
(2)format(),函数也可以用来完成对齐的任务,合理使用< > ^以及一个期望的宽度值。
>>> format(text, '>20')
>>> format(text, '*^20s')
>>> '{:>10s} {:>10s}'.format('Hello', 'World')
format好处在于并不特定于字符串,还可以对数字做格式化处理:
>>> format(1.2345, '>10')
>>> format(1.2345, '^10.2f')
十四、字符串连接及合并
合并使用join()、+操作符或者直接排列一起,中间不加操作符;
复杂场景用format。
+操作符做链接非常低效,在于内存拷贝和垃圾收集产生的影响。每个+=操作符都会创建一个新的字符串对象。
利用生成器技巧:
>>> data = ['ACME', 50, 902]
>>> ','.join(str(d) for d in data)
打印时连接操作:
>>> print(a + ':' + b + ':' + c)
>>> print(':'.join([a, b, c]))
>>> print(a, b, c, sep=':')
同I/O操作混合使用:
def combine(source, maxsize): parts = [] size = 0 for part in source: parts.append(part) size += len(part) if size < maxsize: yield ''.join(parts) parts = [] size = 0 yield ''.join(parts) for part in combine(sample(), 32876): f.write(part)
十五、字符串中的变量名做插值处理
创建一个字符串,其中嵌入的变量名称会以变量的字符串值形式替换掉。Python并不会直接支持在字符串中对变量做简单的值替换
(1)使用format()方法。
>>> s = '{name} has {n} message'
>>> s.format(name='David', n=33)
(2)format_map()和vars联合使用。vars还可以用于实例上。
>>> s.format_map(vars())
>>> s.format_map(vars( Info('David',37) ))
(3)format和format_map缺点,无法处理缺省值。单独定义__missing__()方法的字典类
class safesub(dict): def __missing__(self): return '{' + key +'}' s.format_map(safesub(vars())) 'David has {n} message'
十六、以固定列数重新格式化文本
使用textwrap 模块
>>> print(textwrap.fill(s, 40))
os.get_terminal_size().columns 来获取终端尺寸大小。
十七、在文本中处理HTML和XML实体
将&entity或&#code这样的HTML或XML实体替换为它们相对应的文本。
或者我们需要生成文本,但是要对特定的字符做转义处理。
>>> s = 'as dafsd "<tag>Hello world</tag>" asd'
(1)生成文本,使用html.escape(s, quote=False)
# as dafsd "<tag>Hello world</tag>" asd'
(2)替换实体 HTML:
>>> from html.parser import HTMLParser
>>> p = HTMLParser()
>>> p.unescape(s)
(3)替换实体 XML:
>>> from xml.sax.saxutils import unescape
>>> unescape(t)
二十、在字节串上执行文本操作
在字节串(Byte String)上执行常见的文本操作,拆分、搜索和替换。
特别的需要在正则模式上做处理需要指定字节串的形式。
>>> re.split(b'[:,]', data)
print输出区别:
>>> a = 'Hello World' >>> a[0] 'H' >>> a[1] 'e' >>> b = b'Hello World' >>> b[0] 72 >>> b[1] 101
字节串上没有格式化操作。