Как проверить, находится ли строка в Python в ASCII?

212

Я хочу проверить, есть ли строка в ASCII или нет.

Я знаю ord(), однако, когда я пытаюсь ord('é'), у меня есть TypeError: ord() expected a character, but string of length 2 found. Я понял, что это вызвано тем, как я собирал Python (как объяснено в ord()документации ).

Есть ли другой способ проверить?

Нико
источник
Строковое кодирование довольно сильно отличается между Python 2 и Python 3, поэтому было бы хорошо знать, на какую версию вы ориентируетесь.
Флорисла

Ответы:

188
def is_ascii(s):
    return all(ord(c) < 128 for c in s)
Александр Кожевников
источник
95
Бессмысленно неэффективно. Намного лучше попробовать s.decode ('ascii') и поймать UnicodeDecodeError, как это предложил Винсент Маркетти.
ddaa
20
Это не неэффективно. all () замкнет и вернет False, как только встретит недопустимый байт.
Джон Милликин
10
Неэффективно или нет, тем более питонический метод является попыткой / исключением.
Джереми Кантрелл
43
Это неэффективно по сравнению с попыткой / исключением. Здесь цикл находится в интерпретаторе. С формой try / исключением цикл находится в реализации кодека C, вызываемой str.decode ('ascii'). И я согласен, форма «попробуй / кроме» тоже более питонна.
ddaa
25
@JohnMachin ord(c) < 128бесконечно более читаемым и понятным , чемc <= "\x7F"
Slater Victoroff
253

Я думаю, что вы не задаете правильный вопрос ...

Строка в python не имеет свойства, соответствующего 'ascii', utf-8 или любой другой кодировке. Источник вашей строки (независимо от того, читаете ли вы ее из файла, вводите с клавиатуры и т. Д.), Возможно, закодировал строку unicode в ascii, чтобы создать вашу строку, но вам нужно найти ответ.

Возможно, вы можете задать вопрос: «Является ли эта строка результатом кодирования строки Unicode в ascii?» - На это вы можете ответить, попробовав:

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
лучше использовать кодирование, потому что в Python 3 нет метода декодирования, смотрите, в чем разница между кодированием / декодированием? (python 2.x)
Jet Guo
@Sri: Это потому, что вы используете его в незашифрованной строке ( strв Python 2, bytesв Python 3).
Dotancohen
В Python 2 это решение работает только для строки Unicode . A strв любой кодировке ISO необходимо сначала кодировать в Unicode. Ответ должен идти в этом.
Алексис
@JetGuo: вы должны использовать оба в зависимости от типа ввода: s.decode('ascii') if isinstance(s, bytes) else s.encode('ascii')в Python 3. Ввод OP является байтовой строкой 'é'(синтаксис Python 2, Python 3 не был выпущен в то время) и, следовательно, .decode()является правильным.
JFS
2
@alexis: неправильно. strна Python 2 это строка байтов. Правильно использовать, .decode('ascii')чтобы узнать, все ли байты находятся в диапазоне ASCII.
JFS
153

Python 3 way:

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
Это хороший маленький трюк для обнаружения не-ascii символов в строках Unicode, которые в python3 - это почти все строки. Поскольку символы ascii могут быть закодированы с использованием только 1 байта, любая длина символов ascii будет соответствовать их размеру после кодирования в байтах; тогда как другие не-ascii символы будут закодированы в 2 байта или 3 байта соответственно, что увеличит их размеры.
Деви
@Far лучший ответ, но не то, что некоторые символы, такие как… и - могут выглядеть как ascii, поэтому, если вы хотите использовать это для обнаружения текста на английском языке, вам нужно заменить такие символы перед проверкой
Кристоф Русси,
1
Но в Python2 он выдаст ошибку UnicodeEncodeError. Добрался , чтобы найти решение как для py2 и PY3
Alvas
2
Для тех, кто не знаком с использованием лямбды (как я был, когда впервые столкнулся с этим ответом) isascii, теперь есть функция, передающая строку: isascii('somestring')== Trueи isascii('àéç')==False
rabidang3ls
8
Это просто расточительно. Он кодирует строку в UTF-8, создавая совершенно другую строку байтов. Истинный путь Python 3 есть try: s.encode('ascii'); return True except UnicodeEncodeError: return False(как и выше, но кодирование, так как строки в Юникоде в Python 3). Этот ответ также вызывает ошибку в Python 3, когда у вас есть суррогаты (например, isascii('\uD800')False
выдает
74

Новое в Python 3.7 ( bpo32677 )

Нет более утомительным / неэффективные проверки ASCII на строках, новый встроенный str/ bytes/ bytearrayметод - .isascii()не будет проверять , если строки в ASCII.

print("is this ascii?".isascii())
# True
abccd
источник
Этот заслуживает того, чтобы быть на вершине!
Салек
"\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')
Alvin
источник
7
Конечно, для этого нужна библиотека chardet .
StackExchange печально танцует
1
да, хотя chardet доступен по умолчанию в большинстве установок
Элвин
7
Чарде только с определенной вероятностью угадывает кодирование: {'confidence': 0.99, 'encoding': 'EUC-JP'}(что в данном случае было совершенно неверно)
Suzana
19

Винсент Маркетти имеет правильную идею, но 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.

DRS
источник
Ввод OP - это строка байтов ( bytesвведите в Python 3, у которого нет .encode()метода). .decode()в @Vincent Marchetti ответ правильный .
JFS
@JFSebastian ОП спрашивает: «Как проверить, есть ли строка в Python в ASCII?» и не определяет байты против строк Юникода. Почему вы говорите, что его / ее вход является строкой?
DRS
1
посмотрите на дату вопроса: 'é'был ли бистринг в то время.
Jfs
1
@JFSebastian, хорошо, учитывая, что этот ответ отвечает на этот вопрос, как если бы он был задан сегодня, я думаю, что он по-прежнему актуален и полезен. Все меньше и меньше людей приходят сюда в поисках ответов , как если бы они были запущены Python в 2008
DRS
2
Я нашел этот вопрос, когда искал решение для python3, и быстрое чтение вопроса не заставило меня заподозрить, что это специфика Python 2. Но этот ответ был действительно полезным - голосование против!
josch
17

Ваш вопрос неверен; ошибка, которую вы видите, является не результатом того, как вы создали Python, а из-за путаницы между строками байтов и строками Unicode.

Строки байтов (например, «foo» или «bar» в синтаксисе Python) представляют собой последовательности октетов; цифры от 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'

Этот символ на самом деле может быть представлен как одной или несколькими «кодовыми точками» Юникода, которые сами представляют графемы или символы. Это либо «e с острым акцентом (т.е. кодовая точка 233)», либо «e» (кодовая точка 101), за которым следует «острый акцент на предыдущий символ» (кодовая точка 770). Так что этот точно такой же символ может быть представлен как структура данных Python u'e\u0301'или u'\u00e9'.

Большую часть времени вам не нужно об этом беспокоиться, но это может стать проблемой, если вы перебираете строку в юникоде, поскольку итерация выполняется по коду, а не по разложимому символу. Другими словами, len(u'e\u0301') == 2и len(u'\u00e9') == 1. Если это важно для вас, вы можете конвертировать между составными и разложенными формами, используя unicodedata.normalize.

Глоссарий Unicode может быть полезным руководством для понимания некоторых из этих проблем, указывая, как каждый конкретный термин относится к другой части представления текста, что гораздо сложнее, чем понимают многие программисты.

глиф
источник
3
«é» не обязательно представляет одну кодовую точку. Это могут быть две кодовые точки (U + 0065 + U + 0301).
JFS
2
Каждый абстрактный символ всегда представлен одной кодовой точкой. Однако кодовые точки могут быть закодированы в несколько байтов, в зависимости от схемы кодирования. то есть 'é' - это два байта в UTF-8 и UTF-16 и четыре байта в UTF-32, но в каждом случае это все еще одна кодовая точка - U + 00E9.
Бен Бланк
5
@Ben Blank: U + 0065 и U + 0301 являются кодовыми точками , и они делают представления «E» , который может также быть представлен U + 00E9. Google "сочетает в себе острый акцент".
JFS
JF прав в объединении U + 0065 и U + 0301 для формирования «é», но это не обратимый функтино. Вы получите U + 00E9. Согласно википедии , эти составные кодовые точки полезны для обратной совместимости
Мартин Конечни
1
@teehoo - это обратимая функция в том смысле, что вы можете повторно нормализовать кодовую точку, представляющую составной символ, в последовательность кодовых точек, представляющих тот же составной символ. В Python вы можете сделать это следующим образом: unicodedata.normalize ('NFD', u '\ xe9').
Символ
10

Как насчет этого?

import string

def isAscii(s):
    for c in s:
        if c not in string.ascii_letters:
            return False
    return True
мия
источник
5
Это не удастся, если ваша строка содержит символы ASCII, которые не являются буквами. Для вас примеры кода, которые включают перевод строки, пробел, точку, запятую, подчеркивание и круглые скобки.
Флорида
9

Я нашел этот вопрос, пытаясь определить, как использовать / кодировать / декодировать строку, в кодировке которой я не был уверен (и как экранировать / преобразовать специальные символы в этой строке).

Мой первый шаг должен был проверить тип строки - я не знал, что смогу получить хорошие данные о ее форматировании из типа (ов). Этот ответ был очень полезным и добрался до сути моих проблем.

Если вы получаете грубый и настойчивый

UnicodeDecodeError: кодек «ascii» не может декодировать байт 0xc3 в позиции 263: порядковый номер не в диапазоне (128)

особенно, когда вы кодируете, убедитесь, что вы не пытаетесь unicode () строка, которая уже является Unicode - по какой-то ужасной причине вы получаете ошибки кодека ascii. (См. Также рецепт Python Kitchen и учебники по Python для лучшего понимания того, насколько это может быть ужасно.)

В конце концов я решил, что то, что я хотел сделать, это:

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

Также в отладке было полезно установить кодировку по умолчанию в моем файле на utf-8 (поместите это в начало вашего файла python):

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

Это позволяет вам тестировать специальные символы ('àéç'), не используя их экранированные символы Юникода (u '\ xe0 \ xe9 \ xe7').

>>> specials='àéç'
>>> specials.decode('latin-1').encode('ascii','xmlcharrefreplace')
'&#224;&#233;&#231;'
Макс П Маги
источник
4

Чтобы улучшить решение Александра из Python 2.6 (и в Python 3.x), вы можете использовать вспомогательный модуль curses.ascii и функцию curses.ascii.isascii () или другие: https://docs.python.org/2.6/ библиотека / curses.ascii.html

from curses import ascii

def isascii(s):
    return all(ascii.isascii(c) for c in s)
Сергей Невмержицкий
источник
2

Вы можете использовать библиотеку регулярных выражений, которая принимает стандартное определение Posix [[: ASCII:]].

Стив Мойер
источник
2

Sting ( str-type) в Python представляет собой серию байтов. Там нет никакого способа , чтобы говорить просто смотреть на строки этой серии байтов , представляют ли строку ASCII, строку в 8-битной кодировке , как ISO-8859-1 или строки , зашифрованную с UTF-8 или UTF-16 или что - то ,

Однако, если вы знаете используемую кодировку, то вы можете decodeпоместить str в строку Unicode, а затем использовать регулярное выражение (или цикл), чтобы проверить, содержит ли он символы вне диапазона, который вас беспокоит.

JacquesB
источник
1

Как и ответ @ RogerDahl, но более эффективно закорачивать, отрицая класс символов и используя поиск вместо 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

Я предполагаю, что регулярное выражение хорошо оптимизировано для этого.

варочные панели
источник
0
import re

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

Чтобы включить пустую строку ASCII, изменить +к *.

Роджер Даль
источник
-1

Чтобы ваш код от аварий, может быть , вы хотите использовать , 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
mvknowles
источник
4
-1 AARRGGHH это обрабатывает все символы с ord (c) в диапазоне (128, 256) как ASCII !!!
Джон Мачин
Не работает Попробуйте вызвать следующее: is_ascii(u'i am ascii'). Даже если буквы и пробелы определенно являются ASCII, это все равно возвращается, Falseпотому что мы заставили строку быть unicode.
jpmc26