В чем разница между строкой и байтовой строкой?

210

Я работаю с библиотекой, которая возвращает байтовую строку, и мне нужно преобразовать ее в строку.

Хотя я не уверен, какая разница - если есть.

Шелдон
источник

Ответы:

261

Предполагая Python 3 (в Python 2 эта разница немного менее четко определена) - строка представляет собой последовательность символов, то есть кодовые точки Юникода ; это абстрактная концепция, и ее нельзя сохранить непосредственно на диске. Строка байтов представляет собой последовательность, что неудивительно, байтов - вещей, которые можно хранить на диске. Сопоставление между ними представляет собой кодировку - их довольно много (и бесконечно много возможно) - и вам необходимо знать, что применяется в конкретном случае для выполнения преобразования, поскольку другая кодировка может отображать одни и те же байты в другую строку:

>>> 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'
LVC
источник
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
391

Единственное, что может хранить компьютер - это байты.

Чтобы сохранить что-либо на компьютере, вы должны сначала закодировать его, то есть преобразовать в байты. Например:

  • Если вы хотите хранить музыку, вы должны сначала закодировать ее, используя 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'.

Кодирование и декодирование являются обратными операциями. Все должно быть закодировано, прежде чем оно может быть записано на диск, и оно должно быть декодировано, прежде чем оно может быть прочитано человеком.

Zenadix
источник
59
Zenadix заслуживает похвалы здесь. После нескольких лет работы в этой среде его первое объяснение пришло мне в голову. Я могу нанести татуировку на другую руку (на одной руке уже есть «Абсолютный минимум для каждого разработчика программного обеспечения, абсолютно, положительно должен знать о Юникоде и наборах символов (без извинений!)» Джоэла Спольски »
neil.millikin
4
Абсолютно блестящий. Ясный и легкий для понимания. Тем не менее, я хотел бы отметить, что эта строка - «Если вы напечатаете ее, Python представит ее как b« Я строка »», то верно для Python3, так как для Python2 байты и str - это одно и то же.
SRC
5
Я награждаю вас этой наградой за то, что вы предложили понятное человеку объяснение, чтобы внести ясность в эту тему!
Федорки 'ТАК прекрати вредить'
3
Отличный ответ. Единственное, что можно было бы добавить, - это более четко указать, что исторически программисты и языки программирования обычно явно или неявно полагали, что последовательность байтов и строка ASCII - это одно и то же . Python 3 решил явно нарушить это предположение, правильно ИМХО.
nekomatic
4
Ссылка на пост Джоэла, упомянутый @ neil.millikin выше: joelonsoftware.com/2003/10/08/…
Kshitij Saraogi
14

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

В питоне 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.

lmiguelvargasf
источник
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 - это согласованный формат для двоичного представления символов и различных видов форматирования (например, строчные / прописные буквы, новая строка, возврат каретки) и других «вещей» (например, смайликов). Компьютер не менее способен хранить представление в юникоде (последовательность битов) в памяти или в файле, чем для хранения представления ascii (другая серия битов) или любого другого представления (серии битов). ).

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

Поскольку юникод стремится представлять все возможные символы (и другие «вещи»), используемые в меж-человеческом и межкомпьютерном общении, ему требуется большее количество бит для представления многих символов (или вещей), чем в других системах представления, которые стремиться представлять более ограниченный набор символов / вещей. Чтобы «упростить» и, возможно, учесть историческое использование, представление в юникоде почти исключительно преобразуется в какую-то другую систему представления (например, 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))
'š'
Jeyekomon
источник
0

Языки Python включают в себя strи в bytesкачестве стандарта "Встроенные типы". Другими словами, они оба классы. Я не думаю, что стоит пытаться объяснить, почему Python был реализован таким образом.

Сказав это, strи bytesочень похожи друг на друга. Оба используют одни и те же методы. Следующие методы являются уникальными для strкласса:

casefold
encode
format
format_map
isdecimal
isidentifier
isnumeric
isprintable

Следующие методы являются уникальными для bytesкласса:

decode
fromhex
hex
fiftytwocards
источник