Bagaimana cara saya memeriksa apakah suatu string adalah unicode atau ascii?

271

Apa yang harus saya lakukan dengan Python untuk mengetahui pengkodean string mana yang dimiliki?

TIMEX
sumber
56
Unicode bukan penyandian.
ulidtko
Lebih penting lagi, mengapa Anda harus peduli?
Johnsyweb
@Johnsyweb Karena{UnicodeDecodeError} 'ascii' codec can't decode byte 0xc2
alex

Jawaban:

295

Dalam Python 3, semua string adalah urutan karakter Unicode. Ada bytestipe yang menyimpan byte mentah.

Dalam Python 2, sebuah string dapat berupa tipe stratau tipe unicode. Anda dapat mengetahui kode mana yang menggunakan sesuatu seperti ini:

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

Ini tidak membedakan "Unicode atau ASCII"; itu hanya membedakan tipe Python. String Unicode dapat terdiri dari karakter murni dalam rentang ASCII, dan bytestring dapat berisi ASCII, Unicode yang dikodekan, atau bahkan data non-tekstual.

Greg Hewgill
sumber
3
@ProsperousHeart: Anda mungkin menggunakan Python 3.
Greg Hewgill
124

Bagaimana cara mengetahui apakah suatu objek adalah string unicode atau byte

Anda bisa menggunakan typeatau isinstance.

Dengan Python 2:

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

Dalam Python 2, strhanya urutan byte. Python tidak tahu apa penyandiannya. The unicodejenis adalah cara yang lebih aman untuk menyimpan teks. Jika Anda ingin lebih memahami ini, saya sarankan http://farmdev.com/talks/unicode/ .

Dengan Python 3:

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

Dalam Python 3, strseperti Python 2 unicode, dan digunakan untuk menyimpan teks. Apa yang disebut strdengan Python 2 disebut bytesdengan Python 3.


Bagaimana cara mengetahui apakah sebuah byte string valid atau 8 atau ascii

Anda bisa menelepon decode. Jika itu memunculkan eksepsi UnicodeDecodeError, itu tidak valid.

>>> 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
sumber
Hanya untuk referensi orang lain - str.decode tidak tidak ada dalam python 3. Sepertinya Anda harus melakukan unicode(s, "ascii")sesuatu
Shadow
3
Maaf, maksud sayastr(s, "ascii")
Shadow
1
Ini tidak akurat untuk python 3
ProsperousHeart
2
@ProsperousHeart Diperbarui untuk mencakup Python 3. Dan untuk mencoba menjelaskan perbedaan antara bytestrings dan string unicode.
Mikel
44

Dalam python 3.x semua string adalah urutan karakter Unicode. dan melakukan isinstance check untuk str (yang berarti string unicode secara default) sudah cukup.

isinstance(x, str)

Sehubungan dengan python 2.x, Kebanyakan orang tampaknya menggunakan pernyataan if yang memiliki dua pemeriksaan. satu untuk str dan satu untuk unicode.

Jika Anda ingin memeriksa apakah Anda memiliki objek 'string-like' semua dengan satu pernyataan, Anda dapat melakukan hal berikut:

isinstance(x, basestring)
ThinkBonobo
sumber
Ini salah. Dalam Python 2.7 isinstance(u"x",basestring)kembali True.
PythonNut
11
@PythonNut: Saya yakin itulah intinya. Penggunaan isinstance (x, basestring) sudah cukup untuk menggantikan tes ganda yang berbeda di atas.
KQ.
5
Ini berguna dalam banyak kasus, tetapi jelas bukan yang dimaksud si penanya.
mhsmith
3
Ini adalah jawaban untuk pertanyaan itu. Semua yang lain salah memahami apa yang dikatakan OP dan memberikan jawaban umum tentang jenis pemeriksaan dengan Python.
fiatjaf
1
Tidak menjawab pertanyaan OP. Judul pertanyaan (sendiri) BISA diinterpretasikan sedemikian rupa sehingga jawaban ini benar. Namun, OP secara khusus mengatakan "mencari tahu mana" dalam deskripsi pertanyaan, dan jawaban ini tidak membahasnya.
MD004
31

Unicode bukan pengodean - mengutip Kumar McMillan:

Jika ASCII, UTF-8, dan string byte lainnya adalah "teks" ...

... maka Unicode adalah "text-ness";

itu adalah bentuk abstrak dari teks

Telah membaca tentang McMillan's Unicode In Python, bicara Sepenuhnya Demystified dari PyCon 2008, itu menjelaskan banyak hal lebih baik daripada sebagian besar jawaban terkait pada Stack Overflow.

Alex Dean
sumber
Slide-slide itu mungkin adalah pengantar terbaik untuk Unicode yang pernah saya temui
Jonny
23

Jika kebutuhan kode Anda agar kompatibel dengan kedua Python 2 dan Python 3, Anda tidak bisa langsung menggunakan hal-hal seperti isinstance(s,bytes)atau isinstance(s,unicode)tanpa membungkus mereka baik mencoba / kecuali atau tes versi python, karena bytestidak terdefinisi dengan Python 2 dan unicodetidak terdefinisi di Python 3 .

Ada beberapa solusi buruk. Yang sangat jelek adalah membandingkan nama tipe, bukan membandingkan tipe itu sendiri. Ini sebuah contoh:

# 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)

Solusi yang bisa dibilang sedikit kurang jelek adalah memeriksa nomor versi Python, misalnya:

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)

Keduanya unpythonic, dan sebagian besar waktu mungkin ada cara yang lebih baik.

Dave Burton
sumber
6
Cara yang lebih baik mungkin adalah dengan menggunakan six, dan uji terhadap six.binary_typedansix.text_type
Ian Clelland
1
Anda dapat menggunakan jenis .__ nama__ untuk menyelidiki nama jenis.
Paulo Freitas
Saya tidak begitu yakin kasus penggunaan untuk sedikit kode, kecuali ada kesalahan logika. Saya pikir harus ada "tidak" dalam kode python 2. Kalau tidak, Anda mengubah segalanya menjadi string unicode untuk Python 3 dan sebaliknya untuk Python 2!
oligofren
Ya, oligofren, itulah fungsinya. String internal standar adalah Unicode dalam Python 3 dan ASCII dalam Python 2. Jadi potongan kode mengkonversi teks ke tipe string internal standar (baik itu Unicode atau ASCII).
Dave Burton
12

menggunakan:

import six
if isinstance(obj, six.text_type)

di dalam enam perpustakaan direpresentasikan sebagai:

if PY3:
    string_types = str,
else:
    string_types = basestring,
madjardi
sumber
2
seharusnya begitu if isinstance(obj, six.text_type) . Tapi ya ini adalah jawaban yang benar.
karantan
Tidak menjawab pertanyaan OP. Judul pertanyaan (sendiri) BISA diinterpretasikan sedemikian rupa sehingga jawaban ini benar. Namun, OP secara khusus mengatakan "mencari tahu mana" dalam deskripsi pertanyaan, dan jawaban ini tidak membahasnya.
MD004
4

Perhatikan bahwa pada Python 3, tidak adil untuk mengatakan:

  • strs adalah UTFx untuk x (mis. UTF8)

  • strIni adalah Unicode

  • strs adalah koleksi karakter Unicode yang dipesan

strJenis Python adalah (biasanya) urutan titik kode Unicode, beberapa di antaranya memetakan karakter.


Bahkan di Python 3, tidak semudah menjawab pertanyaan ini seperti yang Anda bayangkan.

Cara yang jelas untuk menguji string yang kompatibel dengan ASCII adalah dengan mencoba penyandian:

"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)

Kesalahan membedakan kasus.

Di Python 3, bahkan ada beberapa string yang berisi poin kode Unicode yang tidak valid:

"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

Metode yang sama untuk membedakan mereka digunakan.

Veedrac
sumber
3

Ini dapat membantu orang lain, saya memulai pengujian untuk tipe string dari variabel s, tetapi untuk aplikasi saya, lebih masuk akal untuk mengembalikan s sebagai utf-8. Proses memanggil return_utf, kemudian tahu apa yang ia hadapi dan dapat menangani string dengan tepat. Kode ini tidak asli, tetapi saya bermaksud untuk menjadi agnostik versi Python tanpa tes versi atau mengimpor enam. Berikan komentar dengan penyempurnaan kode contoh di bawah ini untuk membantu orang lain.

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
sumber
Anda, teman saya, layak mendapat jawaban yang benar! Saya menggunakan python 3 dan saya masih mengalami masalah sampai saya menemukan harta ini!
mnsr
2

Anda dapat menggunakan Universal Encoding Detector , tetapi ketahuilah bahwa itu hanya akan memberi Anda tebakan terbaik, bukan pengkodean yang sebenarnya, karena tidak mungkin untuk mengetahui pengkodean string "abc" misalnya. Anda perlu mendapatkan informasi penyandian di tempat lain, mis. Protokol HTTP menggunakan header Tipe-Konten untuk itu.

Seb
sumber
0

Untuk kompatibilitas py2 / py3 cukup gunakan

import six if isinstance(obj, six.text_type)

Vishvajit Pathak
sumber
0

Salah satu pendekatan sederhana adalah untuk memeriksa apakah unicodefungsi builtin. Jika demikian, Anda menggunakan Python 2 dan string Anda akan menjadi string. Untuk memastikan semuanya dalam unicodesatu dapat dilakukan:

import builtins

i = 'cats'
if 'unicode' in dir(builtins):     # True in python 2, False in 3
  i = unicode(i)
duhaime
sumber