Python str та unicode типів


101

Працюючи з Python 2.7, мені цікаво, яка реальна перевага є у використанні типу unicodeзамість str, оскільки обидва вони, здається, можуть утримувати рядки Unicode. Чи є якась особлива причина, окрім того, що не можна встановлювати коди Unicode в unicodeрядках, використовуючи табличку втечі \?:

Виконання модуля за допомогою:

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

a = 'á'
ua = u'á'
print a, ua

Результати в: á, á

Редагувати:

Більше тестування за допомогою оболонки Python:

>>> a = 'á'
>>> a
'\xc3\xa1'
>>> ua = u'á'
>>> ua
u'\xe1'
>>> ua.encode('utf8')
'\xc3\xa1'
>>> ua.encode('latin1')
'\xe1'
>>> ua
u'\xe1'

Отже, unicodeздається , що рядок кодується з використанням latin1замість, utf-8а необроблена рядок кодується за допомогою utf-8? Я зараз ще більше плутаю! : S


Там немає кодування для unicode, це просто абстракція юнікода характеру; unicodeможна перетворити на strдеяке кодування (наприклад utf-8).
Бін

Відповіді:


178

unicodeпризначений для обробки тексту . Текст - це послідовність точок коду, яка може бути більшою, ніж один байт . Текст може бути закодований у певному кодуванні для подання тексту як необроблених байтів (наприклад utf-8, latin-1...).

Зверніть увагу, що unicode не закодовано ! Внутрішнє представлення, яке використовує python, - це деталь реалізації, і вам не варто дбати про нього, якщо воно здатне представляти потрібні кодові точки.

Навпаки, strв Python 2 є звичайна послідовність байтів . Він не представляє текст!

Ви можете розглядати unicodeяк загальне подання деякого тексту, який можна кодувати різними способами у послідовність двійкових даних, представлених через str.

Примітка. У Python 3 unicodeбуло перейменовано strв новий bytesтип для простої послідовності байтів.

Деякі відмінності, які ви можете бачити:

>>> len(u'à')  # a single code point
1
>>> len('à')   # by default utf-8 -> takes two bytes
2
>>> len(u'à'.encode('utf-8'))
2
>>> len(u'à'.encode('latin1'))  # in latin1 it takes one byte
1
>>> print u'à'.encode('utf-8')  # terminal encoding is utf-8
à
>>> print u'à'.encode('latin1') # it cannot understand the latin1 byte

Зауважте, що при використанні у strвас є управління нижчого рівня на одиничних байтах конкретного представлення кодування, при цьому unicodeви можете керувати лише на рівні кодової точки. Наприклад, ви можете зробити:

>>> 'àèìòù'
'\xc3\xa0\xc3\xa8\xc3\xac\xc3\xb2\xc3\xb9'
>>> print 'àèìòù'.replace('\xa8', '')
à�ìòù

Те, що раніше було дійсним UTF-8, вже не є. Використовуючи рядок unicode, ви не можете працювати таким чином, що отримана рядок не є дійсним текстом unicode. Ви можете видалити кодову точку, замінити кодову точку на іншу точку коду тощо, але не можете поплутатися з внутрішнім представленням.


4
Дуже дякую за вашу відповідь, це дуже допомогло! Найясніша частина для мене - це: "unicode не кодується! Внутрішнє представлення, яке використовується python, - це детальна інформація про реалізацію, і вам не слід про це хвилюватись [...]". Отже, при серіалізації unicodeоб'єктів я думаю, що спочатку ми маємо чітко encode()їх встановити у відповідному форматі кодування, оскільки ми не знаємо, який із них використовується для представлення unicodeзначення.
Каумони

10
Так. Коли ви хочете зберегти якийсь текст (наприклад, у файл), ви повинні представити його байтами, тобто ви повинні закодувати його. Під час отримання вмісту ви повинні знати кодування, яке було використано, щоб мати можливість декодувати байти в unicodeоб'єкт.
Бакуріу

Вибачте, але твердження, яке unicodeне закодовано, явно неправильне. UTF-16 / UCS-2 і UTF-32 / UCS-4 також є кодуванням ... і в майбутньому більше цього, можливо, буде створено. Справа в тому, що ви не повинні дбати про деталі реалізації (і, справді, не слід!), Все ще не означає, що unicodeце не закодовано. Це, звичайно. Чи може це бути .decode()- це зовсім інша історія.
0xC0000022L

1
@ 0xC0000022L Можливо, пропозиція такою, якою вона є, незрозумілою. Слід сказати: unicodeвнутрішнє представлення об'єкта може бути будь-яким, що хоче, в тому числі нестандартним. Зокрема , в Python3 + unicode дійсно використовувати нестандартне внутрішнє уявлення , що також змінюється в залежності від даних , що міститься. Як таке, це не стандартне кодування . Unicode як текстовий стандарт визначає лише кодові точки, які є абстрактним поданням тексту, існує багато способів кодування unicode в пам'яті, включаючи стандартний utf-X і т. Д. Python використовує власний шлях для ефективності.
Бакуріу

1
@ 0xC0000022L Також факт, що UTF-16 є кодуванням, не має нічого спільного з unicodeоб'єктом CPython , оскільки він не використовує ні UTF-16, ні UTF-32. Він використовує спеціальне подання, і якщо ви хочете закодувати дані в фактичні байти, які ви повинні використовувати encode. Також: мова не вимагає того, як unicodeреалізовано, тому різні версії або реалізації python можуть (і мають ) різні внутрішні представлення.
Бакуріу

38

Unicode та кодування - це абсолютно різні, не пов'язані між собою речі.

Unicode

Призначає числовий ідентифікатор кожному символу:

  • 0x41 → A
  • 0xE1 → á
  • 0x414 → Д

Отже, Unicode призначає число 0x41 до A, 0xE1 до á, а 0x414 - D.

Навіть у маленької стрілки → Я використав свій номер Unicode, це 0x2192. І навіть емоджи мають свої номери Unicode, 😂 - 0x1F602.

Ви можете шукати номери Unicode всіх символів у цій таблиці . Зокрема, ви можете знайти перші три символи вище тут , стрілку тут та емоджи тут .

Ці числа, присвоєні Unicode всім символам, називаються кодовими точками .

Метою всього цього є надання засобу для однозначного звернення до кожного персонажа. Наприклад, якщо я кажу про 😂, замість того, щоб сказати "знаєте, це сміються емоджи зі сльозами" , я можу просто сказати, код коду Unicode 0x1F602 . Простіше, правда?

Зауважте, що кодові точки Unicode зазвичай відформатовані з провідним U+, тоді шістнадцяткове числове значення додається щонайменше до 4 цифр. Отже, наведеними вище прикладами будуть U + 0041, U + 00E1, U + 0414, U + 2192, U + 1F602.

Кодові точки Unicode варіюються від U + 0000 до U + 10FFFF. Це 1,114,112 цифр. 2048 з цих номерів використовуються для сурогатів , таким чином, залишається 1112 064. Це означає, що Unicode може призначити унікальний ідентифікатор (кодова точка) 1112,064 різних символів. Ще не всі ці точки коду присвоєні символу, і Unicode розширюється постійно (наприклад, коли вводяться нові емоджи).

Важливо пам’ятати, що все, що Unicode робить - це призначити числовий ідентифікатор, який називається кодовою точкою, кожному символу для легкого та однозначного посилання.

Кодування

Зображуйте символи, щоб розбити шаблони

Ці бітові структури використовуються для представлення символів у пам'яті комп'ютера або на диску.

Існує багато різних кодувань, які охоплюють різні підмножини символів. В англомовному світі найпоширенішими кодування є такі:

ASCII

Карти 128 символів (кодові точки U + 0000 до U + 007F), щоб розрядити шаблони довжиною 7.

Приклад:

  • a → 1100001 (0x61)

Ви можете переглянути всі відображення в цій таблиці .

ISO 8859-1 (він же латинський-1)

Карти 191 символів (кодові точки U + 0020 до U + 007E та U + 00A0 до U + 00FF), щоб розрядити діапазони довжиною 8.

Приклад:

  • a → 01100001 (0x61)
  • á → 11100001 (0xE1)

Ви можете переглянути всі відображення в цій таблиці .

UTF-8

Карти 1,112,064 символів (усі існуючі точки коду Unicode) для бітових шаблонів довжиною 8, 16, 24 або 32 біт (тобто 1, 2, 3 або 4 байти).

Приклад:

  • a → 01100001 (0x61)
  • á → 11000011 10100001 (0xC3 0xA1)
  • ≠ → 11100010 10001001 10100000 (0xE2 0x89 0xA0)
  • 😂 → 11110000 10011111 10011000 10000010 (0xF0 0x9F 0x98 0x82)

До речі UTF-8 кодує символи в бітові рядки дуже добре описано тут .

Unicode та кодування

Переглядаючи наведені вище приклади, стає зрозуміло, наскільки Unicode корисний.

Наприклад, якщо я є Latin-1 і хочу пояснити своє кодування á, мені не потрібно говорити:

"Я кодую це з aigu (або, як ви називаєте, цей піднімаючий бар) як 11100001"

Але я можу просто сказати:

"Я кодую U + 00E1 як 11100001"

І якщо я UTF-8 , я можу сказати:

"Я, в свою чергу, кодую U + 00E1 як 11000011 10100001"

І всім однозначно зрозуміло, про який персонаж ми маємо на увазі.

Тепер до часто виникаючої плутанини

Це правда, що іноді бітова модель кодування, якщо ви інтерпретуєте її як двійкове число, є такою ж, як точка коду Unicode цього символу.

Наприклад:

  • ASCII кодує а також 1100001, який можна інтерпретувати як шістнадцяткове число 0x61 і точка коди Юникода є U + 0061 .
  • Latin-1 кодує á , як 11100001, який можна інтерпретувати як шістнадцяткове число 0xE1 , а код точки Юникода á є U + 00E1 .

Звичайно, це було організовано спеціально для зручності. Але ви повинні дивитися на це як на чистий збіг . Бітова модель, яка використовується для представлення символу в пам'яті, жодним чином не прив’язана до кодової точки Unicode цього символу.

Ніхто навіть не каже, що ви повинні інтерпретувати такий рядок, як 11100001, як двійкове число. Просто дивитися на нього як послідовність бітів, Latin-1 використовується для кодування символів á .

Поверніться до свого питання

Кодування, що використовується інтерпретатором Python, є UTF-8 .

Ось що відбувається у ваших прикладах:

Приклад 1

Далі кодується символ á в UTF-8. У результаті виходить бітовий рядок 11000011 10100001, який зберігається у змінній a.

>>> a = 'á'

Коли ви дивитесь на значення a, його вміст 11000011 10100001 форматується як шістнадцяткове число 0xC3 0xA1 і виводиться у вигляді '\xc3\xa1':

>>> a
'\xc3\xa1'

Приклад 2

Далі зберігається кодова точка Unicode від á, що є U + 00E1, у змінній ua(ми не знаємо, який формат даних Python використовує внутрішньо для представлення кодової точки U + 00E1 в пам'яті, і це для нас неважливо):

>>> ua = u'á'

Коли ви дивитесь на значення ua, Python повідомляє вам, що воно містить кодову точку U + 00E1:

>>> ua
u'\xe1'

Приклад 3

Наступне кодує кодову точку Unicode U + 00E1 (представляє символ á) з UTF-8, що призводить до бітової схеми 11000011 10100001. Знову для виведення цей бітовий малюнок представлений як шістнадцятковий номер 0xC3 0xA1:

>>> ua.encode('utf-8')
'\xc3\xa1'

Приклад 4

Наступні кодує кодову точку Unicode U + 00E1 (представляє символ á) з Latin-1, що призводить до бітової схеми 11100001. Для виведення цей бітовий візерунок представлений як шістнадцятковий номер 0xE1, який за збігом обставин такий же, як початковий кодова точка U + 00E1:

>>> ua.encode('latin1')
'\xe1'

Немає зв'язку між об'єктом Unicode uaта кодуванням Latin-1. Те, що кодовою точкою á є U + 00E1, а кодування латинських значень á дорівнює 0xE1 (якщо інтерпретувати бітову схему кодування як двійкове число), є чистим збігом обставин.


31

Ваш термінал має бути налаштований на UTF-8.

Те, що друкарні a- це збіг; ви записуєте необроблені байти UTF-8 в термінал. a- значення довжини два , що містить два байти, шістнадцяткові значення C3 та A1, в той час uaяк значення unicode довжини один , що містить кодову точку U + 00E1.

Ця різниця в довжині є однією з основних причин використання значень Unicode; ви не можете легко виміряти кількість текстових символів у рядку байтів; len()рядки байт говорять вам , як були використані багато байт, а не як було закодовані багато символів.

Ви можете побачити різницю, коли кодуєте значення unicode для різних вихідних кодувань:

>>> a = 'á'
>>> ua = u'á'
>>> ua.encode('utf8')
'\xc3\xa1'
>>> ua.encode('latin1')
'\xe1'
>>> a
'\xc3\xa1'

Зауважте, що перші 256 кодових точок стандарту Unicode відповідають стандарту Latin 1, тому кодова точка U + 00E1 кодується до латиниці 1 як байт із шістнадцятковим значенням E1.

Крім того, Python використовує коди евакуації в представленнях рядків unicode та байтів, а також низькі кодові точки, які не можна друкувати ASCII, також використовуються \x..значеннями escape. Ось чому рядок Unicode з кодовою точкою між 128 та 255 виглядає так само, як кодування з латинської 1. Якщо у вас є рядок Unicode з кодовими точками, що перевищують U + 00FF, натомість використовується інша послідовність евакуації \u....із чотиризначним шестизначним значенням.

Схоже, ви ще не повністю розумієте, у чому різниця між Unicode та кодуванням. Будь ласка, прочитайте наступні статті, перш ніж продовжувати:


Я відредагував своє запитання з подальшим тестуванням. Я деякий час читав унікод та різні кодування, і думаю, що розумію теорію, але, фактично тестуючи код Python, я не розумію, що відбувається
Caumons

1
Кодування Latin-1 відповідає першим 256 кодовим точкам стандарту Unicode. Ось чому U + 00E1 кодує до \xe1латиниці 1.
Martijn Pieters

2
Це єдиний найважливіший аспект для Unicode. Це не кодування . Це текст. Unicode - це стандарт, який включає багато, набагато більше, як-от інформація про те, які кодові точки є цифрами, або пробіл чи інші категорії, повинні відображатися зліва направо чи справа наліво тощо тощо тощо.
Martijn Pieters

1
Це так само, як сказати, що Unicode - це як "Інтерфейс", а кодування - як власне "Впровадження".
Каумони

2
@Varun: ви повинні використовувати вузьку збірку Python 2, яка використовує UCS-2 внутрішньо і неправильно представляє що-небудь над U + FFFF як довжину дві. Python 3 та UCS-2 (широка) збірка покажуть вам довжину дійсно 1.
Martijn Pieters

2

Коли ви визначаєте як unicode, символи a і á рівні. В іншому випадку á вважається двома знаками. Спробуйте len (a) та len (au). Крім цього, вам може знадобитися кодування під час роботи з іншими середовищами. Наприклад, якщо ви використовуєте md5, ви отримуєте різні значення для a і ua

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.