我想检查一个字符串是否是ASCII格式。
我知道ord(),但是当我尝试ord('é')时,我有TypeError: ord() expected a character, but string of length 2 found。我知道这是由我构建python的方式引起的(如ord()的文档中所述)。
还有其他检查方法吗?
- 字符串编码在python 2和python 3之间有很大的不同,所以最好知道要针对哪个版本。
我觉得你问的问题不对--
python中的字符串没有对应于"ascii"、utf-8或任何其他编码的属性。字符串的来源(无论是从文件中读取,还是从键盘输入等)可能已经用ASCII编码了一个Unicode字符串来生成字符串,但这正是您需要回答的地方。
您可能会问:"这个字符串是用ASCII编码Unicode字符串的结果吗?"--你可以回答这个问题尝试:
1 2 3 4 5 6
| 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" |
- 使用encode更好,因为python 3中的string-no-decode方法,看看encode/decode之间有什么区别?(Python 2。x)
- python 2.7,抛出unicodeencodeerror
- @SRI:这是因为您在未编码的字符串上使用它(Python2中的str,Python3中的bytes)。
- 在python 2中,此解决方案仅适用于Unicode字符串。任何ISO编码中的str都需要首先编码为unicode。答案应该是这样的。
- @Jetguo:您应该根据输入类型使用这两种方法:Python3中的s.decode('ascii') if isinstance(s, bytes) else s.encode('ascii')。op的输入是一个bytestring 'é'(python 2语法,python 3当时没有发布),因此.decode()是正确的。
- @SRI:您传递了一个Unicode字符串,而不是字节字符串。python 2试图隐式地使用sys.getdefaultencoding()('ascii'进行编码。如果输入是Unicode,则显式使用.encode('ascii', 'strict')。
- @亚历克西斯:错了。python 2上的str是一个字节字符串。使用.decode('ascii')来确定所有字节是否都在ASCII范围内是正确的。
- @Jetguo op的问题是关于python 2。在这里使用mystring.encode('ascii')将首先尝试使用系统默认值对字符串进行解码以创建Unicode字符串,然后将该字符串重新编码为指定的编码,这是一种糟糕的方法。
1 2
| def is_ascii(s):
return all(ord(c) < 128 for c in s) |
- 毫无意义的效率低下。最好尝试s.decode("ascii")并捕获unicodedecodeerror,正如Vincent Marchetti建议的那样。
- 它不是低效的。all()一旦遇到无效字节,就会短路并返回false。
- 不管是否效率低下,尝试/排除的方法都是更为简单的方法。
- 与尝试/例外相比,它效率很低。循环在解释器中。对于try/except形式,循环位于str.decode("ascii")调用的C编解码器实现中。我同意,"尝试/排除"的形式也更像是Python。
- -1不仅是对python代码的循环而不是C代码,而且还有一个python函数调用ord(c)--难看--至少使用c <="\x7F"。
- @约翰马钦·埃多克斯(johnmachin EDOCX1)(14)比埃多克斯(EDOCX1)(13)更易读、更直观。
- "效率低下"取决于字符串的长度和ASCII数据的可能性;对于非ASCII的短字符串,此函数可能比设置Try/Except块和处理异常更快。
- .decode('ascii')也会很快失效。
- 使用try-catch阻滞不是Python,而是滥用。
- 似乎人们对做Python比神志清醒更疯狂。这个答案对我来说比尝试要容易得多,除非我想在列表理解中使用它。我想过滤一个大的文本语料库,把所有单词都扔掉,而不是ASCII。你会怎么做呢?除了?
Python 3路:
1
| isascii = lambda s: len(s) == len(s.encode()) |
- 这是在Unicode字符串中检测非ASCII字符的一个很好的小技巧,而在python3中,几乎所有的字符串都是非ASCII字符。由于ASCII字符只能使用1个字节进行编码,因此任何ASCII字符的长度在编码为字节后都将与其大小一致;而其他非ASCII字符将相应地编码为2个字节或3个字节,这将增加它们的大小。
- 到目前为止,最好的答案是,但有些字符,如…和-可能看起来像ASCII,所以如果你想用它来检测英文文本,请在检查之前替换这些字符。
- 但是在python2中,它会抛出一个单码编码错误。必须找到Py2和Py3的解决方案
- 对于那些不熟悉使用lambda的人(就像我第一次看到这个答案时一样),isascii现在是一个函数,可以传递一个字符串:isascii('somestring')==True和isascii('àéç')==False。
- 这真是太美了。
- 这简直是浪费。它用UTF-8编码一个字符串,创建一个完整的其他字节串。真正的python 3方式是try: s.encode('ascii'); return Trueexcept UnicodeEncodeError: return False(和上面一样,但是编码,因为字符串在python 3中是Unicode的)。当您有代理时,这个答案还会在python 3中引发一个错误(例如,isascii('\uD800')引发一个错误而不是返回False)
- 这看起来很漂亮,但我想知道它在处理一根长绳时是否和all一样有效
最近碰到这样的事-供将来参考
1 2 3 4 5
| import chardet
encoding = chardet.detect(string)
if encoding['encoding'] == 'ascii':
print 'string is in ascii' |
可用于:
1
| string_ascii = string.decode(encoding['encoding']).encode('ascii') |
- 当然,这需要chardet库。
- 是的,尽管chardet在大多数安装中默认是可用的
- chardet只猜测具有某种概率的编码:{'confidence': 0.99, 'encoding': 'EUC-JP'}(在本例中是完全错误的)
您的问题不正确;您看到的错误不是您如何构建Python的结果,而是字节字符串和Unicode字符串之间的混淆。
字节字符串(如python语法中的"foo"或"bar")是八位字节的序列;0-255之间的数字。Unicode字符串(例如U"foo"或U"bar")是Unicode码位的序列;0-1112064中的数字。但您似乎对字符_感兴趣,它(在您的终端中)是表示单个字符的多字节序列。
尝试以下方法,而不是使用ord(u'é'):
1
| >>> [ord(x) for x in u'é'] |
它告诉你代码点的序列"_"代表什么。它可以给你[233],也可以给你[101770]。
与chr()相反,unichr()是:
1 2
| >>> unichr(233)
u'\xe9' |
这个字符实际上可以表示为一个或多个Unicode"代码点",它们本身可以表示图形或字符。它可以是"e,带有锐音符(即代码点233)",也可以是"e"(代码点101),后跟"前一个字符的锐音符"(代码点770)。因此,这个完全相同的字符可以表示为python数据结构u'e\u0301'或u'\u00e9'。
大多数情况下,您不必关心这个问题,但是如果您在一个Unicode字符串上迭代,这可能会成为一个问题,因为迭代是按代码点进行的,而不是按可分解字符进行的。换言之,len(u'e\u0301') == 2和len(u'\u00e9') == 1。如果这对您很重要,可以使用unicodedata.normalize在组合形式和分解形式之间进行转换。
Unicode术语表是理解其中一些问题的有用指南,它指出了每个特定术语是如何引用文本表示的不同部分的,这比许多程序员认识到的要复杂得多。
- "&233;"不一定表示单个代码点。它可以是两个代码点(U+0065+U+0301)。
- 每个抽象字符总是由一个代码点表示。但是,根据编码方案,代码点可以编码为多个字节。也就是说,&233;'是两个字节的UTF-8和UTF-16,四个字节的UTF-32,但在每种情况下,它仍然是一个单一的码位-U+00E9。
- @本空白:U+0065和U+0301是代码点,它们确实表示"&233;",也可以用U+00E9表示。谷歌"结合了尖锐的口音"。
- J.F.将U+0065和U+0301组合成"&233;"是正确的,但这不是可逆函数。你会得到U+00E9。根据维基百科,这些复合代码点对于向后兼容性很有用
- @teehoo——从某种意义上说,它是一个可逆函数,可以将表示合成字符的代码点重新规范化为表示相同合成字符的代码点序列。在python中,您可以这样做:unicodedata.normalize('nfd',u'xe9')。
- 我更新了答案,试图解决一些反馈以及对问题所做的更改。
- +1表示编辑的答案和评论。
python 3.7中的新功能(bpo32677)
不再对字符串进行烦人/低效的ASCII检查,新的内置str/bytes/bytearray方法-.isascii()将检查字符串是否为ASCII。
1 2
| print("is this ascii?".isascii())
# True |
文森特·马切蒂的想法是正确的,但是在python 3中,str.decode已经被否决了。在python 3中,可以使用str.encode进行相同的测试:
1 2 3 4 5 6
| try:
mystring.encode('ascii')
except UnicodeEncodeError:
pass # string is not ascii
else:
pass # string is ascii |
注意,您希望捕获的异常也已从UnicodeDecodeError更改为UnicodeEncodeError。
- op的输入是一个bytestring(python 3中的bytes类型,没有.encode()方法)。@vincent marchetti的回答是正确的。
- @J.F.Sebastian操作询问"如何检查Python中的字符串是否为ASCII?"并且不指定字节与Unicode字符串。你为什么说他/她的输入是字节串?
- 看看问题的日期:当时,'é'是一个字节数。
- @J.F.Sebastian,好吧,考虑到这个答案,回答这个问题就像今天问的一样,我认为它仍然有效和有用。越来越少的人来到这里寻找答案,就像他们在2008年运行python一样。
- OP今天不会问这个问题:python 3上的ord('é') == 1(通常可以大于1,但与ASCII范围内的字符串无关)。无论如何,如果没有指定输入字符串是否为Unicode,则可以同时使用:s.decode('ascii') if isinstance(s, bytes) else s.encode('ascii')。
- 我在为python3寻找解决方案时发现了这个问题,快速阅读这个问题并没有让我怀疑这是python 2特有的。但这个答案真的很有帮助-投票!
- 我更喜欢这个答案,因为我使用的是python3,根据定义,所有字符串都是Unicode字符串,因此会调用.encode()方法。这个答案比另一个比较字符长度编码前和编码后的答案更让人感觉到Python。
做这个怎么样?
1 2 3 4 5 6 7
| import string
def isAscii(s):
for c in s:
if c not in string.ascii_letters:
return False
return True |
- 如果字符串包含非字母的ASCII字符,则此操作失败。对于代码示例,包括换行符、空格、点、逗号、下划线和括号。
我在确定如何使用/编码/解码我不确定其编码的字符串(以及如何转义/转换该字符串中的特殊字符)时发现了这个问题。
我的第一步应该是检查字符串的类型-我没有意识到在那里我可以从类型中获得关于其格式的好数据。这个答案很有帮助,并找到了我问题的真正根源。
如果你变得粗鲁和执著
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 263: ordinal not in range(128)
尤其是在编码时,请确保您没有尝试unicode()一个已经是unicode的字符串-由于一些可怕的原因,您会得到ascii编解码器错误。(另请参阅python kitchen菜谱和python docs教程,以更好地了解这有多可怕。)
最终我决定我要做的是:
1
| escaped_string = unicode(original_string.encode('ascii','xmlcharrefreplace')) |
调试的另一个帮助是将my文件中的默认编码设置为utf-8(将其放在python文件的开头):
允许您测试特殊字符("_?")不必使用它们的Unicode转义符(u'xe0xe9xe7')。
1 2 3
| >>> specials='àé?'
>>> specials.decode('latin-1').encode('ascii','xmlcharrefreplace')
'àéç' |
要从python 2.6(以及python 3.x)改进Alexander的解决方案,可以使用helper模块curses.ascii并使用curses.ascii.isascii()函数或其他各种函数:https://docs.python.org/2.6/library/curses.ascii.html
1 2 3 4
| from curses import ascii
def isascii(s):
return all(ascii.isascii(c) for c in s) |
- 它是有效的,但要注意,curses.ascii中的字符分类功能存在已知问题。
您可以使用正则表达式库,该库接受POSIX标准[[:ascii:]]定义。
python中的sting(str类型)是一系列字节。从字符串来看,无法判断这一系列字节是否代表一个ASCII字符串、一个8位字符集(如ISO-8859-1)中的字符串、或是一个用UTF-8或UTF-16编码的字符串。
但是,如果您知道所使用的编码,那么您可以将str转换成unicode字符串,然后使用正则表达式(或循环)检查它是否包含超出您关心的范围的字符。
为了防止代码崩溃,您可能需要使用try-except来捕获TypeErrors。
1 2 3 4
| >>> ord("?")
Traceback (most recent call last):
File"<stdin>", line 1, in <module>
TypeError: ord() expected a character, but string of length 2 found |
例如
1 2 3 4 5
| def is_ascii(s):
try:
return all(ord(c) < 128 for c in s)
except TypeError:
return False |
1 2 3 4
| import re
def is_ascii(s):
return bool(re.match(r'[\x00-\x7F]+$', s)) |
要将空字符串包含为ASCII,请将+更改为*。
就像@rogerdahl的答案一样,但是通过否定字符类并使用搜索而不是find_all或match,短路更有效。
1 2 3 4 5
| >>> 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 |
我认为正则表达式对此进行了很好的优化。
我使用以下内容来确定字符串是ASCII还是Unicode:
1 2 3 4 5
| >> print 'test string'.__class__.__name__
str
>>> print u'test string'.__class__.__name__
unicode
>>> |
然后使用条件块定义函数:
1 2 3 4
| def is_ascii(input):
if input.__class__.__name__ =="str":
return True
return False |
- -1 aarrgghh这将ORD(c)在范围(128、256)内的所有字符都视为ASCII!!!!
- 不起作用。尝试拨打以下电话:is_ascii(u'i am ascii')。尽管字母和空格绝对是ASCII码,但仍然返回False,因为我们强制字符串为unicode。