Python'da bir dizenin ASCII'de olup olmadığını nasıl kontrol edebilirim?


211

Bir dize ASCII olup olmadığını kontrol etmek istiyorum.

Biliyorum ord(), ama denediğimde ord('é'), var TypeError: ord() expected a character, but string of length 2 found. Bunun Python'u oluşturma şeklimden kaynaklandığını anladım ( belgelerinde açıklandığı gibi)ord() ).

Kontrol etmenin başka bir yolu var mı?


Dize kodlaması Python 2 ve Python 3 arasında biraz farklıdır, bu nedenle hangi sürümü hedeflediğinizi bilmek iyi olur.
florisla

Yanıtlar:


188
def is_ascii(s):
    return all(ord(c) < 128 for c in s)

95
Anlamsızca verimsiz. Vincent Marchetti'nin önerdiği gibi, s.decode'u ('ascii') denemek ve UnicodeDecodeError'ı yakalamak çok daha iyi.
ddaa

20
Verimsiz değil. all () kısa devre yapar ve geçersiz bir baytla karşılaşır karşılaşmaz False değerini döndürür.
John Millikin

10
Verimsiz olsun veya olmasın, daha pitonik yöntem try / hariçtir.
Jeremy Cantrell

43
Deneme / hariç tutma işlemine göre verimsizdir. Burada döngü yorumlayıcıda. Try / hariç formuyla döngü, str.decode ('ascii') tarafından çağrılan C kodek uygulamasındadır. Katılıyorum, denemek / hariç form da daha pitonik.
ddaa

25
@JohnMachin ord(c) < 128sonsuzdan daha okunabilir ve sezgiselc <= "\x7F"
Slater Victoroff

253

Bence doğru soruyu sormuyorsun--

Python'daki bir dize, 'ascii', utf-8 veya başka bir kodlamaya karşılık gelen hiçbir özelliğe sahip değildir. Dizenizin kaynağı (ister bir dosyadan okuyun, ister klavyeden giriş yapın, vb.) Dizenizi üretmek için ascii'de bir unicode dizgiyi kodlamış olabilir, ancak yanıt için gitmeniz gereken yer burasıdır.

Belki de sorabileceğiniz soru şudur: "Bu dize, ascii'de bir unicode dizginin kodlanmasının sonucu mu?" - Bunu deneyerek cevaplayabilirsiniz:

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
kodlama kullanımı daha iyidir, çünkü python 3'te dize kod çözme yöntemi yoktur, kodlama / kod çözme arasındaki fark nedir? (python 2.x)
Jet Guo

@Sri: Bunun nedeni, kodu kodlanmamış bir dizede ( strPython 2'de, bytesPython 3'te) kullanmanızdır.
13:15

Python 2'de bu çözüm yalnızca bir unicode dize için çalışır . strHerhangi bir ISO kodlamasındaki A'nın önce Unicode'a kodlanması gerekir. Cevap buna girmeli.
alexis

@JetGuo: s.decode('ascii') if isinstance(s, bytes) else s.encode('ascii')Python 3'te giriş türüne bağlı olarak her ikisini de kullanmalısınız . OP'nin girişi bir testtir 'é'(Python 2 sözdizimi, Python 3 o sırada serbest bırakılmamıştı) ve bu nedenle .decode()doğrudur.
jfs

2
@alexis: yanlış. strPython 2 üzerinde bir bytestring olduğunu. .decode('ascii')Tüm baytların ascii aralığında olup olmadığını bulmak için kullanmak doğrudur .
jfs

153

Python 3 yolu:

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

Kontrol etmek için test dizesini iletin:

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

print(isascii(str1)) -> will return False
print(isascii(str2)) -> will return True

7
Bu, python3'te neredeyse tüm dizeler olan Unicode dizelerinde ascii olmayan karakterleri tespit etmek için güzel bir numara. Ascii karakterleri yalnızca 1 bayt kullanılarak kodlanabildiğinden, herhangi bir ascii karakter uzunluğu baytlara kodlandıktan sonra boyutuna göre doğru olacaktır; diğer ascii olmayan karakterler ise 2 bayta veya 3 bayta kodlanacak ve bu da boyutlarını artıracaktır.
Devy

@Far tarafından en iyi cevap, ama değil gibi bazı chars ... ve - ascii gibi görünebilir, bu yüzden İngilizce metni tespit etmek için kullanmak istiyorsanız, kontrol etmeden önce bu tür chars yerine yapmak
Christophe Roussy

1
Ancak Python2'de bir UnicodeEncodeError atar. Hem Py2 hem de Py3 için bir çözüm bulmalıyım
alvas

2
Lambda kullanmayı bilmeyenler için (bu cevaba ilk geldiğimde olduğu gibi) isasciişimdi bir dize isascii('somestring')Trueisascii('àéç')False
geçirdiğiniz bir işlevdir

8
Bu sadece israftır. UTF-8'de bir dizeyi kodlayarak başka bir bayt dizisi oluşturur. Gerçek Python 3 yolu try: s.encode('ascii'); return True except UnicodeEncodeError: return False(Yukarıdaki gibi, ancak kodlamalar, Python 3'te Unicode olduğu için kodlama). Bu yanıt ayrıca, vekilleriniz olduğunda Python 3'te bir hata oluşturur (örneğin isascii('\uD800'), geri dönmek yerine bir hata ortaya çıkarır False)
Artyer

73

Python 3.7'deki Yenilikler ( BPO32677 )

Artık yorucu / verimsiz ascii dizeleri kontrol etmez, yeni yerleşik str/ bytes/ bytearrayyöntemi - .isascii()dizelerin ascii olup olmadığını kontrol eder.

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

Bu zirvede olmayı hak ediyor!
Salek

"\x03".isascii()aynı zamanda doğrudur. Belgelerde bunun tüm karakterlerin 128 kod noktasının (0-127) altında olduğunu kontrol ettiğini söylüyor. Ayrıca kontrol karakterleri önlemek istiyorsanız, ihtiyacınız olacaktır: text.isascii() and text.isprintable(). Sadece isprintablekendi başına kullanmak da yeterli değildir, çünkü ¿gibi bir karakteri yazdırılabilir (doğru) yazdırır, ancak ascii yazdırılabilir bölümünde değildir, bu yüzden her ikisini de istiyorsanız her ikisini de kontrol etmeniz gerekir. Yine başka bir gotcha: boşluklar yazdırılabilir, sekmeler ve yeni satırlar kabul edilmez.
Luc

19

Son zamanlarda böyle bir şeyle karşılaştım - gelecekteki referans için

import chardet

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

hangi ile kullanabilirsiniz:

string_ascii = string.decode(encoding['encoding']).encode('ascii')

7
Tabii ki, bunu gerektirir Chardet kütüphanesi.
StackExchange saddens dancek

1
evet, çoğu kurulumda varsayılan olarak chardet kullanılabilir
Alvin

7
chardet sadece böyle bir olasılıkla kodlamayı tahmin eder: {'confidence': 0.99, 'encoding': 'EUC-JP'}(bu durumda tamamen yanlıştı)
Suzana

19

Vincent Marchetti'nin doğru fikri var, ancak str.decodePython 3'te kullanımdan kaldırıldı. Python 3'te aynı testi aşağıdakilerle yapabilirsiniz str.encode:

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

Ayrıca değiştiğini yakalama istediğiniz istisna Not UnicodeDecodeErroriçin UnicodeEncodeError.


OP'nin girdisi bir testtir ( yöntemi bytesolmayan Python 3'e yazın .encode()). .decode()@Vincent Marchetti'nin cevabı doğrudur .
jfs

@JFSebastian OP "Python'daki bir dizenin ASCII'de olup olmadığını nasıl kontrol edebilirim?" ve bayt ve unicode dizeleri belirtmez. Neden onun girdisinin bir bytestring olduğunu söylüyorsun?
drs

1
sorunun tarihine bakın: 'é'o zamanlar bir testti.
jfs

1
@JFSebastian, tamam, bu cevabı dikkate alarak bu soruyu bugün sorulmuş gibi cevaplarsak, hala geçerli ve yararlı olduğunu düşünüyorum. Daha az ve daha az kişi 2008'de Python çalıştırıyormuş gibi cevap aramaya gelecek
drs

2
Ben python3 için bir çözüm ararken ve hızlı bir şekilde soru okumak beni bu python 2 specfic şüpheli yapmadı zaman bu soru bulundu. Ama bu cevap gerçekten yardımcı oldu - upvoting!
josch

17

Sorunuz yanlış; gördüğünüz hata, python'u nasıl oluşturduğunuzun değil, bayt dizeleri ve unicode dizeleri arasındaki karışıklığın sonucudur.

Bayt dizeleri (örneğin, python sözdiziminde "foo" veya "bar") sekizli dizileridir; 0-255 arası sayılar. Unicode dizeleri (örn. U "foo" veya u'bar '), unicode kod noktalarının dizileridir; 0-1112064 arasındaki numaralar. Ancak, (terminalinizde) tek bir karakteri temsil eden çok baytlık bir sıra olan é karakteriyle ilgileniyorsunuz.

Bunun yerine şunu ord(u'é')deneyin:

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

Bu, hangi kod noktaları dizisinin "é" yi temsil ettiğini gösterir. Size verebilir [233] veya size verebilir [101, 770].

chr()Bunu tersine çevirmek yerine unichr():

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

Bu karakter aslında tek veya çoklu unicode "kod noktaları" olarak temsil edilebilir ve bunlar kendileri grafik veya karakterleri temsil eder. "Akut aksanlı (yani kod noktası 233)" veya "e" (kod noktası 101), ardından "önceki karakterde akut aksan" (kod noktası 770). Dolayısıyla bu aynı karakter Python veri yapısı u'e\u0301'veya u'\u00e9'.

Çoğu zaman bununla ilgilenmek zorunda kalmamalısınız, ancak unicode dize üzerinde yineleme yapıyorsanız, yineleme ayrıştırılabilir karakterle değil, kod noktasına göre çalışır. Başka bir deyişle, len(u'e\u0301') == 2ve len(u'\u00e9') == 1. Bu sizin için önemliyse, kullanarak oluşturulmuş ve ayrıştırılmış formlar arasında dönüştürme yapabilirsiniz unicodedata.normalize.

Unicode Sözlüğü , her bir özel terimin, metin gösteriminin birçok programcının fark ettiğinden çok daha karmaşık olan farklı bir bölümünü nasıl ifade ettiğini göstererek bu sorunların bazılarını anlamak için yararlı bir rehber olabilir.


3
'é' yok değil mutlaka tek bir kod noktasını temsil eder. Bu olabilir , iki kod noktaları (+ 0065 + U +, 0301 U).
jfs

2
Her soyut karakter her zaman tek bir kod noktasıyla temsil edilir. Bununla birlikte, kodlama noktaları, kodlama şemasına bağlı olarak birden çok bayta kodlanabilir. yani 'é' UTF-8 ve UTF-16'da iki bayt ve UTF-32'de dört bayttır, ancak her durumda hala tek bir kod noktasıdır - U + 00E9.
Ben Blank

5
@Ben Boş: u + 0065 U + 0301 olan kod noktaları ve bunu , 'E' temsil eder , aynı zamanda U +, 00E9 ile temsil edilebilir. Google "akut aksanı birleştiriyor".
jfs

JF, U + 0065 ve U + 0301'i 'é' oluşturmak üzere birleştirmek konusunda haklıdır, ancak bu geri dönüşümlü bir işlev değildir. U + 00E9 alacaksınız. Vikipedi göre , bu kompozit kod noktaları geriye dönük uyumluluk için kullanışlıdır
Martin Konecny

1
@teehoo - Oluşturulan karakteri temsil eden kod noktasını, aynı oluşturulan karakteri temsil eden bir kod noktası dizisine yeniden normalleştirebilmeniz anlamında tersine çevrilebilir bir işlevdir. Python'da bunu şu şekilde yapabilirsiniz: unicodedata.normalize ('NFD', u '\ xe9').
Glif

10

Bunu yapmaya ne dersin?

import string

def isAscii(s):
    for c in s:
        if c not in string.ascii_letters:
            return False
    return True

5
Dize, harf olmayan ASCII karakterleri içeriyorsa bu başarısız olur. Yeni satır, boşluk, nokta, virgül, alt çizgi ve parantez içeren örnek kodlar için.
florisla

9

Ben nasıl kodlama emin değildi bir dize / kod çözme / kod çözme (ve nasıl kaçış / bu dizede özel karakterler dönüştürmek) belirlemek çalışırken çalışırken bu soruyu buldum.

İlk adımım dize türünü kontrol etmek olmalıdır-orada biçimlendirme hakkında iyi veri alabilir tür (ler) farkında değildi. Bu cevap çok yardımcı oldu ve sorunlarımın gerçek köküne ulaştı.

Kaba ve ısrarcı biriyseniz

UnicodeDecodeError: 'ascii' codec bileşeni 263 konumundaki bayt 0xc3 kodunu çözemiyor: sıra değeri aralıkta değil (128)

özellikle ENCODING yaparken, zaten unicode IS olan bir dizeyi unicode () yapmaya çalışmadığınızdan emin olun - bazı korkunç nedenlerden dolayı ascii codec hatası alıyorsunuz. (Ayrıca bkz. Python Kitchen tarifi ve Python belgeleri ne kadar korkunç olabileceğini daha iyi anlamak için eğiticilerine bakın.)

Sonunda ne yapmak istediğimin şu olduğunu belirledim:

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

Ayrıca hata ayıklamada yararlı benim dosyamda varsayılan kodlama utf-8 (python dosyanızın başına koy):

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

Bu, özel karakterleri ('àéç') unicode çıkışlarını kullanmak zorunda kalmadan test etmenizi sağlar (u '\ xe0 \ xe9 \ xe7').

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

4

Alexander'ın çözümünü Python 2.6'dan (ve Python 3.x'te) geliştirmek için curses.ascii yardımcı modülünü kullanabilir ve curses.ascii.isascii () işlevini ya da diğerlerini kullanabilirsiniz: https://docs.python.org/2.6/ kütüphane / curses.ascii.html

from curses import ascii

def isascii(s):
    return all(ascii.isascii(c) for c in s)


2

Posix standart [[: ASCII:]] tanımını kabul eden normal ifade kitaplığını kullanabilirsiniz.


2

strPython'da bir acı ( -tipi) bir bayt serisidir. Orada hiçbir şekilde bayt bu dizi bir ascii dize, UTF-8 veya UTF-16 ya da her türlü ile kodlanmış ISO-8859-1 veya ip gibi bir 8-bit karakter kümesi bir dize temsil edip dize bakarak sadece anlatmanın .

Bununla birlikte, kullanılan kodlamayı biliyorsanız, decodestr'yi bir unicode dizgiye yerleştirebilir ve daha sonra endişe duyduğunuz aralığın dışında karakterler içerip içermediğini kontrol etmek için normal bir ifade (veya döngü) kullanabilirsiniz.


1

@ RogerDahl'ın cevabı gibi, ancak karakter sınıfını reddederek ve find_allveya yerine arama kullanarak kısa devre yapmak daha etkilidir 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

Bunun için düzenli bir ifadenin iyi optimize edildiğini düşünüyorum.


0
import re

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

ASCII olarak boş bir dize eklemek için değiştirmek +için *.


-1

Kodunuzun kilitlenmesini önlemek try-exceptiçin yakalamak için birTypeErrors

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

Örneğin

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

Bu tryambalaj tamamen anlamsız. Eğer "¶"bir Unicode dizesi, sonra ord("¶")çalışacak ve (Python 2) değilse, for c in sbu yüzden bayt içine ayrışır ordçalışmalarına devam edecektir.
Ry-

-5

Dize ascii veya unicode olup olmadığını belirlemek için aşağıdaki kullanın:

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

Ardından işlevi tanımlamak için koşullu bir blok kullanın:

def is_ascii(input):
    if input.__class__.__name__ == "str":
        return True
    return False

4
-1 AARRGGHH (128, 256) aralığında ord (c) olan tüm karakterlere ASCII olarak davranıyor !!!
John Machin

Çalışmıyor. Aşağıdakileri aramayı deneyin: is_ascii(u'i am ascii'). Harfler ve boşluklar kesinlikle ASCII olmasına rağmen, bu hala geri döner Falseçünkü ipi olmaya zorladık unicode.
jpmc26
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.