note-PythonCookbook-第二章 字符串和文本

第二章 字符串和文本

2.1 使用多个界定符分割字符串

re.split() 方法很适合同时用多个定界符或定界符周围有空格的情形。

import re
line = 'abc def, ghi/ jkl-mno'
re.split('[ ,/-]\s*', line)
# ['abc', 'def', 'ghi', 'jkl', 'mno']

当正则表达式中包含括号时,其捕获的子组也会出现在结果中。需要原字符串的拼接格式时可以这么用,使用 (?😃 来避免子组出现在结果中。

2.2 字符串开头或结尾匹配

字符串的 startswith() 和 endswith() 方法。
想匹配多种可能,需要把所有字符串放到元组中传给方法。

[name for name in namelist if name.endswith(('.xls', '.xlsx'))]

if any(name.endswith(('.c', '.py')) for name in os.dirlist(path)): 
    pass

2.3 用 shell 通配符匹配字符串

fnmatch 模块的 fnmatch() 和 fnmatchcase() 方法。介于简单匹配和正则表达式之间。

from fnmatch import fnmatch, fnmatchcase
fnmatch('foo.txt', '*.txt')
# True

fnmatch() 用底层操作系统对应的大小写敏感规则;fnmatchcase() 完全使用给定的模式进行匹配。

2.4 字符串匹配和搜索

字面匹配:find(),startswith(),endswith() 等方法。
复杂的匹配:正则表达式和 re 模块。
​ 需要用同一个模式做大量匹配时,可用 re.compile 把模式预编译为模式对象;
​ 括号捕获子组,结果用 group() 方法全部或分别显示;
​ findall() 返回列表形式;finditer() 返回迭代器形式。

2.5 字符串搜索和替换

简单的替换,用 replace() 方法;复杂的替换,用 re.sub() 方法。

import re
text = 'Today is 11/27/2019.'
re.sub('(\d+)/(\d+)/(\d+)', r'\3-\1-\2', text)
# 'Today is 2019-11-27.'

sub() 方法第一个参数是匹配模式,第二个参数是替换模式。\3 是指前面的第三个子组。
需要多次替换的话,就编译这个模式。

datepat = re.compile('(\d+)/(\d+)/(\d+)')
datepat.sub(r'\3-\1-\2', text)

对于更复杂的替换,可以传递一个替换回调函数来实现。替换回调函数的参数是匹配得到的 Match 对象,必须返回一个用于替换的字符串。

from calendar import month_abbr
def change_month(m):
	mon_name = month_abbr[int(m.group(1))]
	return f'{m.group(2)} {mon_name} {m.group(3)}'
	
datepat.sub(change_month, text)
# 'Today is 27 Nov 2019.'

如果想知道替换了多少次,可以用 re.subn() 方法:

newtext, n = datepat.subn(r'\3-\1-\2', text)
# 'Today is 2019-11-27.', 1

2.6 字符串忽略大小写的搜索替换

使用 re 搜索和替换时提供 re.IGNORECASE 给参数 flags 即可忽略大小写。但是在替换时想要让替换后的大小写模式和原来的一致,就需要一个辅助函数。

import re
text = 'abc ABC Abc'
re.sub('abc', 'xyz', text, flags=re.I)
# 'xyz xyz xyz'

def matchcase(word):
	def replace(m):
		t = m.group()
		if t.isupper():
			return word.upper()
		elif t.islower():
			return word.lower()
		elif t.capitalize():
			return word.capitalize()
		else:
			return word
	return replace

re.sub('abc', matchcase('xyz'), text, flags=re.I)
# 'xyz XYZ Xyz'

2.7 最短匹配模式

在正则表达式的 + 或 * 后面加 ? 实现。

2.8 多行匹配模式

需要匹配跨越多行的信息,但是 . 符号不能匹配换行符,可以指定一个非捕获子组:

import re
text = '#123\nabc\n123#'
re.findall('#((?:.|\n)*?)#', text)
# ['123\nabc\n123']

如果只是简单的匹配,可以使用 re.DOTALL 标志参数:

re.findall('#(.*?)#', text, flags=re.DOTALL)
# ['123\nabc\n123']

2.9 将 Unicode 文本标准化

Unicode 中的某些字符有多种合法的编码表示,在需要比较字符串时不同的编码表示会出现问题。
可以用 unicodedata 模块使字符串标准化。

import unicodedata
unicodedata.normalize('NFC', text)
unicodedata.normalize('NFD', text)

2.10 在正则式中使用 Unicode

想在模式中包含指定的 Unicode 字符,可以使用这些字符对应的转移序列。
执行匹配和搜索操作时,最好先标准化。
有些特殊情况最好使用第三方的正则式库。

2.11 删除字符串中不需要的字符

strip() 方法删除开头和结尾的指定字符;lstrip() 和 rstrip() 分别删除开头和结尾的字符。默认去除空格,也能指定其他字符。
对于字符串中间的字符处理,需要使用 replace() 或者正则实现。

2.12 审查清理文本字符串

想要将不需要的字符剔除掉。
translate() 方法,传入一个映射,替换字符串中的字符。
目标是获取文本对应的 ASCII 表示的时候,可以先标准化,再使用 encode() 和 decode() 清除 ASCII 以外的字符。

对于简单字符的替换,replace() 是最快的;对于复杂字符的替换和删除操作,translate() 更快。

2.13 字符串对齐

基本的对齐操作,可以使用 ljust(),rjust(),center()。指定宽度和填充字符(默认空格)。
format() 函数用来对齐字符串,format(str, ‘[fillchar]>width’),<、>、^ 分别表示左对齐、右对齐和居中对齐。
格式化同一个字符串的多个值时,这些格式化符号也能用在 format 的花括号中。
format() 对其它值也很通用。

2.14 合并拼接字符串

如果想合并的字符串在一个列表或迭代器中,使用 join() 方法最快。
简单地合并少数几个字符串,使用 ‘+’ 就够了。
字面字符串,直接放到一起就能合并。

使用 ‘+’ 时,会引起内存复制和垃圾回收操作,因此在合并大量字符串时不如 join() 高效。join() 还能使用生成器表达式,处理数据的同时完成连接操作。

2.15 字符串中插入变量

format() 方法可以实现该需求。如果格式化用的变量能在变量域中找到,可以结合使用 format_map() 和 vars() 。 vars() 可适用于对象实例,将对象的属性作为格式化的变量。

s = "{name} is {age}."
class Stu(object):
	def __init__(self, name, age):
		self.name = name
		self.age = age
stua = Stu('Mike', 24)
s.format_map(vars(stua))
# 'Mike is 24.'

format() 和 format_map() 不能很好地处理变量缺失的情况,可以另外定义一个含有 missing() 方法的字典对象来解决。

class safesub(dict):
	def __missing__(self, key):
		return '{' + key + '}'

del age
s.format_map(safesub(vars()))
# 'Mike is {age}.'

如果需要频繁执行这类操作,可以定义一个用于变量替换的工具函数:

import sys
def sub(text):
	return text.format_map(sagesub(sys._getframe(1).l_locals)

sub('Hello, {name}!')
# 'Hello, Mike!'
sub('He is {age}.')
# 'He is 24.'
sub('His hair is {color}.')
# 'His hair is {color}.'

2.16 以指定列宽格式化字符串

textwrap 模块用于对字符串的输出进行格式化。

testwrap.fill(text, width=70, **kwargs)

fill() 中的关键字参数可用于自定义输出的格式。
可以和 os.get_terminal_size() 结合来让输出自动匹配终端大小。

2.17 在字符串中处理 html 和 xml

想替换特定字符如 ‘<’ 或 ‘>’,html 模块的 escape() 可以实现。
需要把非 ASCII 字符对应的编码实体嵌入,可以在 encode 时传入参数 errors=‘xmlcharrefreplace’。
输出时选用合适的解析器把编码实体替换为对应字符。

2.18 字符串令牌解析

# 1.用命名捕获组的正则表达式来定义所有令牌
import re
NAME = r'(?P<NAME>[a-zA-Z_][a-zA-Z_0-9]*)'
NUM = r'(?P<NUM>\d+)'
PLUS = r'(?P<PLUS>\+)'
TIMES = r'(?P<TIMES>\*)'
EQ = r'(?P<EQ>=)'
WS = r'(?P<WS>\s+)'

master_pat = re.compile('|'.join([NAME, NUM, PLUS, TIMES, EQ, WS]))

# 2.使用scanner()方法创建scanner对象,调用match()方法,一步步扫描目标文本,每次匹配一次,可以写成生成器
def generate_tokens(pat, text):
    Token = namedtuple('Token', ['type', 'value'])
    scanner = pat.scanner(text)
    for m in iter(scanner.match, None):
        yield Token(m.lastgroup, m.group())

for tok in generate_tokens(master_pat, 'foo = 42'):
	print(tok)

第一步组装 master_pat 的时候,各个令牌的顺序是有影响的,如果一个模式是另一个更长模式的子字符串,长的模式必须写在前面。

2.19 实现一个简单的递归下降分析

2.20 字节字符串上的字符串操作

字节字符串也支持大部分文本字符串的内置操作。
正则表达式也可以用来匹配字节字符串,但是正则表达式本身也得是字节串。
字节串的索引返回的是整数而不是字符。
字节串不支持格式化操作,只能先用字符串格式化,再编码为字节串。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值