Як перевірити, чи є рядок в Python в ASCII?


211

Я хочу перевірити, чи є рядок в ASCII чи ні.

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

Чи є інший спосіб перевірити?


Кодування рядків різниться між Python 2 та Python 3, тому було б добре знати, на яку версію ви орієнтуєтесь.
florisla

Відповіді:


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
Він неефективний порівняно з спробою / за винятком. Тут цикл знаходиться в інтерпретаторі. З формою спробу / крім, цикл знаходиться у реалізації кодека C, який називається str.decode ('ascii'). І я погоджуюсь, форма спробувати / за винятком форм теж є більш пітонічною.
ддаа

25
@JohnMachin ord(c) < 128нескінченно більш читабельний та інтуїтивний, ніжc <= "\x7F"
Slater Victoroff

252

Я думаю, ви не ставите правильного питання ...

Рядок у 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 . А strв будь-якому кодуванні ISO потрібно спочатку кодувати до Unicode. Відповідь має йти в цьому.
alexis

@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 спосіб:

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 буде істинною його розміру після кодування в байти; тоді як інші символи, що не відносяться до коду, будуть закодовані відповідно до 2 байт або 3 байтів, що збільшить їх розміри.
Devy

За @far найкраща відповідь, але не те, що деякі символи, як… і - можуть виглядати як ascii, тому у випадку, якщо ви хочете використовувати це для виявлення англійського тексту, змушуйте замінити такі символи перед перевіркою
Крістоф Руссі

1
Але в Python2 він кине UnicodeEncodeError. Треба знайти рішення і для Py2, і для Py3
alvas

2
Для тих, хто незнайомий з використанням лямбда (як я був, коли я вперше натрапив на цю відповідь) isasciiтепер функція, яку ви isascii('somestring')Trueisascii('àéç')False
передаєте

8
Це просто марнотратно. Він кодує рядок в UTF-8, створюючи цілий інший бітестрінг. Дійсний спосіб Python 3 є try: s.encode('ascii'); return True except UnicodeEncodeError: return False(Як і вище, але кодування, оскільки рядки є Unicode в Python 3). Ця відповідь також викликає помилку в Python 3, коли у вас є сурогати (наприклад, isascii('\uD800')виникає помилка замість повернення False)
Artyer

71

Нове в Python 3.7 ( bpo32677 )

Більше не стомлююча / неефективна перевірка ascii на струнах, новий вбудований str/ bytes/ bytearrayметод - .isascii()перевірять, чи є рядки ascii.

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

Цей заслуговує бути на вершині!
Салек

"\x03".isascii()також вірно. У документації сказано, що це просто перевіряє, чи всі символи знаходяться нижче кодової точки 128 (0-127). Якщо ви хочете , щоб уникнути керуючих символів, вам потрібно: text.isascii() and text.isprintable(). Просто використання isprintableсаме по собі також недостатньо, оскільки він вважатиме, що символ типу like є (правильно) для друку, але це не в розділі для друку 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')

7
Звичайно, для цього потрібна бібліотека шардетів .
StackExchange saddens dancek

1
так, хоча шардет доступний за замовчуванням у більшості установок
Елвін

7
chardet лише здогадується кодування з певною вірогідністю, як це: {'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.


Вхід OP - це тестування ( bytesвведіть у Python 3, що не має .encode()методу). .decode()у @Vincent Marchetti відповідь правильна .
jfs

@JFSebastian ОП запитує "Як перевірити, чи є рядок в Python в ASCII?" і не вказує байти проти рядків unicode. Чому ти вважаєш, що його / її внесок є пробіром?
пр.

1
Подивіться на дату питання: 'é'в той час був бійстеринг.
jfs

1
@JFSebastian, добре, враховуючи цю відповідь, відповідає на це запитання так, ніби його сьогодні задавали, я думаю, що це все-таки справедливо і корисно. Все менше і менше людей прийдуть сюди, шукаючи відповіді, ніби вони керували Python у 2008 році
drs

2
Я знайшов це питання, коли я шукав рішення для python3, і швидко читаючи це питання не змусило мене підозрювати, що це python 2. Але ця відповідь була справді корисною - наголос!
Джош

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' .

Більшу частину часу вам не доведеться дбати про це, але це може стати проблемою, якщо ви перебираєте рядок Unicode, оскільки ітерація працює за кодовою точкою, а не за символом, який можна розкласти. Іншими словами, 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 є кодовими точками, вони означають 'é', який також може бути представлений U + 00E9. Google "поєднує гострий акцент".
jfs

JF вірно поєднує U + 0065 і U + 0301, щоб утворити "é", але це не є зворотним функтино. Ви отримаєте U + 00E9. Згідно з wikipedia , ці складові кодові точки корисні для зворотної сумісності
Martin Konecny

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, які не є літерами. Для вас приклади коду, що включає нову лінію, пробіл, крапку, кома, підкреслення та круглі дужки.
florisla

9

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

Першим моїм кроком повинна була перевірити тип рядка - я не розумів, що я можу отримати хороші дані про його форматування від типів (типів). Ця відповідь була дуже корисною і дістала справжній корінь моїх питань.

Якщо ти стаєш грубим і наполегливим

UnicodeDecodeError: кодек "ascii" не може декодувати байт 0xc3 в положенні 263: порядковий не знаходиться в діапазоні (128)

особливо коли ви кодуєте, переконайтеся, що ви не намагаєтесь unicode () рядок, яка вже є unicode - з якоїсь жахливої ​​причини ви отримуєте помилки кодека ascii. (Див. Також рецепт кухні Python , і документи 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-тип) у Python - це ряд байтів. Немає способу сказати лише з погляду на рядок, чи представляє ця серія байтів рядок Ascii, рядок у 8-бітовій діаграмі типу ISO-8859-1 або рядок, закодований з UTF-8 або UTF-16 чи будь-яким іншим .

Однак якщо ви знаєте використовуване кодування, ви можете decodeввести рядок у рядок unicode, а потім скористатися регулярним виразом (або циклом), щоб перевірити, чи містить символи поза діапазону, який вас хвилює.


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

Щоб уникнути збоїв у вашому коді, можливо, ви хочете використовувати a try-exceptcatchTypeErrors

>>> 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він продовжить працювати.
Ри-

-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 це трактує всі символи з порядком (c) в діапазоні (128, 256) як ASCII !!!
Джон Махін

Не працює. Спробуйте викликати наступне: is_ascii(u'i am ascii'). Незважаючи на те, що букви та пробіли, безумовно, є ASCII, це все одно повертається, Falseтому що ми змусили рядок бути unicode.
jpmc26
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.