Чим відрізняється рядок від байтового рядка?


209

Я працюю з бібліотекою, яка повертає рядок байтів, і мені потрібно перетворити це в рядок.

Хоча я не впевнений, у чому різниця - якщо така є.

Відповіді:


260

Якщо припустити Python 3 (у Python 2 ця різниця трохи менш чітко визначена) - рядок - це послідовність символів, тобто кодові точки unicode ; це абстрактне поняття, яке не може бути безпосередньо збережено на диску. Рядок байтів - це послідовність, не дивно, байтів - речей, які можна зберігати на диску. Відображення між ними є кодуванням - їх дуже багато (і нескінченно багато можливих) - і вам потрібно знати, що застосовується в конкретному випадку для здійснення перетворення, оскільки інше кодування може відображати однакові байти до іншого рядка:

>>> b'\xcf\x84o\xcf\x81\xce\xbdo\xcf\x82'.decode('utf-16')
'蓏콯캁澽苏'
>>> b'\xcf\x84o\xcf\x81\xce\xbdo\xcf\x82'.decode('utf-8')
'τoρνoς'

Після того, як ви дізнаєтеся, який з них використовувати, ви можете скористатися .decode()методом байтового рядка, щоб отримати потрібну рядок символів з нього, як зазначено вище. Для повноти .encode()метод рядка символів йде навпаки:

>>> 'τoρνoς'.encode('utf-8')
b'\xcf\x84o\xcf\x81\xce\xbdo\xcf\x82'

7
Для уточнення для користувачів Python 2: strтип такий же, як bytesтип; ця відповідь рівномірно порівнює unicodeтип (не існує в Python 3) з strтипом.
craymichael

3
@KshitijSaraogi, що теж не зовсім вірно; все це речення було відредаговане і трохи прикро. Представлення об'єктів Python 3 в пам'яті strне є доступним або релевантним з боку Python; структура даних - це лише послідовність кодових точок. Відповідно до PEP 393 , точне внутрішнє кодування є латинським-1, UCS2 або UCS4, і представлення utf-8 може бути кешоване після його першого запиту, але навіть C-код відмовляється покладатися на ці внутрішні деталі.
lvc

1
Якщо вони не можуть бути безпосередньо збережені на диску, то як вони зберігаються в пам'яті?
z33k

2
@orety, вони дійсно повинні бути закодовані якось внутрішньо саме з цієї причини, але це не експозиції для вас з коду Python, як і вам не потрібно дбати про те, як зберігаються номери з плаваючою комою.
lvc

1
@ChrisStryczynski див. Коментарі вище - переконайтеся, що вони зберігаються в пам’яті якось , але ця форма явно абстрагується. Дійсно, в ці дні він може змінюватися протягом життя програми і відрізнятися між різними рядками або навіть може бути більше ніж один (деякі кодування є кешованими), залежно від символів у них - але єдиний час, коли потрібно турбуватися про тобто якщо ви зламаєте реалізацію самого типу рядка.
lvc

390

Єдине, що може зберігати комп’ютер, - це байти.

Щоб зберігати що-небудь у комп’ютері, спершу його потрібно закодувати , тобто перетворити в байти. Наприклад:

  • Якщо ви хочете зберегти музику, ви повинні спочатку закодувати його з допомогою MP3, WAVі т.д.
  • Якщо ви хочете зберегти зображення, ви повинні спочатку закодувати його з допомогою PNG, JPEGі т.д.
  • Якщо ви хочете зберегти текст, ви повинні спочатку закодувати його з допомогою ASCII, UTF-8і т.д.

MP3, WAV, PNG, JPEG, ASCIIІ UTF-8приклади кодування . Кодування - це формат для представлення звуку, зображень, тексту тощо в байтах.

У Python рядок байтів - це саме те, що: послідовність байтів. Це не читабельно для людей. Під кришкою все повинно бути перетворено в рядок байтів, щоб воно могло зберігатися в комп'ютері.

З іншого боку, символьний рядок, який часто називають "рядком", - це послідовність символів. Це читається людиною. Рядок символів не може бути безпосередньо збережений у комп'ютері, його потрібно спочатку закодувати (перетворити в рядок байтів). Існує кілька кодувань, за допомогою яких символьна рядок може бути перетворена в рядок байтів, таких як ASCIIі UTF-8.

'I am a string'.encode('ASCII')

Вищенаведений код Python буде кодувати рядок 'I am a string'за допомогою кодування ASCII. Результатом вищевказаного коду стане рядок байтів. Якщо ви надрукуєте його, Python буде представляти його як b'I am a string'. Пам’ятайте, однак, що байтові рядки не читаються людиною , це лише те, що Python розшифровує їх, ASCIIколи ви їх друкуєте. У Python рядок байтів представлений символом a b, а потім представленням рядка байтів ASCII.

Рядок байтів може бути декодований назад в символьну рядок, якщо ви знаєте кодування, яке було використано для його кодування.

b'I am a string'.decode('ASCII')

Вищевказаний код поверне початковий рядок 'I am a string'.

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


59
Зенадікс тут заслуговує на кудо. Через кілька років функціонування в цьому середовищі його перше пояснення, яке натиснуло на мене. Я можу татуювати його на іншій руці (на одній руці вже є "Абсолютний мінімум кожного розробника програмного забезпечення абсолютно, позитивно повинен знати про набори юнікодів та персонажів (без виправдань!) Джоела Спольського"
neil.millikin

4
Абсолютно геніально. Легкий і зрозумілий. Однак я хотів би зазначити, що цей рядок - "Якщо ви надрукуєте його, Python буде представляти його як b'I am string" "вірно для Python3, так як для байтів Python2 і str це те саме.
СРЦ

5
Я нагороджую вас цією винагородою за те, що я пропонував дуже зрозумілі для людини пояснення, щоб внести деяку ясність у цю тему!
fedorqui 'ТАК перестаньте шкодити'

3
Чудова відповідь. Єдине, що, можливо, можна додати, це більш чітко зазначити, що історично програмісти та мови програмування мали тенденцію явно чи неявно припускати, що послідовність байтів і рядок ASCII - це одне і те ж . Python 3 вирішив явно порушити це припущення, правильно IMHO.
некоматичний

4
Посилання на повідомлення Джоеля, згадане @ neil.millikin вище: joelonsoftware.com/2003/10/08/…
Kshitij Saraogi

14

Примітка: Я детальніше розробимо свою відповідь на Python 3, оскільки кінець життя Python 2 дуже близький.

У Python 3

bytesскладається з послідовностей 8-бітних неподписаних значень, тоді як strскладається з послідовностей кодових точок Unicode, які представляють текстові символи з людських мов.

>>> # bytes
>>> b = b'h\x65llo'
>>> type(b)
<class 'bytes'>
>>> list(b)
[104, 101, 108, 108, 111]
>>> print(b)
b'hello'
>>>
>>> # str
>>> s = 'nai\u0308ve'
>>> type(s)
<class 'str'>
>>> list(s)
['n', 'a', 'i', '̈', 'v', 'e']
>>> print(s)
naïve

Незважаючи на те, bytesі strздається, працюють точно так же, їх екземпляри не сумісні один з одним, тобто bytesі strекземпляри не можуть бути використані разом з операторами , як >і +. Крім того, майте на увазі, що порівняння bytesта strвипадки рівності, тобто використання ==, завжди оцінюватимуть Falseнавіть тоді, коли вони містять абсолютно однакові символи.

>>> # concatenation
>>> b'hi' + b'bye' # this is possible
b'hibye'
>>> 'hi' + 'bye' # this is also possible
'hibye'
>>> b'hi' + 'bye' # this will fail
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't concat str to bytes
>>> 'hi' + b'bye' # this will also fail
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "bytes") to str
>>>
>>> # comparison
>>> b'red' > b'blue' # this is possible
True
>>> 'red'> 'blue' # this is also possible
True
>>> b'red' > 'blue' # you can't compare bytes with str
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '>' not supported between instances of 'bytes' and 'str'
>>> 'red' > b'blue' # you can't compare str with bytes
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '>' not supported between instances of 'str' and 'bytes'
>>> b'blue' == 'red' # equality between str and bytes always evaluates to False
False
>>> b'blue' == 'blue' # equality between str and bytes always evaluates to False
False

Ще одне питання при роботі bytesі strє при роботі з файлами, які повертаються за допомогою openвбудованої функції. З одного боку, якщо ви хочете читати або записувати бінарні дані в / з файлу, завжди відкривайте файл у двійковому режимі, наприклад 'rb' або 'wb'. З іншого боку, якщо ви хочете прочитати або записати дані Unicode у файл / з файлу, пам’ятайте про кодування за замовчуванням вашого комп’ютера, тому при необхідності передайте encodingпараметр, щоб уникнути сюрпризів.

У Python 2

strскладається з послідовностей 8-бітних значень, тоді як unicodeскладається з послідовностей символів Unicode. Одна річ , щоб мати на увазі, що strі unicodeможе бути використано разом з операторами , якщо strскладається тільки з 7-бітових символів ASCI.

Можливо, буде корисно використовувати допоміжні функції для перетворення між strі unicodeв Python 2, і між bytesі strв Python 3.


4

З Що таке Unicode :

Принципово, комп'ютери просто мають справу з цифрами. Вони зберігають букви та інші символи, присвоюючи номер кожному.

......

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

Отже, коли комп'ютер представляє рядок, він знаходить символи, що зберігаються в комп'ютері рядка через їх унікальний номер Unicode, і ці фігури зберігаються в пам'яті. Але ви не можете безпосередньо записати рядок на диск або передати рядок в мережі через їх унікальний номер Unicode, оскільки ці цифри - це просто просте десяткове число. Ви повинні кодувати рядок до байтового рядка, наприклад UTF-8. UTF-8це символ , який кодує здатними кодувати всі можливі символи і зберігає символи , як байти (це виглядає як це ). Тож закодована рядок може використовуватися скрізь, оскільки UTF-8майже підтримується скрізь. Коли ви відкриєте закодований текстовий файлUTF-8в інших системах ваш комп'ютер буде декодувати його та відображати символи в ньому через унікальний номер Unicode. Коли браузер отримує рядкові дані, закодовані UTF-8з мережі, він декодує дані в рядкові (припустимо браузер в UTF-8кодуванні) і відображає рядок.

У python3 ви можете перетворити рядок і рядок байтів один в одного:

>>> print('中文'.encode('utf-8'))
b'\xe4\xb8\xad\xe6\x96\x87'
>>> print(b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8'))
中文 

Словом, рядок призначений для відображення людям для читання на комп’ютері, а байтовий рядок - для зберігання на диску та передачі даних.


1

Unicode - це узгоджений формат для двійкового представлення символів та різного роду форматування (наприклад, нижній регістр / верхній регістр, нова лінія, повернення каретки) та інші "речі" (наприклад, емоджи). Комп'ютер не менш здатний зберігати представлення unicode (серія біт), чи в пам'яті, чи у файлі, ніж це для зберігання представлення ascii (інша серія біт) або будь-яке інше представлення (серія біт) ).

Для того, щоб спілкування відбулося, сторони спілкування повинні домовитись про те, яке представництво буде використовуватися.

Оскільки unicode прагне представити всі можливі символи (та інші "речі"), що використовуються в спілкуванні між людьми та між комп'ютерами, для представлення багатьох символів (або речей) потрібна більша кількість бітів, ніж інші системи представлення, які прагнути представити більш обмежений набір символів / речей. Для "спрощення" та, можливо, для історичного використання, представлення unicode майже виключно перетворюється на якусь іншу систему подання (наприклад, ascii) з метою зберігання символів у файлах.

Це не той випадок, юнікод не може бути використаний для зберігання символів в файлах, або передавати їх через будь-який канал зв'язку, просто , що це НЕ так .

Термін "рядок" точно не визначений. "Рядок" у своєму загальному використанні відноситься до набору символів / речей. На комп'ютері ці символи можуть зберігатися в будь-якому з багатьох різних побітових зображень. "Рядок байтів" - це набір символів, що зберігається за допомогою представлення, яке використовує вісім біт (вісім біт називають байтом). Оскільки в ці дні комп’ютери використовують систему Unicode (символи, представлені змінною кількістю байтів) для зберігання символів у пам'яті, а рядки байтів (символи, представлені одиничними байтами) для зберігання символів у файлах, перетворення повинно використовуватися перед тим, як символи представлені в пам'яті буде переміщено в сховище у файлах.


0

Будемо мати простий односимвольний рядок 'š'і кодувати його в послідовності байтів:

>>> 'š'.encode('utf-8')
b'\xc5\xa1'

Для цього прикладу покажемо послідовність байтів у його двійковому вигляді:

>>> bin(int(b'\xc5\xa1'.hex(), 16))
'0b1100010110100001'

Зараз взагалі неможливо декодувати інформацію назад, не знаючи, як вона була закодована. Тільки якщо ви знаєте, що використовувалося utf-8кодування тексту, ви можете слідувати алгоритму розшифровки utf-8 та отримати початковий рядок:

11000101 10100001
   ^^^^^   ^^^^^^
   00101   100001

Ви можете відображати бінарне число 101100001назад як рядок:

>>> chr(int('101100001', 2))
'š'

0

Мова Python включає strі bytesяк "Вбудовані типи" як стандарт. Іншими словами, вони обидва класи. Я не думаю, що варто намагатися раціоналізувати, чому Python реалізований таким чином.

Сказавши це, strі bytesдуже схожі між собою. Обидва поділяють більшість однакових методів. Наступні методи унікальні для strкласу:

casefold
encode
format
format_map
isdecimal
isidentifier
isnumeric
isprintable

Наступні методи унікальні для bytesкласу:

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