在Python中查找和替换非ASCII字符的正则表达式

8

我需要将一些非ASCII字符替换为“_”。

例如,
Tannh‰user -> Tannh_user
  • 如果我使用Python正则表达式,我该怎么做?
  • 有没有更好的方法来替换而不使用正则表达式?
7个回答

16
re.sub(r'[^\x00-\x7F]', '_', theString)

如果theString是Unicode,或者是ASCII占据值0至0x7F的编码字符串(如Latin-1、UTF-8等),则此方法可行。


9
为了回答这个问题:
'[\u0080-\uFFFF]'

将匹配第一个128个字符范围之外的任何UTF-8字符

re.sub('[\u0080-\uFFFF]+', '_', x)

将替换任何连续的非ASCII字符序列为下划线


很高兴看到有人回答了这个问题!即使是在列表底部。 - Sam Watkins

6

已更新至Python 3:

>>> 'Tannh‰user'.encode().decode('ascii', 'replace').replace(u'\ufffd', '_')
'Tannh___user'

首先使用encode()创建字节串-它默认使用UTF-8编解码器。如果您已经有了字节串,则可以跳过此编码步骤。 然后使用ascii编解码器将其转换为“普通”字符串。

这利用了UTF-8的特性,即所有非ASCII字符都被编码为值>= 0x80的字节序列。


Python 2的原始答案:

如何使用内置的str.decode方法来实现:

>>> 'Tannh‰user'.decode('ascii', 'replace').replace(u'\ufffd', '_')
u'Tannh___user'

您得到了一个Unicode字符串,如果需要,请将其转换为str。
您还可以将Unicode转换为str,因此一个非ASCII字符被替换为ASCII字符。但问题是,使用replace的unicode.encode将非ASCII字符翻译为'?',因此您不知道问号是否已经存在;请参见Ignacio Vazquez-Abrams的解决方案。
另一种方法是使用ord()并比较每个字符的值是否适合ASCII范围(0-127)-这适用于Unicode字符串和utf-8,latin和某些其他编码的str。
>>> s = 'Tannh‰user' # or u'Tannh‰user' in Python 2
>>> 
>>> ''.join(c if ord(c) < 128 else '_' for c in s)
'Tannh_user'

这不是百分之(%)而是千分之(‰),不确定它是否仍然是ASCII编码。它是吗? - joaquin
这是U+2030字符,千分号符号(不在ASCII中)。 - interjay

5

利用Python对字符编码的支持:

# coding: utf8
import codecs

def underscorereplace_errors(exc):
  return (u'_', exc.end)

codecs.register_error('underscorereplace', underscorereplace_errors)

print u'Tannh‰user'.encode('ascii', 'underscorereplace')

3

我更愿意对字符串中的每个字符都调用ord函数,逐个检查。如果 ord([char]) >= 128,那么该字符不是ASCII字符,需要进行替换。


2
通过神奇的正则表达式[ -~],可以解决它:
import re
re.sub(r"[^ -~]", "_", "Tannh‰user")
# 'Tannh_user'

解释:
  • ASCII字符是符号从空格到“~”,因此[ -~]可以捕获所有ASCII字符
  • 通过附加^,我们可以捕获所有非ASCII字符
  • 其余部分现在只是一种形式

1
如果您知道要替换哪些字符,可以应用字符串方法。
mystring.replace('oldchar', 'newchar')

-1:首先,应该是 ord(item)> 127。 然后,想想你的代码对于这个字符串 '\xa0'*1000 做了什么。 - tzot
@ΤΖΩΤΖΙΟΥ 被点踩的部分已经被消除。请注意,虽然您是正确的,它无法处理'\xa0'类型的表示,但它能够完美地处理任何一个可打印的单字符符号,我理解这是 OP 正在与之抗争的字符串类型。同时,请注意另一篇帖子提出了完全相同的方法。 - joaquin
我已经撤销了我的踩,但我无法找到任何其他答案,即:对于输入字符串中的每个适用字符,替换整个输入字符串。在我的'\xa0'*1000示例中,您的代码执行了整个mystring替换一千次,其中有999次是不必要的。如果您不同意此事,则在给定astr ='hello'; c = 0的情况下,循环后c的值是多少:“for char in astr:c + = 1; astr ='”?我说它将是5,而您可能认为它将是1。 - tzot
Brian的答案提出了相同的方法:检查字符串中每个字符的ord()值,并在其超过127时进行替换。我现在理解了你的观点。在你的第一条评论之后,我意识到我的代码无法处理mystring = '\xa0',因为它是先检查'',然后是'x'...,所以我以为这就是你的观点。现在我明白了,你指的是我忽略了另外一件事情:在搜索过程中for循环中的字符串未被改变,因此即使变量在循环内部已经被替换,重复的字符仍然会被检查。不是很高效。谢谢! - joaquin

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