如何高效地删除非ASCII字符和数字,但保留带重音的ASCII字符

3

我有几个像这样的字符串:

s = u'awëerwq\u0645\u0631\u062d\u0628\u0627\u043c\u0438\u0440bròn 1990 23x4 + &23 \'we\' we\'s mexicqué'
s
"awëerwq مرحباмир bròn 1990 23x4 + &23 'we' we's mexicqué"

我找不到一种方法来去除非可打印的字符,比如 'مرحباми',但保留拉丁字符,比如 'óé, ...'。在我的情况下,数字(比如 '1990')也是不想要的。我已经使用了 reASCII 标志,但我不知道它的问题在哪里,因为它删除了 'óëé,...'。使用 string.printable 也遇到了同样的问题。

ord('ë')
235

鉴于ASCII表被分配了137,我期望的结果应该是这样的:

x = some_method(s)
"awëerwq bròn 23x4 we we s mexicqué" 

接下来,我希望能够在编码时不依赖于未固定的编码方式。


看起来你想要 ord(c) < 256 的字符,但是排除像 +' 这样的字符。最好使用硬编码字符串包含所有你想保留的字符,然后只需执行 ''.join(c for c in s if c in okay_chars) - TigerhawkT3
抱歉,我误读了问题。您想保留代码页437中使用的所有字符,而不是ASCII,但有选择地删除数字。 ë 的Unicode值为235。在代码页437中,它的值为137。 - ayane_m
等等,如果你不想要数字,那么 23x4 是怎么通过的呢? - TigerhawkT3
你为什么认为阿拉伯文和西里尔文是“不可打印”的?一定有更好的说法。 - Jongware
通过阅读@Martin的帖子,我现在意识到了术语错误。谢谢大家。 - Nacho
2个回答

2
以下是一种可能有帮助的方法(适用于Python 3.4):
import unicodedata
def remove_nonlatin(s): 
    s = (ch for ch in s
         if unicodedata.name(ch).startswith(('LATIN', 'DIGIT', 'SPACE')))
    return ''.join(s)

>>> s = 'awëerwq\u0645\u0631\u062d\u0628\u0627\u043c\u0438\u0440bròn 1990 23x4 + &23 \'we\' we\'s mexicqué'
>>> remove_nonlatin(s)
'awëerwqbròn 1990 23x4  23 we wes mexicqué'

这会获取字符串中字符的Unicode名称,并匹配名称以LATIN、DIGIT或SPACE开头的字符。

例如,它将匹配以下内容:

>>> unicodedata.name('S')
'LATIN CAPITAL LETTER S'

这是不可能的:
>>> unicodedata.name('م')
'ARABIC LETTER MEEM'

我相信拉丁字符都有以“LATIN”开头的Unicode名称,因此这应该可以过滤掉其他书写脚本,同时保留数字和空格。在这个例子中,没有方便的一行代码来过滤标点符号,因此感叹号等也被过滤掉了。
您可以通过使用类似于 ord(c) < 0x250 的东西按代码点进行过滤,但是您可能会得到一些意外的东西。或者,您可以尝试通过unicodedata.category进行过滤。然而,“字母”类别包括许多脚本的字母,因此您仍然会得到一些这样的字符:“م”。

谢谢 Seth... 我为保留空格独特地添加了条件yielding。 - Nacho

0
我使用了re中的ASCII标志,但不知道出了什么问题,因为它删除了“óëé,...”。 我认为你的问题问错了。 ASCII中没有字符“óëé”。请在这里查看所有ASCII字符集并了解其基本性质:

https://en.wikipedia.org/wiki/ASCII#ASCII_printable_code_chart

看起来您正在使用的字符串是Unicode编码,因为它可以同时支持“مرحباми”和“'óëé”。

在这种情况下,您可以使用以下方法找到所需的字符范围:

http://jrgraphix.net/research/unicode_blocks.php

并且只包含拉丁字符(这将过滤掉例如阿拉伯字符)。

以下是一个例子:

import re
s = u"مرحباми123"

# prints "123" by keeping all characters from the following ranges:
# 0020 — 007F   Basic Latin
# 00A0 — 00FF   Latin-1 Supplement
# 0100 — 017F   Latin Extended-A
# 0180 — 024F   Latin Extended-B
print ''.join(re.findall(ur'[\u0020-\u007F\u00A0-\u00FF\u0100-\u017F\u0180-\u024F]+', s))

谢谢@Martin,你的帖子非常有见地。我遵循了Seth的建议,并牢记了你所指出的差异。 - Nacho

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