ฉันจะตรวจสอบว่าสตริงเป็น Unicode หรือ ascii ได้อย่างไร?


271

ฉันต้องทำอะไรใน Python เพื่อหาว่าการเข้ารหัสสตริงมีอะไรบ้าง


56
Unicode ไม่ใช่การเข้ารหัส
ulidtko

ที่สำคัญกว่านั้นทำไมคุณถึงต้องแคร์?
Johnsyweb

@Johnsyweb เพราะ{UnicodeDecodeError} 'ascii' codec can't decode byte 0xc2
alex

คำตอบ:


295

ใน Python 3 สตริงทั้งหมดเป็นลำดับของอักขระ Unicode มีbytesชนิดที่เก็บไบต์ดิบ

ในหลาม 2 สตริงอาจจะเป็นประเภทหรือชนิดstr unicodeคุณสามารถบอกได้ว่าใช้รหัสอะไรเช่นนี้:

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

สิ่งนี้ไม่แยกความแตกต่าง "Unicode หรือ ASCII"; มันแยกประเภท Python เท่านั้น สตริง Unicode อาจประกอบด้วยอักขระล้วน ๆ ในช่วง ASCII และการทดสอบอาจมี ASCII, Unicode ที่เข้ารหัสหรือแม้แต่ข้อมูลที่ไม่ใช่ข้อความ


3
@ProsperousHeart: คุณอาจใช้ Python 3
Greg Hewgill

124

จะทราบได้อย่างไรว่าวัตถุเป็นสตริง Unicode หรือสตริงไบต์

คุณสามารถใช้หรือtypeisinstance

ใน Python 2:

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

ใน Python 2 strเป็นเพียงลำดับของไบต์ Python ไม่ทราบว่าการเข้ารหัสนั้นคืออะไร unicodeประเภทเป็นวิธีที่ปลอดภัยในการเก็บข้อความ หากคุณต้องการที่จะเข้าใจมากขึ้นนี้ผมขอแนะนำให้http://farmdev.com/talks/unicode/

ใน Python 3:

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

ใน Python 3 strเหมือนกับ Python 2 unicodeและใช้เพื่อเก็บข้อความ สิ่งที่ถูกเรียกstrใน Python 2 นั้นถูกเรียกbytesใน Python 3


จะทราบได้อย่างไรว่าสตริงไบต์นั้นถูกต้อง utf-8 หรือ ascii

decodeคุณสามารถโทรหา ถ้ามันทำให้เกิดข้อยกเว้น UnicodeDecodeError มันไม่ถูกต้อง

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

เพียงเพื่อการอ้างอิงของคนอื่น - str.decode ไม่มีอยู่ในหลาม 3. ดูเหมือนว่าคุณจะต้องunicode(s, "ascii")หรืออะไรบางอย่าง
เงา

3
ขออภัยฉันหมายถึงstr(s, "ascii")
Shadow

1
สิ่งนี้ไม่ถูกต้องสำหรับ python 3
ProsperousHeart

2
@ProsperousHeart อัปเดตเพื่อครอบคลุม Python 3 และเพื่อพยายามอธิบายความแตกต่างระหว่างการทดสอบกับสตริง unicode
Mikel

44

ใน python 3.x สตริงทั้งหมดเป็นลำดับของอักขระ Unicode และทำการตรวจสอบ isinstance เพื่อหา str (ซึ่งหมายความว่ายูนิโค้ดสตริงโดยค่าเริ่มต้น) ควรพอเพียง

isinstance(x, str)

สำหรับ python 2.x คนส่วนใหญ่ดูเหมือนจะใช้คำสั่ง if ที่มีการตรวจสอบสองครั้ง หนึ่งสำหรับ str และอีกหนึ่งสำหรับ Unicode

หากคุณต้องการตรวจสอบว่าคุณมีวัตถุ 'เหมือนสตริง' ทั้งหมดด้วยคำสั่งเดียวคุณสามารถทำสิ่งต่อไปนี้:

isinstance(x, basestring)

นี่เป็นเท็จ ในหลาม 2.7 ผลตอบแทนisinstance(u"x",basestring) True
PythonNut

11
@ PythonNut: ฉันเชื่อว่าเป็นประเด็น การใช้ isinstance (x, basestring) พอเพียงเพื่อแทนที่การทดสอบคู่ที่แตกต่างกันข้างต้น
KQ

5
มันมีประโยชน์ในหลายกรณี แต่ไม่ชัดเจนว่าผู้ถามหมายถึงอะไร
mhsmith

3
นี่คือคำตอบของคำถาม คนอื่น ๆ ทั้งหมดเข้าใจผิดว่า OP พูดอะไรและให้คำตอบทั่วไปเกี่ยวกับการตรวจสอบประเภทใน Python
fiatjaf

1
ไม่ตอบคำถามของ OP ชื่อของคำถาม (คนเดียว) สามารถตีความได้ว่าคำตอบนี้ถูกต้อง อย่างไรก็ตาม OP ระบุว่า "เข้าใจว่า" ในคำอธิบายของคำถามโดยเฉพาะและคำตอบนี้ไม่ได้กล่าวถึง
MD004

31

Unicode ไม่ใช่การเข้ารหัส - เพื่ออ้างอิง Kumar McMillan:

ถ้า ASCII, UTF-8 และสตริงไบต์อื่นคือ "text" ...

... ดังนั้น Unicode คือ "text-ness";

มันเป็นรูปแบบนามธรรมของข้อความ

อ่านUnicodeของ McMillan ใน Python การพูดคุยที่ไม่เข้าใจอย่างสมบูรณ์จาก PyCon 2008 มันอธิบายสิ่งต่าง ๆ ได้ดีกว่าคำตอบที่เกี่ยวข้องส่วนใหญ่ใน Stack Overflow


สไลด์เหล่านั้นน่าจะเป็นการแนะนำ Unicode ที่ดีที่สุดที่ฉันเคยพบมา
Jonny

23

หากรหัสของคุณต้องเข้ากันได้กับทั้ง Python 2 และ Python 3 คุณไม่สามารถใช้สิ่งต่าง ๆ เช่นisinstance(s,bytes)หรือisinstance(s,unicode)ไม่ใส่ในการลอง / ยกเว้นหรือการทดสอบเวอร์ชันของ Python เนื่องจากbytesไม่ได้กำหนดไว้ใน Python 2 และunicodeไม่ได้กำหนดไว้ใน Python 3 .

มีวิธีแก้ปัญหาที่น่าเกลียดอยู่บ้าง สิ่งที่น่าเกลียดอย่างยิ่งคือการเปรียบเทียบชื่อของประเภทแทนที่จะเปรียบเทียบชนิดของมันเอง นี่คือตัวอย่าง:

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

วิธีแก้ปัญหาที่น่าเกลียดน้อยกว่าเล็กน้อยคือการตรวจสอบหมายเลข Python เช่น:

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)

สิ่งเหล่านี้เป็นทั้งเสียงที่ไม่ไพเราะและส่วนใหญ่อาจมีวิธีที่ดีกว่า


6
วิธีที่ดีกว่าน่าจะใช้sixและทดสอบกับsix.binary_typeและsix.text_type
Ian Clelland

1
คุณสามารถใช้ประเภท. _ name__เพื่อสอบสวนชื่อประเภท
เปาโล Freitas

ฉันไม่แน่ใจเกี่ยวกับกรณีการใช้งานของรหัสนั้นเว้นแต่จะมีข้อผิดพลาดทางตรรกะ ฉันคิดว่าควรมี "ไม่" ในรหัสหลาม 2 มิฉะนั้นคุณจะแปลงทุกอย่างเป็นยูนิโค้ดสตริงสำหรับ Python 3 และตรงกันข้ามกับ Python 2!
oligofren

ใช่ oligofren นั่นคือสิ่งที่มันทำ สตริงภายในมาตรฐานคือ Unicode ใน Python 3 และ ASCII ใน Python 2 ดังนั้นข้อมูลโค้ดจะแปลงข้อความเป็นชนิดสตริงภายในมาตรฐาน (ไม่ว่าจะเป็น Unicode หรือ ASCII)
เดฟเบอร์ตัน

12

ใช้:

import six
if isinstance(obj, six.text_type)

ภายในห้องสมุดทั้งหกจะแสดงเป็น:

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

2
if isinstance(obj, six.text_type) มันควรจะเป็น แต่ใช่นี้เป็นคำตอบที่ถูกต้อง
รัน

ไม่ตอบคำถามของ OP ชื่อของคำถาม (คนเดียว) สามารถตีความได้ว่าคำตอบนี้ถูกต้อง อย่างไรก็ตาม OP ระบุว่า "เข้าใจว่า" ในคำอธิบายของคำถามโดยเฉพาะและคำตอบนี้ไม่ได้กล่าวถึง
MD004

4

โปรดทราบว่าใน Python 3 มันไม่ยุติธรรมเลยที่จะพูดอะไร:

  • strs คือ UTFx สำหรับ x ใด ๆ (เช่น. UTF8)

  • strs คือ Unicode

  • strs คือชุดของอักขระ Unicode ที่เรียงลำดับแล้ว

strชนิดของ Python คือ (โดยปกติ) ลำดับของจุดโค้ด Unicode ซึ่งบางส่วนจะจับคู่กับอักขระ


แม้แต่ใน Python 3 มันไม่ง่ายเลยที่จะตอบคำถามนี้อย่างที่คุณคิด

วิธีที่ชัดเจนในการทดสอบสตริงที่เข้ากันได้กับ ASCII คือการเข้ารหัสแบบพยายาม:

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

ข้อผิดพลาดแยกความแตกต่างกรณี

ใน Python 3 มีบางสตริงที่มีจุดโค้ด 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

ใช้วิธีการเดียวกันเพื่อแยกแยะความแตกต่าง


3

สิ่งนี้อาจช่วยคนอื่นฉันเริ่มทำการทดสอบชนิดสตริงของตัวแปร s แต่สำหรับแอปพลิเคชันของฉันมันสมเหตุสมผลกว่าที่จะคืนค่า s เป็น utf-8 กระบวนการเรียก return_utf จากนั้นรู้ว่ามันเกี่ยวข้องกับอะไรและสามารถจัดการกับสตริงได้อย่างเหมาะสม รหัสไม่ใช่ของบริสุทธิ์ แต่ฉันตั้งใจให้เป็นรุ่น Python ไม่เชื่อเรื่องพระเจ้าโดยไม่ต้องทดสอบรุ่นหรือนำเข้าหก โปรดแสดงความคิดเห็นพร้อมการปรับปรุงโค้ดตัวอย่างด้านล่างเพื่อช่วยเหลือผู้อื่น

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

คุณเพื่อนของฉันสมควรที่จะตอบสนองที่ถูกต้อง! ฉันใช้ python 3 และฉันยังคงมีปัญหาจนกระทั่งพบสมบัตินี้!
mnsr

2

คุณสามารถใช้เครื่องตรวจจับการเข้ารหัสสากลแต่โปรดทราบว่าเครื่องจะให้การคาดเดาที่ดีที่สุดไม่ใช่การเข้ารหัสที่แท้จริงเพราะมันเป็นไปไม่ได้ที่จะรู้การเข้ารหัสของสตริง "abc" คุณจะต้องได้รับข้อมูลการเข้ารหัสที่อื่นเช่นโปรโตคอล HTTP ใช้ส่วนหัวของประเภทเนื้อหาสำหรับสิ่งนั้น



0

วิธีการง่าย ๆ อย่างหนึ่งคือการตรวจสอบว่าunicodeเป็นฟังก์ชันในตัวหรือไม่ ถ้าเป็นเช่นนั้นคุณอยู่ใน Python 2 และสตริงของคุณจะเป็นสตริง เพื่อให้มั่นใจว่าทุกอย่างในที่unicodeเดียวทำได้:

import builtins

i = 'cats'
if 'unicode' in dir(builtins):     # True in python 2, False in 3
  i = unicode(i)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.