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?

TIMEX
źródło
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.

Greg Hewgill
źródło
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)
Mikel
źródło
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)
ThinkBonobo
źródło
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.

Alex Dean
źródło
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.

Dave Burton
źródło
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,
madjardi
źródło
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.

Veedrac
źródło
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
jfl
źródło
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.

Seb
źródło
0

Aby uzyskać zgodność z py2 / py3, wystarczy użyć

import six if isinstance(obj, six.text_type)

Vishvajit Pathak
źródło
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)
duhaime
źródło