高效替换坏字符

25

我经常使用包含以下字符的utf-8文本:

\xc2\x99

\xc2\x95

\xc2\x85

等等

这些字符会让我工作中的其他库混淆,所以需要进行替换。

有没有一种更有效的方法来做到这一点,而不是:

text.replace('\xc2\x99', ' ').replace('\xc2\x85, '...')

你想允许哪些字符?仅限ASCII码吗? - Tim Pietzcker
我仍然使用Unicode,但有一些特定的字符会导致库出现问题,需要进行替换。 - hoju
1
我相信你会想要使用text.translate(table),参考http://docs.python.org/library/stdtypes.html#str.translate - TryPyPy
@TryPyPy:把你的评论变成答案,这样我就可以点赞了。你可能还想提一下Python 3+中有str.maketrans() - JAB
1
str.translate() 只适用于单字节字符。 - hoju
6个回答

38

可以使用正则表达式解决问题;只需在方括号内列出所有有问题的字符即可,例如:

import re
print re.sub(r'[\xc2\x99]'," ","Hello\xc2There\x99")

这将打印出“Hello There ”,并将不需要的字符替换为空格。

或者,如果您有不同的替换字符:

# remove annoying characters
chars = {
    '\xc2\x82' : ',',        # High code comma
    '\xc2\x84' : ',,',       # High code double comma
    '\xc2\x85' : '...',      # Tripple dot
    '\xc2\x88' : '^',        # High carat
    '\xc2\x91' : '\x27',     # Forward single quote
    '\xc2\x92' : '\x27',     # Reverse single quote
    '\xc2\x93' : '\x22',     # Forward double quote
    '\xc2\x94' : '\x22',     # Reverse double quote
    '\xc2\x95' : ' ',
    '\xc2\x96' : '-',        # High hyphen
    '\xc2\x97' : '--',       # Double hyphen
    '\xc2\x99' : ' ',
    '\xc2\xa0' : ' ',
    '\xc2\xa6' : '|',        # Split vertical bar
    '\xc2\xab' : '<<',       # Double less than
    '\xc2\xbb' : '>>',       # Double greater than
    '\xc2\xbc' : '1/4',      # one quarter
    '\xc2\xbd' : '1/2',      # one half
    '\xc2\xbe' : '3/4',      # three quarters
    '\xca\xbf' : '\x27',     # c-single quote
    '\xcc\xa8' : '',         # modifier - under curve
    '\xcc\xb1' : ''          # modifier - under line
}
def replace_chars(match):
    char = match.group(0)
    return chars[char]
return re.sub('(' + '|'.join(chars.keys()) + ')', replace_chars, text)

这是一个不错的方法,但我们希望为每个字符设置不同的替换字符。 - hoju
你能举个例子说明你的意思吗?我很乐意解决更具体的情况。 - Nate
嗨,Nate - 这种替换方式的负面评价并不是应该在这种情况下采取的,尽管 OP 已经要求这样做。 (好吧,我有点生气,我会取消对你的负面评价)- Python 有复杂的机制来转换编码字符串,这些机制应该被使用。 - jsbueno
@jsbueno:编码不是问题。 - hoju
哦,完全没问题。还有,我说错了 - 我不是指你给我投了反对票,而是指史蒂文。 - Nate
显示剩余5条评论

24

我认为这里存在一个潜在的问题,调查和解决它可能是个好主意,而不只是试图掩盖症状。

\xc2\x95是字符U+0095(C1控制字符 MESSAGE WAITING)的UTF-8编码。你的库无法处理它并不奇怪,但问题在于,它是如何出现在你的数据中的呢?

很有可能最初它以Windows-1252编码中的0x95字符(BULLET)开始,被错误地解码为U+0095,而不是正确的U+2022,并重新编码为UTF-8。(日本术语mojibake描述了这种错误)。

如果这是正确的,则可以将它们放回到Windows-1252中,然后这次正确地解码成Unicode来恢复原始字符。(在这些示例中,我使用的是Python 3.3;在Python 2中,这些操作略有不同。)

>>> b'\x95'.decode('windows-1252')
'\u2022'
>>> import unicodedata
>>> unicodedata.name(_)
'BULLET'

如果您想对范围为0x80–0x99的所有有效Windows-1252字符进行此更正,可以使用以下方法:

def restore_windows_1252_characters(s):
    """Replace C1 control characters in the Unicode string s by the
    characters at the corresponding code points in Windows-1252,
    where possible.

    """
    import re
    def to_windows_1252(match):
        try:
            return bytes([ord(match.group(0))]).decode('windows-1252')
        except UnicodeDecodeError:
            # No character at the corresponding code point: remove it.
            return ''
    return re.sub(r'[\u0080-\u0099]', to_windows_1252, s)
例如:
>>> restore_windows_1252_characters('\x95\x99\x85')
'•™…'

有趣。我正在处理的数据是随机的HTML页面,所以这似乎很可能。 - hoju
3
啊!如果你正在处理随机的 HTML 页面,那么你需要执行“字符编码自动检测”。你是如何确定页面的编码方式的?问题在于,一个页面可能会声称它是以 ISO Latin-1 编码,但实际上却是 Windows-1252 编码。 - Gareth Rees

13

如果您想从字符串中删除所有非ASCII字符,可以使用

text.encode("ascii", "ignore")

3
请确保 text 是一个unicode字符串——即以 text=u"..." 的形式定义——如果不是,则会引发 UnicodeDecodeError 错误。 - Nate
还要确保您不想剥离到只有ASCII!(这是不言而喻的:p) - 2rs2ts

2
import unicodedata

# Convert to unicode
text_to_uncicode = unicode(text, "utf-8")           

# Convert back to ascii
text_fixed = unicodedata.normalize('NFKD',text_to_unicode).encode('ascii','ignore')         

你的回答如果能再多解释一些就更好了。 - Tom

0

这些字符不在 ASCII 库中,这就是你遇到错误的原因。 为了避免这些错误,在读取文件时可以采取以下措施。

import codecs   
f = codecs.open('file.txt', 'r',encoding='utf-8')

想了解更多关于这种错误的信息,请查看此链接


0

这不是“Unicode字符” - 它更像是一个UTF-8编码的字符串。(虽然对于大多数字符,你的前缀应该是\xC3,而不是\xC2)。在95%的情况下,除非你正在与COBOL后端通信,否则你不应该将它们丢弃。你知道,世界不仅限于26个字符。

有一篇简明的文章可以解释Unicode字符串(在Python 2中用作Unicode对象,在Python 3中用作字符串)和这里使用的字符串之间的区别:http://www.joelonsoftware.com/articles/Unicode.html - 请务必阅读。即使您从未计划在所有应用程序中使用英语以外的任何内容,您仍将遇到无法适应7位ASCII的符号,如€或º。那篇文章会帮助你。

话虽如此,也许你正在使用的库接受Unicode Python对象,你可以通过以下方式将你的UTF-8 Python 2字符串转换为Unicode:

var_unicode = var.decode("utf-8")

如果你真的需要100%纯ASCII,可以在将字符串解码为Unicode后,将所有非ASCII字符替换为ASCII字符,然后重新将其编码为ASCII,并告诉它忽略不适合字符集的字符。
var_ascii = var_unicode.encode("ascii", "replace")

问题不在于Unicode与ASCII的区别。我所依赖的库和服务支持UTF-8,但某些字符会导致它们出现问题。因此,我会将这些字符删除,因为它们并不重要。 - hoju
我所依赖的库和服务支持utf-8,但在处理某些字符时会出现问题。因此,它们并不是完全支持UTF-8,而是支持其子集。 - JAB
好的!无论如何,他们声称支持UTF-8。 - hoju

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接