2.2.3 字符串清理
在实际工作场合中,我们可能遇到的不只是中文、英文,还会经常遇到一些不熟悉的语言文本,如阿拉伯语、法语等,或是被恶意编纂出来的文本,这就需要对字符串做清理。
文本清理问题涉及包括文本解析与数据处理等一系列问题。在非常简单的情形下,我们可以选择使用字符串函数(如str.upper()和str.lower())将文本转为标准格式,使用str.replace()或者re.sub()函数的替换操作删除或者改变指定的字符序列。
有时候,我们可能想在清理操作上更进一步,如想消除整个区间的字符或者变音符,这时可以使用str.translate()方法实现,相关代码(str_clean.py)示例如下:
test_str = 'pyt???\fis\tawesome\r\n' print(test_str)
首先做空白字符清理,创建一个小的转换表格,然后使用translate()方法,示例如下:
re_map = {ord('\t') : ' ', ord('\f'): ' ', ord('\r'): None} print(test_str.translate(re_map))
执行py文件,输出结果如下:
pýtĥöñ is awesome
由输出结果可知,空白字符\t和\f已经被重新映射为一个空格,回车字符r直接被删除。
以这个表格为基础进一步构建更大的表格,如删除所有的和音符,相关代码(str_clean.py)示例如下:
import unicodedata import sys cmb_chrs = dict.fromkeys(c for c in range(sys.maxunicode) if unicodedata.combining(chr(c))) b_val = unicodedata.normalize('NFD', test_str) print(b_val) print(b_val.translate(cmb_chrs))
执行py文件,输出结果如下:
Pýtĥöñ is awesome Python is awesome
上述示例通过dict.fromkeys()方法构造一个字典,每个Unicode和音符作为键,对应的值全部为None。
使用unicodedata.normalize()方法将原始输入标准化为分解形式字符,再调用translate()方法删除所有重音符。同样的技术也可以用来删除其他类型的字符(比如控制字符等)。
再看一个示例,构造一个将所有Unicode数字字符映射到对应的ASCII字符上的表格:
digit_map = {c: ord('0') + unicodedata.digit(chr(c)) for c in range(sys.maxunicode) if unicodedata.category(chr(c)) == 'Nd' } print(len(digit_map)) x = '\u0661\u0662\u0663' print(x.translate(digit_map))
另一种清理文本的技术涉及I/O解码与编码函数。这里的思路是先对文本做一些初步清理,再结合encode()或者decode()方法对其进行清除或修改,代码示例如下:
test_b = unicodedata.normalize('NFD', test_str) print(test_b.encode('ascii', 'ignore').decode('ascii'))
这里的标准化操作将原来的文本分解为单独的和音符。接下来的ASCII编码/解码只是简单丢弃那些不需要的字符。这种方法将在获取文本对应的ACSII编码表示的时候生效。
字符串清理最主要的问题是运行性能。一般情况下,代码越简单运行越快。对于简单的替换操作,str.replace()方法通常是最快的,甚至在需要多次调用的时候速度也很快。清理空白字符的代码示例如下:
def clean_spaces(s): s = s.replace('\r', '') s = s.replace('\t', ' ') s = s.replace('\f', ' ') return s
通过测试,我们会发现str.replace()方法比translate()方法或者正则表达式运行要快很多。如果需要执行任何复杂字符对字符的重新映射或者删除操作,translate()方法也可以,执行速度也很快。
对于应用程序来说,性能是不得不去研究的。而且对于性能,我们不能给出特定的方法使它能够适应所有的情况。因此,在实际应用中我们需要尝试不同的方法并做对应的评估后,再选择具体的方案。