如何检查Python中的字符串是不是为ASCII?
Posted
技术标签:
【中文标题】如何检查Python中的字符串是不是为ASCII?【英文标题】:How to check if a string in Python is in ASCII?如何检查Python中的字符串是否为ASCII? 【发布时间】:2010-09-16 19:36:07 【问题描述】:我想检查一个字符串是否是ASCII码。
我知道ord()
,但是当我尝试ord('é')
时,我有TypeError: ord() expected a character, but string of length 2 found
。我知道这是由我构建 Python 的方式引起的(如ord()
's documentation 中所述)。
还有其他方法可以检查吗?
【问题讨论】:
字符串编码在 Python 2 和 Python 3 之间有很大的不同,所以最好知道你的目标是哪个版本。 @florisla 基于来自ord('é')
的错误,OP 使用的是 Python 2。
【参考方案1】:
我认为你没有问正确的问题--
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"
【讨论】:
使用encode比较好,因为python 3中string没有decode方法,见what's the difference between encode/decode? (python 2.x) @Sri:那是因为你在一个未编码的字符串上使用它(str
在 Python 2 中,bytes
在 Python 3 中)。
在 Python 2 中,此解决方案仅适用于 unicode 字符串。任何 ISO 编码中的 str
都需要首先编码为 Unicode。答案应该是这个。
@JetGuo:您应该根据输入类型同时使用两者:Python 3 中的s.decode('ascii') if isinstance(s, bytes) else s.encode('ascii')
。OP 的输入是一个字节串'é'
(Python 2 语法,Python 3 尚未发布时间),因此.decode()
是正确的。
@alexis:错了。 Python 2 上的 str
是一个字节串。使用.decode('ascii')
判断是否所有字节都在ascii范围内是正确的。【参考方案2】:
def is_ascii(s):
return all(ord(c) < 128 for c in s)
【讨论】:
毫无意义的低效。按照 Vincent Marchetti 的建议,尝试 s.decode('ascii') 并捕获 UnicodeDecodeError 会更好。 这不是低效的。 all() 将在遇到无效字节时短路并返回 False。 低效与否,比较pythonic的方法是try/except。 与try/except相比效率低下。这里循环在解释器中。使用 try/except 形式,循环位于 str.decode('ascii') 调用的 C 编解码器实现中。我同意,try/except 形式也更 Pythonic。 @JohnMachinord(c) < 128
比 c <= "\x7F"
更具可读性和直观性【参考方案3】:
在 Python 3 中,我们可以将字符串编码为 UTF-8,然后检查长度是否保持不变。如果是这样,那么原始字符串是 ASCII。
def isascii(s):
"""Check if the characters in string s are in ASCII, U+0-U+7F."""
return len(s) == len(s.encode())
要检查,请通过测试字符串:
>>> isascii("♥O◘♦♥O◘♦")
False
>>> isascii("Python")
True
【讨论】:
这是一个很好的小技巧,可以检测 Unicode 字符串中的非 ascii 字符,这在 python3 中几乎是所有字符串。由于 ascii 字符只能使用 1 个字节进行编码,因此任何 ascii 字符的长度在编码为字节后都将与其大小一致;而其他非 ascii 字符将相应地编码为 2 个字节或 3 个字节,这将增加它们的大小。 @far 是最好的答案,但不是某些字符,比如……和 — 可能看起来像 ascii,所以如果你想用它来检测英文文本,请在检查之前替换这些字符跨度> 但是在 Python2 中它会抛出一个 UnicodeEncodeError。必须为 Py2 和 Py3 找到解决方案 这简直是浪费。它用 UTF-8 编码一个字符串,创建一个完整的其他字节串。真正的 Python 3 方式是try: s.encode('ascii'); return True
except UnicodeEncodeError: return False
(如上,但编码,因为字符串在 Python 3 中是 Unicode)。当您有代理项时,此答案也会在 Python 3 中引发错误(例如,isascii('\uD800')
引发错误而不是返回 False
)
这个看起来挺漂亮的,不过不知道是不是和all
处理长字符串一样高效【参考方案4】:
Python 3.7 中的新功能 (bpo32677)
不再对字符串进行繁琐/低效的 ascii 检查,新的内置 str
/bytes
/bytearray
方法 - .isascii()
将检查字符串是否为 ascii。
print("is this ascii?".isascii())
# True
【讨论】:
"\x03".isascii()
也是 True。文档说这只是检查所有字符是否低于代码点 128 (0-127)。如果您还想避免使用控制字符,则需要:text.isascii() and text.isprintable()
。仅使用 isprintable
本身也是不够的,因为它会认为像 ¿ 这样的字符是(正确)可打印的,但它不在 ascii 可打印部分内,所以如果你想要两者都需要检查。还有一个问题:空格被认为是可打印的,制表符和换行符不是。
@Luc 很高兴知道,但 ASCII 包含控制字符。避免它们是另一个话题。
@wjandrea 是的,很明显,但是因为 0x03 适合 7 位并不意味着大多数人在搜索结果中找到此页面时会想要检查它。
@Luc 是的,完全正确。如果有人认为所有 ASCII 字符都可以安全打印,那他们就大错特错了,但这是一个有效的话题,值得提出自己的问题。【参考方案5】:
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
请注意,您要捕获的异常也已从 UnicodeDecodeError
更改为 UnicodeEncodeError
。
【讨论】:
OP 的输入是一个字节串(Python 3 中的bytes
类型没有.encode()
方法)。 .decode()
in @Vincent Marchetti's answer is correct.
@J.F.Sebastian OP 询问“如何检查 Python 中的字符串是否为 ASCII?”并且没有指定字节与 Unicode 字符串。为什么说他/她的输入是字节串?
看问题的日期:'é'
当时是一个字节串。
@J.F.Sebastian,好的,考虑到这个答案就像今天被问到的那样回答了这个问题,我认为它仍然有效且有帮助。越来越少的人会来这里寻找答案,就像他们在 2008 年运行 Python 一样
我在寻找 python3 的解决方案时发现了这个问题,并且快速阅读了这个问题并没有让我怀疑这是 python 2 特有的。但这个答案真的很有帮助——点赞!【参考方案6】:
您的问题不正确;您看到的错误不是您如何构建 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') == 2
和 len(u'\u00e9') == 1
。如果这对您很重要,您可以使用 unicodedata.normalize
在组合形式和分解形式之间进行转换。
The Unicode Glossary 可以成为理解其中一些问题的有用指南,通过指出每个特定术语如何引用文本表示的不同部分,这比许多程序员意识到的要复杂得多。
【讨论】:
'é' 不一定 一定代表单个代码点。它可能是两个代码点(U+0065 + U+0301)。 每个抽象字符总是由单个代码点表示。但是,代码点可能被编码为多个字节,具体取决于编码方案。即,'é' 在 UTF-8 和 UTF-16 中是两个字节,在 UTF-32 中是四个字节,但在每种情况下它仍然是一个代码点 - U+00E9。 @Ben Blank: U+0065 和 U+0301 是代码点,它们do代表'é',它可以也 由 U+00E9 表示。谷歌“结合尖锐的口音”。 J.F.将 U+0065 和 U+0301 组合成 'é' 是正确的,但这不是一个可逆的函数。你会得到 U+00E9。根据wikipedia,这些复合代码点对于向后兼容很有用 @teehoo - 从某种意义上说,它是一个可逆函数,您可以将表示组合字符的代码点重新规范化为表示相同组合字符的代码点序列。在 Python 中,您可以这样做:unicodedata.normalize('NFD', u'\xe9')。【参考方案7】:最近遇到了这样的事情 - 以供将来参考
import chardet
encoding = chardet.detect(string)
if encoding['encoding'] == 'ascii':
print 'string is in ascii'
你可以使用:
string_ascii = string.decode(encoding['encoding']).encode('ascii')
【讨论】:
当然,这需要chardet库。 是的,尽管 chardet 在大多数安装中默认可用 chardet 仅以一定的概率猜测编码,如下所示:'confidence': 0.99, 'encoding': 'EUC-JP'
(在这种情况下完全错误)【参考方案8】:
这样做怎么样?
import string
def isAscii(s):
for c in s:
if c not in string.ascii_letters:
return False
return True
【讨论】:
如果您的字符串包含非字母的 ASCII 字符,则会失败。对于您的代码示例,包括换行符、空格、点、逗号、下划线和括号。【参考方案9】:我在尝试确定如何使用/编码/解码我不确定其编码的字符串(以及如何转义/转换该字符串中的特殊字符)时发现了这个问题。
我的第一步应该是检查字符串的类型——我没有意识到我可以从类型中获得关于其格式的良好数据。 This answer was very helpful and got to the real root of my issues.
如果你变得粗鲁又固执
UnicodeDecodeError:“ascii”编解码器无法解码位置 263 中的字节 0xc3:序数不在范围内 (128)
特别是在编码时,请确保您没有尝试对已经是 unicode 的字符串进行 unicode() 处理——由于某些可怕的原因,您会收到 ascii 编解码器错误。 (另请参阅 Python Kitchen recipe 和 Python docs 教程,以更好地了解这有多可怕。)
最终我确定我想做的是:
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')
'àéç'
【讨论】:
【参考方案10】:要从 Python 2.6(和 Python 3.x)改进 Alexander 的解决方案,您可以使用辅助模块 curses.ascii 并使用 curses.ascii.isascii() 函数或其他各种函数:https://docs.python.org/2.6/library/curses.ascii.html
from curses import ascii
def isascii(s):
return all(ascii.isascii(c) for c in s)
【讨论】:
它有效,但要小心there are known issues with character classification functions fromcurses.ascii
【参考方案11】:
您可以使用接受 Posix 标准 [[:ASCII:]] 定义的正则表达式库。
【讨论】:
Python 标准库中的re
模块不支持POSIX 字符类。【参考方案12】:
Python 中的字符串 (str
-type) 是一系列字节。 没有办法仅通过查看字符串来判断这一系列字节是表示 ascii 字符串、ISO-8859-1 等 8 位字符集中的字符串还是使用 UTF- 编码的字符串8 或 UTF-16 或其他。
但是,如果您知道使用的编码,那么您可以将decode
str 转换为 unicode 字符串,然后使用正则表达式(或循环)检查它是否包含您关注的范围之外的字符。
【讨论】:
【参考方案13】:类似于@RogerDahl 的answer,但通过否定字符类并使用搜索而不是find_all
或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
我想正则表达式对此进行了很好的优化。
【讨论】:
【参考方案14】:import re
def is_ascii(s):
return bool(re.match(r'[\x00-\x7F]+$', s))
要将空字符串包含为 ASCII,请将 +
更改为 *
。
【讨论】:
【参考方案15】:为了防止您的代码崩溃,您可能需要使用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
将继续起作用。【参考方案16】:
我使用以下方法来确定字符串是 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
【讨论】:
-1 AARRGGHH 这是将范围(128, 256)中带有 ord(c) 的所有字符视为 ASCII !!! 不起作用。尝试拨打以下电话:is_ascii(u'i am ascii')
。即使字母和空格绝对是 ASCII,这仍然返回 False
,因为我们强制字符串为 unicode
。以上是关于如何检查Python中的字符串是不是为ASCII?的主要内容,如果未能解决你的问题,请参考以下文章