Bagaimana cara memeriksa apakah string dengan Python ada di ASCII?

212

Saya ingin memeriksa apakah string ada di ASCII atau tidak.

Saya sadar ord(), namun ketika saya mencoba ord('é'), saya punya TypeError: ord() expected a character, but string of length 2 found. Saya mengerti ini disebabkan oleh cara saya membangun Python (seperti yang dijelaskan dalam ord()dokumentasi ).

Apakah ada cara lain untuk memeriksanya?

Nico
sumber
Pengkodean string sedikit berbeda antara Python 2 dan Python 3, jadi sebaiknya Anda tahu versi mana yang Anda targetkan.
florisla

Jawaban:

188
def is_ascii(s):
    return all(ord(c) < 128 for c in s)
Alexander Kojevnikov
sumber
95
Tidak efisien tanpa tujuan. Jauh lebih baik untuk mencoba s.decode ('ascii') dan menangkap UnicodeDecodeError, seperti yang disarankan oleh Vincent Marchetti.
ddaa
20
Itu tidak efisien. all () akan mengalami hubungan pendek dan mengembalikan False segera setelah menemukan byte yang tidak valid.
John Millikin
10
Tidak efisien atau tidak, metode yang lebih pythonic adalah coba / kecuali.
Jeremy Cantrell
43
Itu tidak efisien dibandingkan dengan coba / kecuali. Di sini loop berada di interpreter. Dengan mencoba / kecuali formulir, loop dalam implementasi C codec disebut oleh str.decode ('ascii'). Dan saya setuju, coba / kecuali bentuk lebih pythonic juga.
ddaa
25
@JohnMachin ord(c) < 128jauh lebih mudah dibaca dan intuitif daripadac <= "\x7F"
Slater Victoroff
253

Saya pikir Anda tidak menanyakan pertanyaan yang tepat--

Sebuah string dalam python tidak memiliki properti yang sesuai dengan 'ascii', utf-8, atau penyandian lainnya. Sumber string Anda (apakah Anda membacanya dari file, input dari keyboard, dll.) Mungkin telah menyandikan string unicode di ascii untuk menghasilkan string Anda, tetapi di situlah Anda harus mencari jawaban.

Mungkin pertanyaan yang dapat Anda tanyakan adalah: "Apakah string ini hasil dari pengkodean string unicode di ascii?" - Ini bisa Anda jawab dengan mencoba:

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"
Vincent Marchetti
sumber
28
menggunakan encode lebih baik, karena string tanpa metode decode di python 3, lihat apa perbedaan antara encode / decode? (python 2.x)
Jet Guo
@ Sri: Itu karena Anda menggunakannya pada string yang tidak ter-enkripsi ( strdalam Python 2, bytesdalam Python 3).
dotancohen
Dalam Python 2, solusi ini hanya berfungsi untuk string unicode . A strdalam pengkodean ISO apa pun harus dikodekan ke Unicode terlebih dahulu. Jawabannya harus masuk ke ini.
alexis
@ JetGuo: Anda harus menggunakan keduanya tergantung pada tipe input: s.decode('ascii') if isinstance(s, bytes) else s.encode('ascii')di Python 3. Input OP adalah bytestring 'é'(sintaks Python 2, Python 3 belum dirilis pada saat itu) dan oleh karena itu .decode()sudah benar.
jfs
2
@alexis: salah. strpada Python 2 adalah bytestring. Itu benar untuk digunakan .decode('ascii')untuk mengetahui apakah semua byte berada dalam kisaran ascii.
jfs
153

Python 3 cara:

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

Untuk memeriksa, lewati string uji:

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

print(isascii(str1)) -> will return False
print(isascii(str2)) -> will return True
jauh
sumber
7
Ini adalah trik kecil yang bagus untuk mendeteksi karakter non-ascii dalam string Unicode, yang dalam python3 hampir semua string. Karena karakter ascii dapat dikodekan hanya menggunakan 1 byte, maka setiap panjang karakter ascii akan sesuai dengan ukurannya setelah dikodekan ke byte; sedangkan karakter non-ascii lainnya akan dikodekan menjadi 2 byte atau 3 byte yang sesuai yang akan menambah ukurannya.
Devy
Dengan @far jawaban terbaik, tetapi bukankah beberapa karakter seperti ... dan - mungkin terlihat seperti ascii, jadi jika Anda ingin menggunakan ini untuk mendeteksi teks bahasa Inggris, Anda mengganti karakter seperti itu sebelum memeriksa
Christophe Roussy
1
Tetapi dalam Python2 itu akan membuang UnicodeEncodeError. Harus menemukan solusi untuk Py2 dan Py3
alvas
2
Bagi mereka yang tidak terbiasa menggunakan lambda (seperti saya ketika saya pertama kali menemukan jawaban ini) isasciisekarang adalah fungsi yang Anda berikan string: isascii('somestring')== Truedanisascii('àéç') ==False
rabidang3ls
8
Ini sia-sia. Ini mengkodekan sebuah string dalam UTF-8, membuat bytestring lainnya. Cara True Python 3 adalah try: s.encode('ascii'); return True except UnicodeEncodeError: return False(Seperti di atas, tetapi penyandian, sebagai string adalah Unicode di Python 3). Jawaban ini juga menimbulkan kesalahan dalam Python 3 ketika Anda memiliki pengganti (misalnya isascii('\uD800')meningkatkan kesalahan alih-alih kembali False)
Artyer
73

Baru dalam Python 3.7 ( bpo32677 )

Tidak ada lagi ascii yang melelahkan / tidak efisien pada string, built-in str/ bytes/ bytearraymetode baru - .isascii()akan memeriksa apakah string ascii.

print("is this ascii?".isascii())
# True
abccd
sumber
Yang ini pantas berada di atas!
Salek
"\x03".isascii()Itu juga benar. Dokumentasi mengatakan ini hanya memeriksa bahwa semua karakter di bawah titik kode 128 (0-127). Jika Anda juga ingin menghindari karakter kontrol, Anda akan perlu: text.isascii() and text.isprintable(). Hanya menggunakan isprintableitu sendiri juga tidak cukup, karena itu akan mempertimbangkan karakter seperti ¿agar (benar) dapat dicetak, tetapi itu tidak termasuk dalam bagian ascii yang dapat dicetak, jadi Anda perlu memeriksa keduanya jika Anda menginginkan keduanya. Satu lagi gotcha: spasi dianggap dapat dicetak, tab dan baris baru tidak.
Luc
19

Menabrak sesuatu seperti ini baru-baru ini - untuk referensi di masa mendatang

import chardet

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

yang dapat Anda gunakan dengan:

string_ascii = string.decode(encoding['encoding']).encode('ascii')
Alvin
sumber
7
Tentu saja, ini membutuhkan perpustakaan chardet .
StackExchange saddens dancek
1
ya, meskipun chardet tersedia secara default di sebagian besar instalasi
Alvin
7
chardet hanya menebak pengodean dengan probabilitas tertentu seperti ini: {'confidence': 0.99, 'encoding': 'EUC-JP'}(yang dalam hal ini benar-benar salah)
Suzana
19

Vincent Marchetti memiliki ide yang tepat, tetapi str.decodetelah ditinggalkan dalam Python 3. Dalam Python 3 Anda dapat melakukan tes yang sama dengan str.encode:

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

Perhatikan pengecualian yang ingin Anda tangkap juga telah berubah dari UnicodeDecodeErrormenjadi UnicodeEncodeError.

drs
sumber
Input OP adalah bytestring ( bytesketik Python 3 yang tidak memiliki .encode()metode). .decode()dalam jawaban @Vincent Marchetti benar .
jfs
@ JFSebastian OP bertanya "Bagaimana cara memeriksa apakah string dengan Python di ASCII?" dan tidak menentukan byte vs string unicode. Mengapa Anda mengatakan bahwa inputnya adalah uji coba?
drs
1
lihatlah tanggal dari pertanyaan: 'é'apakah bytestring saat itu.
jfs
1
@ JSFSebastian, ok, mengingat jawaban ini menjawab pertanyaan ini seolah ditanyakan hari ini, saya pikir itu masih valid dan bermanfaat. Lebih sedikit dan lebih sedikit orang akan datang ke sini mencari jawaban seolah-olah mereka menjalankan Python pada 2008
drs
2
Saya menemukan pertanyaan ini ketika saya sedang mencari solusi untuk python3 dan cepat membaca pertanyaan tidak membuat saya curiga bahwa ini adalah python 2 specfic. Tetapi jawaban ini sangat membantu - upvoting!
josch
17

Pertanyaan Anda salah; kesalahan yang Anda lihat bukan hasil dari bagaimana Anda membangun python, tetapi dari kebingungan antara string byte dan string unicode.

String byte (misalnya "foo", atau 'bar', dalam sintaks python) adalah urutan oktet; angka dari 0-255. String Unicode (mis. U "foo" atau u'bar ') adalah urutan titik kode unicode; angka dari 0-1112064. Tetapi Anda tampaknya tertarik pada karakter é, yang (di terminal Anda) adalah urutan multi-byte yang mewakili satu karakter.

Alih-alih ord(u'é'), coba ini:

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

Itu memberitahu Anda urutan titik kode mana yang mewakili "é". Mungkin memberi Anda [233], atau mungkin memberi Anda [101, 770].

Alih-alih chr()membalikkan ini, ada unichr():

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

Karakter ini sebenarnya dapat direpresentasikan sebagai satu atau beberapa "kode titik" unicode, yang dengan sendirinya mewakili grapheme atau karakter. Entah "e dengan aksen akut (yaitu, titik kode 233)", atau "e" (titik kode 101), diikuti oleh "aksen akut pada karakter sebelumnya" (titik kode 770). Jadi karakter yang sama persis ini dapat disajikan sebagai struktur data Python u'e\u0301'atauu'\u00e9' .

Sebagian besar waktu Anda tidak perlu peduli tentang ini, tetapi itu bisa menjadi masalah jika Anda mengulangi string unicode, karena iterasi bekerja dengan titik kode, bukan oleh karakter yang dapat diurai. Dengan kata lain, len(u'e\u0301') == 2dan len(u'\u00e9') == 1. Jika ini penting bagi Anda, Anda dapat mengonversi antara formulir yang dikomposisi dan diuraikan dengan menggunakan unicodedata.normalize.

Unicode Glosarium dapat menjadi panduan bermanfaat untuk memahami beberapa masalah ini, dengan menunjukkan bagaimana masing-masing istilah spesifik merujuk pada bagian yang berbeda dari representasi teks, yang jauh lebih rumit daripada yang disadari oleh banyak programmer.

Mesin terbang
sumber
3
'é' tidak tidak selalu mewakili titik kode tunggal. Itu bisa dua poin kode (U + 0065 + U + 0301).
jfs
2
Setiap karakter abstrak selalu diwakili oleh satu titik kode. Namun, titik kode dapat dikodekan ke beberapa byte, tergantung pada skema pengkodean. yaitu, 'é' adalah dua byte di UTF-8 dan UTF-16, dan empat byte di UTF-32, tetapi dalam setiap kasus masih satu titik kode tunggal - U + 00E9.
Ben Blank
5
@ Ben Kosong: U + 0065 dan U + 0301 adalah kode poin dan mereka melakukan mewakili 'é' yang bisa juga diwakili oleh U + 00E9. Google "menggabungkan aksen akut".
jfs
JF benar dalam menggabungkan U + 0065 dan U + 0301 untuk membentuk 'é' tetapi ini bukan fungsi yang dapat dibalik. Anda akan mendapatkan U + 00E9. Menurut wikipedia , titik kode komposit ini berguna untuk kompatibilitas mundur
Martin Konecny
1
@teehoo - Ini adalah fungsi yang dapat dibalik dalam arti bahwa Anda dapat menormalkan kembali titik kode yang mewakili karakter yang dikomposisikan menjadi urutan poin kode yang mewakili karakter yang dikomposisikan sama. Dengan Python Anda dapat melakukan ini seperti ini: unicodedata.normalize ('NFD', u '\ xe9').
Glyph
10

Bagaimana kalau melakukan ini?

import string

def isAscii(s):
    for c in s:
        if c not in string.ascii_letters:
            return False
    return True
miya
sumber
5
Ini gagal jika string Anda berisi karakter ASCII yang bukan huruf. Untuk Anda contoh kode, itu termasuk baris baru, spasi, titik, koma, garis bawah, dan tanda kurung.
florisla
9

Saya menemukan pertanyaan ini ketika mencoba menentukan bagaimana menggunakan / meng-encode / mendekodekan suatu string yang pengkodeannya tidak saya yakini (dan bagaimana cara melarikan diri / mengonversi karakter khusus dalam string itu).

Langkah pertama saya seharusnya memeriksa jenis string - saya tidak menyadari di sana saya bisa mendapatkan data yang baik tentang pemformatannya dari tipe (s). Jawaban ini sangat membantu dan sampai ke akar masalah saya yang sebenarnya.

Jika Anda bersikap kasar dan gigih

UnicodeDecodeError: 'ascii' codec tidak dapat mendekodekan byte 0xc3 di posisi 263: ordinal tidak dalam jangkauan (128)

terutama ketika Anda MENYESUAIKAN, pastikan Anda tidak mencoba untuk unicode () string yang sudah IS unicode- untuk beberapa alasan yang mengerikan, Anda mendapatkan kesalahan codec ascii. (Lihat juga resep Python Kitchen , dan Python docs tutorial untuk pemahaman yang lebih baik tentang betapa buruknya hal ini.)

Akhirnya saya memutuskan bahwa yang ingin saya lakukan adalah ini:

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

Juga membantu dalam debugging adalah menetapkan pengkodean default di file saya ke utf-8 (letakkan ini di awal file python Anda):

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

Itu memungkinkan Anda untuk menguji karakter khusus ('àéç') tanpa harus menggunakan kode unicode escapes (u '\ xe0 \ xe9 \ xe7').

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

Untuk meningkatkan solusi Alexander dari Python 2.6 (dan dalam Python 3.x) Anda dapat menggunakan modul helper.ascii dan gunakan fungsi curses.ascii.isascii () atau berbagai fungsi lainnya: 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)
Sergey Nevmerzhitsky
sumber
2

Anda bisa menggunakan pustaka ekspresi reguler yang menerima definisi standar Posix [[: ASCII:]].

Steve Moyer
sumber
2

Sengatan ( str-type) dalam Python adalah serangkaian byte. Tidak ada cara untuk mengatakan hanya dari melihat string apakah rangkaian byte ini mewakili string ascii, string dalam charset 8-bit seperti ISO-8859-1 atau string yang dikodekan dengan UTF-8 atau UTF-16 atau apa pun. .

Namun, jika Anda tahu penyandian yang digunakan, maka Anda dapat decodememasukkan str ke string unicode dan kemudian menggunakan ekspresi reguler (atau loop) untuk memeriksa apakah itu berisi karakter di luar rentang yang Anda khawatirkan.

JacquesB
sumber
1

Seperti jawaban @ RogerDahl tetapi lebih efisien untuk hubungan pendek dengan meniadakan kelas karakter dan menggunakan pencarian daripada find_allatau 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

Saya membayangkan ungkapan reguler dioptimalkan dengan baik untuk ini.

hobs
sumber
0
import re

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

Untuk memasukkan string kosong sebagai ASCII, ubah +ke *.

Roger Dahl
sumber
-1

Untuk mencegah kode Anda mogok, Anda mungkin ingin menggunakan try-exceptuntuk menangkapTypeErrors

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

Sebagai contoh

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

sumber
Ini trywrapper benar-benar sia-sia. Jika "¶"string Unicode, maka ord("¶")akan berfungsi, dan jika bukan (Python 2), for c in sakan menguraikannya menjadi byte sehingga ordakan terus bekerja.
Ry-
-5

Saya menggunakan berikut ini untuk menentukan apakah string ascii atau unicode:

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

Kemudian gunakan saja blok kondisional untuk mendefinisikan fungsi:

def is_ascii(input):
    if input.__class__.__name__ == "str":
        return True
    return False
saya tahu
sumber
4
-1 AARRGGHH ini memperlakukan semua karakter dengan ord (c) dalam kisaran (128, 256) sebagai ASCII !!!
John Machin
Tidak bekerja Mencoba menelepon berikut: is_ascii(u'i am ascii'). Meskipun huruf dan spasi jelas ASCII, ini masih kembali Falsekarena kami memaksakan string unicode.
jpmc26