如何检查Python中的字符串是否为ASCII?


211

我想检查字符串是否为ASCII。

我知道ord(),但是当我尝试时ord('é'),我知道了TypeError: ord() expected a character, but string of length 2 found。我了解这是由我构建Python的方式引起的(如ord()的文档中所述)。

还有另一种检查方法吗?


Python 2和Python 3之间的字符串编码相差很大,因此最好知道您要定位的版本。
florisla

Answers:


188
def is_ascii(s):
    return all(ord(c) < 128 for c in s)

95
毫无意义的低效率。如Vincent Marchetti所建议的那样,尝试s.decode('ascii')并捕获UnicodeDecodeError更好。
ddaa

20
这不是低效率的。all()将短路并在遇到无效字节后立即返回False。
约翰·米利金

10
不管效率低与否,更多的pythonic方法是try / except。
杰里米·坎特雷尔

43
与try / except相比,它效率低下。循环在解释器中。使用try / except形式时,循环位于str.decode('ascii')调用的C编解码器实现中。我同意,try / except形式也更像pythonic。
ddaa

25
@JohnMachin ord(c) < 128c <= "\x7F"
Slater Victoroff

252

我认为您不是在问正确的问题-

python中的字符串没有与'ascii',utf-8或任何其他编码对应的属性。字符串的来源(无论您是从文件中读取字符串,还是从键盘输入等等)可能已经在ASCII中编码了一个unicode字符串以生成您的字符串,但这就是您需要答案的地方。

也许您会问的问题是:“此字符串是在ASCII中编码unicode字符串的结果吗?” -您可以尝试以下方法回答:

try:
    mystring.decode('ascii')
except UnicodeDecodeError:
    print "it was not a ascii-encoded unicode string"
else:
    print "It may have been an ascii-encoded unicode string"

28
使用encoding更好,因为python 3中的string没有解码方法,请看编码/解码之间有什么区别?(python 2.x)
郭沫若

@Sri:这是因为您在未编码的字符串上使用它(str在Python 2中,bytes在Python 3中)。
dotancohen

在Python 2中,此解决方案仅适用于unicode字符串。str任何ISO编码中的A 都需要先编码为Unicode。答案应该在此。
亚历克西斯

@JetGuo:您应该根据输入类型同时使用两者:s.decode('ascii') if isinstance(s, bytes) else s.encode('ascii')在Python 3中。OP 的输入是一个字节串'é'(Python 2语法,当时尚未发布Python 3),因此.decode()是正确的。
jfs 2015年

2
@alexis:错了。str在Python 2上是一个字节串。.decode('ascii')找出所有字节是否都在ascii范围内是正确的。
jfs 2015年

153

Python 3方式:

isascii = lambda s: len(s) == len(s.encode())

要进行检查,请传递测试字符串:

str1 = "♥O◘♦♥O◘♦"
str2 = "Python"

print(isascii(str1)) -> will return False
print(isascii(str2)) -> will return True

7
这是一个很好的小技巧,可以检测Unicode字符串中的非ASCII字符,而python3中的字符串几乎是所有字符串。由于只能使用1个字节对ascii字符进行编码,因此在编码为字节后,任何ascii字符的长度都将符合其大小;而其他非ASCII字符将被相应地编码为2个字节或3个字节,这将增加它们的大小。
Devy

通过@far最佳答案,但不是某些字符,例如…和–可能看起来像ascii,因此,如果您要使用此字符来检测英文文本,请在检查之前替换此类字符
Christophe Roussy

1
但是在Python2中,它将引发UnicodeEncodeError。找到了Py2和Py3的解决方案
alvas

2
对于那些不熟悉使用lambda的人(就像我第一次遇到这个答案时一样)isascii,现在可以传递一个字符串:isascii('somestring')== Trueisascii('àéç')==False
rabidang3ls

8
这只是浪费。它以UTF-8编码一个字符串,从而创建另一个字节字节。真正的Python 3方法是try: s.encode('ascii'); return True except UnicodeEncodeError: return False(就像上面一样,但是编码,因为字符串在Python 3中是Unicode)。当您有代理人时,此答案还会在Python 3中isascii('\uD800')引发错误(例如,引发错误而不是返回错误False
Artyer

71

Python 3.7的新功能(bpo32677

没有更多的无聊/对字符串低效ASCII检查,新的内置str/ bytes/ bytearray方法- .isascii()将检查字符串是ASCII。

print("is this ascii?".isascii())
# True

这一个当之无愧!
Salek

"\x03".isascii()也是如此。文档说这只是检查所有字符是否在代码点128(0-127)以下。如果您还想避免使用控制字符,则需要:text.isascii() and text.isprintable()isprintable仅靠单独使用也是不够的,因为它将认为像¿这样的字符是(正确)可打印的,但是它不在ascii可打印的部分之内,因此,您需要同时检查这两个字符。还有一个陷阱:空格被认为是可打印的,制表符和换行符则不可。
吕克

19

最近遇到了类似的情况-供将来参考

import chardet

encoding = chardet.detect(string)
if encoding['encoding'] == 'ascii':
    print 'string is in ascii'

您可以使用:

string_ascii = string.decode(encoding['encoding']).encode('ascii')


1
是的,尽管在大多数安装中默认都可以使用chardet
Alvin 2012年

7
chardet仅以这样的一定概率猜测编码:({'confidence': 0.99, 'encoding': 'EUC-JP'}在这种情况下是完全错误的)
Suzana 2013年

19

Vincent Marchetti的想法正确,但str.decode已在Python 3中弃用。在Python 3中,您可以使用以下命令进行相同的测试str.encode

try:
    mystring.encode('ascii')
except UnicodeEncodeError:
    pass  # string is not ascii
else:
    pass  # string is ascii

请注意,您要捕获的异常也已从更改UnicodeDecodeErrorUnicodeEncodeError


OP的输入是一个字节串(bytes在Python 3中为类型,没有.encode()方法)。.decode()@Vincent Marchetti的答案是正确的
jfs 2015年

@JFSebastian OP询问“如何检查Python中的字符串是否为ASCII?” 并且未指定字节vs Unicode字符串。为什么说他/她的输入是一个字节串?
drs

1
看问题的日期:'é'当时是一个字节串。
jfs 2015年

1
@JFSebastian,好的,考虑这个答案可以很好地回答这个问题,就好像今天有人问我一样,我认为它仍然是有效和有用的。越来越少的人来这里寻找答案,就好像他们在2008
drs

2
我在寻找python3的解决方案时发现了这个问题,并迅速阅读了该问题,但并没有使我怀疑这是python 2特有的。但是,这个答案确实很有帮助-令人赞叹!
josch 2015年

17

您的问题不正确;您看到的错误不是您构建python的结果,而是字节字符串和unicode字符串之间的混淆。

字节字符串(例如python语法中的“ foo”或“ bar”)是八位字节序列;0-255之间的数字。Unicode字符串(例如u“ foo”或u'bar')是unicode代码点的序列;从0-1112064开始的数字。但是您似乎对字符é感兴趣,该字符é(在您的终端中)是一个多字节序列,代表一个字符。

代替ord(u'é'),试试这个:

>>> [ord(x) for x in u'é']

这就告诉您“é”代表的代码点顺序。它可以给您[233],也可以给您[101,770]。

除了chr()扭转这种情况,还有unichr()

>>> unichr(233)
u'\xe9'

该字符实际上可以表示为单个或多个unicode“代码点”,它们本身表示字素或字符。它可以是“带有重音符号的e(即代码点233)”,也可以是“ e”(编码点101),后跟“上一个字符具有重音符号”(代码点770)。因此,这个完全相同的字符可以表示为Python数据结构u'e\u0301'u'\u00e9'

大多数情况下,您不必关心这一点,但是如果您要遍历unicode字符串,这可能会成为问题,因为迭代是通过代码点而不是通过可分解字符进行的。换句话说,len(u'e\u0301') == 2len(u'\u00e9') == 1。如果您认为这很重要,可以使用来在合成和分解形式之间进行转换unicodedata.normalize

Unicode词汇表可以指出每个特定术语如何指代文本表示形式的不同部分,这对于理解其中的一些问题可能是有用的指南,这比许多程序员意识到的要复杂得多。


3
“E”不一定代表一个单一的代码点。可能是两个代码点(U + 0065 + U + 0301)。
jfs

2
每个抽象字符始终由单个代码点表示。但是,根据编码方案,可以将代码点编码为多个字节。即,“é”在UTF-8和UTF-16中是两个字节,在UTF-32中是四个字节,但是在每种情况下,它仍然是单个代码点— U + 00E9。
本·布兰克

5
@Ben Blank:U + 0065和U + 0301 代码点,它们确实表示“é”,可以用U + 00E9表示。Google“结合了重音符号”。
jfs

JF将U + 0065和U + 0301组合成'é'是正确的,但这不是可逆的函数。您将得到U + 00E9。根据维基百科,这些复合码点是向后兼容性有用
马丁Konecny的

1
@teehoo-在您可以将代表合成字符的代码点重新规范化为代表相同合成字符的代码点序列的意义上,这是一个可逆的函数。在Python中,您可以这样做:unicodedata.normalize('NFD',u'\ xe9')。
Glyph

10

怎么样呢?

import string

def isAscii(s):
    for c in s:
        if c not in string.ascii_letters:
            return False
    return True

5
如果字符串包含不是字母的ASCII字符,则此操作将失败。对于您的代码示例,其中包括换行符,空格,点,逗号,下划线和括号。
florisla

9

我在尝试确定如何使用/编码/解码我不确定其编码的字符串(以及如何转义/转换该字符串中的特殊字符)时发现了这个问题。

我的第一步应该是检查字符串的类型-我不知道在那里可以从类型中获取有关其格式的良好数据。 这个答案非常有帮助,并且是我问题的真正根源。

如果您变得粗鲁和执着

UnicodeDecodeError:'ascii'编解码器无法解码位置263的字节0xc3:序数不在范围内(128)

尤其是在进行编码时,请确保您不尝试对已经是unicode的字符串进行unicode()-出于某种可怕的原因,您会遇到ascii编解码器错误。(另请参阅“ Python厨房食谱 ”和“ Python文档”教程,以更好地了解它的可怕程度。)

最终,我确定我想做的是:

escaped_string = unicode(original_string.encode('ascii','xmlcharrefreplace'))

在调试中也很有帮助,是将我文件中的默认编码设置为utf-8(将其放在python文件的开头):

# -*- coding: utf-8 -*-

这样,您就可以测试特殊字符('àéç'),而不必使用它们的Unicode转义符(u'\ xe0 \ xe9 \ xe7')。

>>> specials='àéç'
>>> specials.decode('latin-1').encode('ascii','xmlcharrefreplace')
'&#224;&#233;&#231;'



2

strPython中的字符串(-type)是一系列字节。有没有办法从看串只是告诉的这一系列字节是否代表一个ASCII字符串,在8位字符集,如ISO-8859-1或字符串使用UTF-8或UTF-16或任何编码的字符串。

但是,如果您知道所使用的编码,则可以decode将str转换为unicode字符串,然后使用正则表达式(或循环)检查其是否包含您所关注范围之外的字符。


1

就像@RogerDahl的答案一样,但通过否定字符类并使用search代替find_allor 来短路更有效match

>>> import re
>>> re.search('[^\x00-\x7F]', 'Did you catch that \x00?') is not None
False
>>> re.search('[^\x00-\x7F]', 'Did you catch that \xFF?') is not None
True

我认为正则表达式已对此进行了优化。


0
import re

def is_ascii(s):
    return bool(re.match(r'[\x00-\x7F]+$', s))

要将空字符串包含为ASCII,请将更改+*


-1

为防止代码崩溃,您可能需要使用a try-except来捕获TypeErrors

>>> ord("¶")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: ord() expected a character, but string of length 2 found

例如

def is_ascii(s):
    try:
        return all(ord(c) < 128 for c in s)
    except TypeError:
        return False

这个try包装器是完全没有意义的。如果"¶"是Unicode字符串,则ord("¶")可以使用;如果不是,则可以使用(Python 2),for c in s将其分解为字节,以便ord继续使用。
Ry-

-5

我使用以下命令确定字符串是ascii还是unicode:

>> print 'test string'.__class__.__name__
str
>>> print u'test string'.__class__.__name__
unicode
>>> 

然后只需使用条件块来定义函数:

def is_ascii(input):
    if input.__class__.__name__ == "str":
        return True
    return False

4
-1 AARRGGHH这会将范围(128,256)中的ord(c)的所有字符都视为ASCII!
约翰·马钦

不起作用 尝试致电以下内容:is_ascii(u'i am ascii')。即使字母和空格绝对是ASCII,这仍然会返回,False因为我们将字符串强制为unicode
jpmc26 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.