Jak sprawdzić, czy ciąg znaków jest Unicode, czy ASCI?


271

Co muszę zrobić w Pythonie, aby dowiedzieć się, które kodowanie ma łańcuch?


56
Unicode nie jest kodowaniem.
ulidtko

Co ważniejsze, dlaczego miałbyś się przejmować?
Johnsyweb

@Johnsyweb powodu{UnicodeDecodeError} 'ascii' codec can't decode byte 0xc2
Alex

Odpowiedzi:


295

W Pythonie 3 wszystkie ciągi są ciągami znaków Unicode. Istnieje bytestyp przechowujący nieprzetworzone bajty.

W Pythonie 2 łańcuch może być typu strlub typu unicode. Możesz stwierdzić, który kod używa czegoś takiego:

def whatisthis(s):
    if isinstance(s, str):
        print "ordinary string"
    elif isinstance(s, unicode):
        print "unicode string"
    else:
        print "not a string"

Nie rozróżnia to „Unicode lub ASCII”; rozróżnia tylko typy Pythona. Łańcuch Unicode może składać się wyłącznie z znaków w zakresie ASCII, a bajtowanie może zawierać ASCII, kodowany Unicode, a nawet dane nietekstowe.


3
@ProsperousHeart: Prawdopodobnie używasz Pythona 3.
Greg Hewgill 10.04.19

124

Jak stwierdzić, czy obiekt jest łańcuchem Unicode czy ciągiem bajtów

Możesz użyć typelub isinstance.

W Python 2:

>>> type(u'abc')  # Python 2 unicode string literal
<type 'unicode'>
>>> type('abc')   # Python 2 byte string literal
<type 'str'>

W Python 2 strjest tylko sekwencją bajtów. Python nie wie, jakie jest jego kodowanie. Ten unicodetyp jest bezpieczniejszym sposobem przechowywania tekstu. Jeśli chcesz to lepiej zrozumieć, polecam http://farmdev.com/talks/unicode/ .

W Pythonie 3:

>>> type('abc')   # Python 3 unicode string literal
<class 'str'>
>>> type(b'abc')  # Python 3 byte string literal
<class 'bytes'>

W Pythonie 3 strjest podobny do Pythona 2 unicodei służy do przechowywania tekstu. To, co nazywa się strw Pythonie 2, nazywa się bytesw Pythonie 3.


Jak stwierdzić, czy łańcuch bajtów jest poprawny utf-8 czy ascii

Można zadzwonić decode. Jeśli zgłosi wyjątek UnicodeDecodeError, nie jest prawidłowy.

>>> u_umlaut = b'\xc3\x9c'   # UTF-8 representation of the letter 'Ü'
>>> u_umlaut.decode('utf-8')
u'\xdc'
>>> u_umlaut.decode('ascii')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0: ordinal not in range(128)

Tylko dla odniesienia innych osób - str.decode nie istnieje w Pythonie 3. Wygląda na to, że musisz unicode(s, "ascii")czy coś
Shadow

3
Przepraszam, miałem na myślistr(s, "ascii")
Shadow

1
To nie jest dokładne dla Pythona 3
ProsperousHeart

2
@ProsperousHeart Zaktualizowano, aby objąć Python 3. I spróbować wyjaśnić różnicę między bajtowaniem i ciągami Unicode.
Mikel

44

W Pythonie 3.x wszystkie ciągi są ciągami znaków Unicode. i wykonanie sprawdzania isinstance dla str (co domyślnie oznacza ciąg Unicode) powinno wystarczyć.

isinstance(x, str)

Jeśli chodzi o Python 2.x, większość osób wydaje się używać instrukcji if, która ma dwa testy. jeden dla str i jeden dla unicode.

Jeśli chcesz sprawdzić, czy masz obiekt „podobny do łańcucha” za pomocą jednej instrukcji, możesz wykonać następujące czynności:

isinstance(x, basestring)

To nieprawda. W Python 2.7 isinstance(u"x",basestring)zwraca True.
PythonNut

11
@PythonNut: Myślę, że o to właśnie chodziło. Zastosowanie isinstance (x, basestring) wystarcza, aby zastąpić odrębne podwójne testy powyżej.
KQ.

5
Jest to przydatne w wielu przypadkach, ale najwyraźniej nie to, co miał na myśli pytający.
mhsmith,

3
To jest odpowiedź na pytanie. Wszyscy inni źle zrozumieli słowa OP i udzielili ogólnych odpowiedzi na temat sprawdzania typów w Pythonie.
fiatjaf

1
Nie odpowiada na pytanie OP. Tytuł pytania (sam) MUSI być interpretowany w taki sposób, aby odpowiedź była poprawna. Jednak OP wyraźnie mówi „dowiedzieć się, które” w opisie pytania, a ta odpowiedź nie rozwiązuje tego.
MD004

31

Unicode nie jest kodowaniem - cytując Kumara McMillana:

Jeśli ASCII, UTF-8 i inne ciągi bajtów są „tekstem” ...

... wtedy Unicode to „tekstowość”;

jest to abstrakcyjna forma tekstu

Przeczytaj Unicode McMillana W Pythonie, całkowicie zdemistalizowanym wykładzie z PyCon 2008, wyjaśnia on rzeczy znacznie lepiej niż większość powiązanych odpowiedzi na temat przepełnienia stosu.


Te slajdy są prawdopodobnie najlepszym wprowadzeniem do Unicode, z jakim się spotkałem
Jonny

23

Jeśli twój kod musi być kompatybilny zarówno z Python 2, jak i Python 3, nie możesz bezpośrednio używać rzeczy takich jak isinstance(s,bytes)lub isinstance(s,unicode)bez owijania ich w try / try lub w testach wersji Python, ponieważ bytesjest niezdefiniowany w Pythonie 2 i unicodeniezdefiniowany w Pythonie 3 .

Istnieje kilka brzydkich obejść. Niezwykle brzydkie jest porównywanie nazwy typu zamiast porównywania samego typu. Oto przykład:

# convert bytes (python 3) or unicode (python 2) to str
if str(type(s)) == "<class 'bytes'>":
    # only possible in Python 3
    s = s.decode('ascii')  # or  s = str(s)[2:-1]
elif str(type(s)) == "<type 'unicode'>":
    # only possible in Python 2
    s = str(s)

Prawdopodobnie nieco mniej brzydkim obejściem jest sprawdzenie numeru wersji Pythona, np .:

if sys.version_info >= (3,0,0):
    # for Python 3
    if isinstance(s, bytes):
        s = s.decode('ascii')  # or  s = str(s)[2:-1]
else:
    # for Python 2
    if isinstance(s, unicode):
        s = str(s)

Oba są niepytoniczne i przez większość czasu jest prawdopodobnie lepszy sposób.


6
Lepszym sposobem jest prawdopodobnie do użytku six, a testy przed six.binary_typeisix.text_type
Ian Clelland

1
Możesz użyć type (s) .__ name__ do sondowania nazw typów.
Paulo Freitas

Nie jestem całkowicie pewien przypadku użycia tego fragmentu kodu, chyba że wystąpił błąd logiczny. Myślę, że w kodzie python 2 powinno być „nie”. W przeciwnym razie konwertujesz wszystko na ciągi Unicode dla Python 3 i odwrotnie dla Python 2!
oligofren

Tak, oligofren, właśnie to robi. Standardowymi ciągami wewnętrznymi są Unicode w Pythonie 3 i ASCII w Pythonie 2. Więc fragmenty kodu konwertują tekst na standardowy wewnętrzny ciąg znaków (czy to Unicode, czy ASCII).
Dave Burton

12

posługiwać się:

import six
if isinstance(obj, six.text_type)

wewnątrz sześciu bibliotek jest reprezentowany jako:

if PY3:
    string_types = str,
else:
    string_types = basestring,

2
tak powinno być if isinstance(obj, six.text_type) . Ale tak, to imo poprawna odpowiedź.
karantan

Nie odpowiada na pytanie OP. Tytuł pytania (sam) MUSI być interpretowany w taki sposób, aby odpowiedź była poprawna. Jednak OP wyraźnie mówi „dowiedzieć się, które” w opisie pytania, a ta odpowiedź nie rozwiązuje tego.
MD004

4

Zauważ, że w Pythonie 3, niesprawiedliwe jest powiedzenie któregokolwiek z:

  • strs są UTFx dla dowolnego x (np. UTF8)

  • strs są Unicode

  • strs to uporządkowane kolekcje znaków Unicode

Python's strTyp jest (normalnie) sekwencją punktów kodu Unicode, z których niektóre są mapowane na znaki.


Nawet w Pythonie 3 odpowiedź na to pytanie nie jest tak prosta, jak można sobie wyobrazić.

Oczywistym sposobem przetestowania ciągów zgodnych z ASCII jest próba kodowania:

"Hello there!".encode("ascii")
#>>> b'Hello there!'

"Hello there... ☃!".encode("ascii")
#>>> Traceback (most recent call last):
#>>>   File "", line 4, in <module>
#>>> UnicodeEncodeError: 'ascii' codec can't encode character '\u2603' in position 15: ordinal not in range(128)

Błąd rozróżnia przypadki.

W Pythonie 3 są nawet niektóre ciągi znaków, które zawierają nieprawidłowe punkty kodu Unicode:

"Hello there!".encode("utf8")
#>>> b'Hello there!'

"\udcc3".encode("utf8")
#>>> Traceback (most recent call last):
#>>>   File "", line 19, in <module>
#>>> UnicodeEncodeError: 'utf-8' codec can't encode character '\udcc3' in position 0: surrogates not allowed

Zastosowano tę samą metodę ich rozróżnienia.


3

To może pomóc komuś innemu. Zacząłem testować typ ciągu zmiennej s, ale dla mojej aplikacji bardziej sensowne było po prostu zwrócenie s jako utf-8. Proces wywołujący return_utf, a następnie wie, z czym ma do czynienia i odpowiednio obsługuje ciąg. Kod nie jest nieskazitelny, ale zamierzam, aby był on niezależny od wersji Pythona bez testu wersji lub importowania sześciu. Skomentuj ulepszenia przykładowego kodu poniżej, aby pomóc innym osobom.

def return_utf(s):
    if isinstance(s, str):
        return s.encode('utf-8')
    if isinstance(s, (int, float, complex)):
        return str(s).encode('utf-8')
    try:
        return s.encode('utf-8')
    except TypeError:
        try:
            return str(s).encode('utf-8')
        except AttributeError:
            return s
    except AttributeError:
        return s
    return s # assume it was already utf-8

Mój przyjacielu zasługujesz na poprawną odpowiedź! Używam Pythona 3 i wciąż miałem problemy, dopóki nie znalazłem tego skarbu!
mnsr

2

Możesz użyć Universal Encoding Detector , ale pamiętaj, że pozwoli ci to odgadnąć, a nie rzeczywiste kodowanie, ponieważ nie można na przykład znać kodowania ciągu „abc”. Będziesz musiał uzyskać informacje o kodowaniu gdzie indziej, np. Protokół HTTP używa do tego nagłówka Content-Type.



0

Jednym prostym podejściem jest sprawdzenie, czy unicodejest to funkcja wbudowana. Jeśli tak, jesteś w Pythonie 2, a Twój ciąg znaków będzie ciągiem. Aby upewnić się, że wszystko jest w unicodeporządku, możesz:

import builtins

i = 'cats'
if 'unicode' in dir(builtins):     # True in python 2, False in 3
  i = unicode(i)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.