2.12 审查清理文本字符串

问题

​ 一些无聊的幼稚黑客在你的网站页面的表单中输入文本你'pÃthöñ',然后你想将这些字符清理掉。

解决方案

​ 文本清理问题会涉及到包含文本解析与数据处理等一系列问题。在非常简单的情形下,你可能会选择使用字符串函数(比如str.upper()和str.lower())将文本转为标准格式。使用str.replace()或者re.sub()的简单替换操作能删除或者改变指定的字符序列。你同样还可以使用unicodedata.normalize()函数将unicode文本标准化.

​ 然后,有时候你可能还在清理操作上更进一步。比如,你可能想消除整个区间上的字符或者去除变音符。为了这样做,你可以使用translate()方法。为了演示,假设你现在有下面这个凌乱的字符串:

s='pÃthöñ\fis\tawesome\r\n'
print(s)  # ->pÃthöñis	awesome

​ 第一步是清理空白字符。will这样做,先创建一个小的转换表格然后使用translate()方法:

remap={
    ord('\t'):' ',
    ord('\f'):' ',
    ord('\r'): ' '
}
a=s.translate(remap)
print(a)  # ->pÃthöñ is awesome

​ 正如你看的那样,空白字符\t和\f已被重新映射到一个空格。回车字符r直接被删除。

​ 你可以以这个表格为基础进一步构建更大的表格,比如,让我们删除所有的和音符:

import unicodedata
import sys
cmb_chrs=dict.fromkeys(c for c in range(sys.maxunicode) if unicodedata.combining(chr(c)))
b=unicodedata.normalize('NFD',a)
print(b)  # ->pÃthöñ is awesome 
print(b.translate(cmb_chrs))  # ->pAthon is awesome 

​ 上面的例子中,通过使用dict.formkeys()方法构造一个字典,每个Unicdoe和音符作为键,对应的值全部为None。

​ 然后使用unicodedata.normal()将原始输入标准化为分解形式字符。然后再调用translate函数删除所有重音符。同样的技术可以被用来删除其他类型的字符(比如空字符等)
​ 作为另外一个例子,这里的构造一个将所有unicode数字字符反映到对应的ASCII字符上的表格:

digitmap={
    c:ord('0')+ unicodedata.digit(chr(c))  for c in range(sys.maxunicode) if unicodedata.category(chr(c))=='Nd'
}

print(len(digitmap))  # ->630
x='\u0661\u0662\u0663'
print(x.translate(digitmap))  # ->123

​ 另一种清理文本的技术涉及到I/O解码与编码函数,这里的思路是先对文本做一些初步的清理,然后再结合encode()或者decode()从操作来清除或者修改。比如:

b=unicodedata.normalize('NFD',a)

print(b.encode('ascii','ignore').decode('ascii'))  #->pAthon is awesome

​ 这里的标准化操作将原来的文本分解为单独的和音符。接下来的ASCII编码/解码只是简单的一下子丢弃掉哪些字符。当然,这种方法仅仅只在最后的目标就是获取到文本的对应的ASCII表示的时候生效

讨论

​ 文本字符清理一个最主要的问题应该是运行的性能。一般来讲,代码越简单运行的越快。对于简单的替换操作,str.replace()方法通常是运行的最快的,甚至在你需要多次调用的时候,比如,为了清理空白字符,你可以这样做:

def clean_space(s):
    s=s.replace('\r','')
    s=s.replace('\t','')
    s=s.replace('\f','')
    return s

​ 如果不去测试,会发现这种方法会比使用translate或者正则表达式要快很多。

另一方面,如果你需要执行任何复杂字符对字符的重新映射或者删除操作,translate()方法会非常的快。

posted @ 2022-02-28 15:04  qiupeng  阅读(58)  评论(0)    收藏  举报