Python进阶编程:编写更高效、优雅的Python代码
上QQ阅读APP看书,第一时间看更新

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()方法也可以,执行速度也很快。

对于应用程序来说,性能是不得不去研究的。而且对于性能,我们不能给出特定的方法使它能够适应所有的情况。因此,在实际应用中我们需要尝试不同的方法并做对应的评估后,再选择具体的方案。