Почему представления байтов для расширенных символов ASCII из bytes() отличаются от chr()?

#python #utf-8 #byte #ascii

Вопрос:

(Я работаю на python)

Предположим, у меня есть этот список целых чисел

 a = [170, 140, 139, 180, 225, 200]  

и я хочу найти необработанное байтовое представление символа ASCII, с которым сопоставляется каждое целое число. Поскольку все они больше 127, они попадают в расширенный набор ASCII. Первоначально я использовал метод chr() в python, чтобы получить символ, а затем кодировать (), чтобы получить представление необработанных байтов.

 a_bytes = [chr(decimal).encode() for decimal in a]  

Используя этот метод, я увидел, что для чисел больше 127 соответствующий символ ASCII представлен 2 байтами.

 [b'xc2xaa', b'xc2x8c', b'xc2x8b', b'xc2xb4', b'xc3xa1', b'xc3x88']  

Но когда я использовал метод bytes (), оказалось, что у каждого символа был один байт.

 a_bytes2 = bytes(a) gt;gt;gt; b'xaax8cx8bxb4xe1xc8'  

Так почему же это отличается, когда я использую chr().encode() по сравнению с байтами()?

Комментарии:

1. Если вы не укажете кодировку, encode() будет использоваться UTF-8, который будет использовать два байта для этих символов. Попробуй encode('latin-1') .

2. Python не использует ни один из нескольких десятков наборов символов «расширенный ASCII». Он использует Юникод. chr() Функция выдает символ, соответствующий кодовой точке Юникода. Если вы вызовете encode() символ с кодовой точкой выше 127, вы получите кодировку по умолчанию, которая является UTF-8. Кодовые точки в диапазоне 128-255 могут иметь представление более одного байта в UTF-8. Если вы хотите увидеть однобайтовое представление, закодируйте кодовую точку, используя кодировку charmap, такую как Windows-1252, а не UTF-8.

Ответ №1:

Не существует такого понятия, как «Расширенный ASCII». ASCII определяется как байты (и кодовые точки) в диапазоне 0-127. Большинство стандартных однобайтовых кодовых страниц (которые используются для преобразования байтов в кодовые точки) используют ASCII для байтов 0-127, а затем сопоставляют 128-255 с тем, что удобно для кодовой страницы. Например, русские кодовые страницы сопоставляют эти байты с кириллическими кодовыми точками.

В вашем примере по .encode() умолчанию используется многобайтовая кодировка UTF-8, которая отображает кодовые точки 0-127 в ASCII и следует правилам многобайтового кодирования для любой кодовой точки выше 128. chr() преобразует целое число в соответствующую фиксированную кодовую точку Unicode.

Поэтому вам нужно выбрать подходящую кодировку, чтобы увидеть, что байт в этой кодировке представляет собой символ. Как вы можете видеть ниже, она варьируется:

 gt;gt;gt; a = [170, 140, 139, 180, 225, 200] gt;gt;gt; ''.join(chr(x) for x in a) # Unicode code points 'ªx8cx8b´áÈ' gt;gt;gt; bytes(a).decode('latin1') # ISO-8859-1, also matches first 256 Unicode code points. 'ªx8cx8b´áÈ' gt;gt;gt; bytes(a).decode('cp1252') # USA and Western Europe 'ªŒ‹´áÈ' gt;gt;gt; bytes(a).decode('cp1251') # Russian, Serbian, Bulgarian, ... 'ЄЊ‹ґбИ' gt;gt;gt; bytes(a).decode('cp1250') # Central and Eastern Europe 'ŞŚ‹´áČ' gt;gt;gt; bytes(a).decode('ascii') # these bytes aren't defined for ASCII Traceback (most recent call last):  File "lt;stdingt;", line 1, in lt;modulegt; UnicodeDecodeError: 'ascii' codec can't decode byte 0xaa in position 0: ordinal not in range(128)  

Кроме того, при отображении байтов Python по умолчанию отображает печатаемые символы ASCII в качестве символов и все остальное (непечатаемые управляющие символы и значения gt;127) в качестве escape-кодов:

 gt;gt;gt; bytes([0,1,2,97,98,99,49,50,51,170,140,139,180,225,200]) b'x00x01x02abc123xaax8cx8bxb4xe1xc8'