А не замахнуться ли нам на UTF-8 и прочий Unocode, головоломки с ASCII, Latin-1, cp1251, !chcp 65001, encode('cp1251'), unicodedata, codecs.open(filepath, encoding='cp1251')... и еще line.encoding, repr() - посмотреть кодировку, ord(),chr(),hex(),bin()
Две недели назад (всего-то) я столкнулся с проблемой: При вызове консольных команд Windows из Notebook iPythons вместо кириллицы крякозябыO
Тогда я понимал, что еще только учусь... Нарушать План обучения не хотелось, потому я довольствовался минимальным приемлемым результатом и торжественно пообещал изучить юникод позже.
Тогда я понимал, что еще только учусь... Нарушать План обучения не хотелось, потому я довольствовался минимальным приемлемым результатом и торжественно пообещал изучить юникод позже.
И вот, опять проблемы с кодировкой... И опять, попыка "по-быстрому" разобораться с кодировками ни к чему не привела. Пришлось угрохать два дня на чтение (последовательность чтения сохраняю):
1. Статьи на Хабре (ссылка под постом) Юникод для чайников / Хабрахабр
2. Википедии (ссылки под постом) Юникод
3. Главы 36 "Юникод и строки байтов" из М. Лутца
4. Документации Python Unicode HOWTO
4. Документации Python Standard Encodings
1. Статьи на Хабре (ссылка под постом) Юникод для чайников / Хабрахабр
2. Википедии (ссылки под постом) Юникод
3. Главы 36 "Юникод и строки байтов" из М. Лутца
4. Документации Python Unicode HOWTO
4. Документации Python Standard Encodings
Стоило ли тратить столько времени? Стоило, поскольку проблемы с кодировками могут выскочить при парсинге файлов... У меня проблемы возникали (только за эти две недели) следующие:
1. Крякозябы в консолях IDLE, iPpython при печати кириллицы
2. Крякозябы при импорте файла c кириллицей в iPython
3. Строки типа "022@Mail.Ru" или "41043244243e@Mail.Ru" при чтениистрок изфайл(ов)(ы)
1. Крякозябы в консолях IDLE, iPpython при печати кириллицы
2. Крякозябы при импорте файла c кириллицей в iPython
3. Строки типа "022@Mail.Ru" или "41043244243e@Mail.Ru" при чтениистрок изфайл(ов)(ы)
При этом в голове было... не то, чтобы девственно пусто..., я скорее обманывал себя, что знаю, что такое кодировка... Очевидно, что начинать надо с модели...
Но, как правило, начинают с истории кодировок. Но единства исторического и логического не наблюдается..., потому попробую начать с модели.
Но, как правило, начинают с истории кодировок. Но единства исторического и логического не наблюдается..., потому попробую начать с модели.
Модель "Наборщик в типографии"¶
Итак, я - наборщик. Передо мной касса (ящик) с буквами. Каждая буква в отдельной ячейке. Над каждой ячейкой этикетка, но на этикетке не буква, а номер ячейки. Вот, например, буква "w" имеет номер 119
In [11]:
ord("a"),ord("w") # "w" – байт с целочисленным значением 119 в ASCII
Out[11]:
In [14]:
chr(119) # буква в 119 ячейке - это w
Out[14]:
Но по ряду причин (подробности ниже) используюутся не десятичные, а шестнадцатиричный обозначения. Переведедем порядковые номера (ячеек в "ящиках" - наборных кассах) в шестнадцатиричную форму:
In [12]:
hex(97), hex(119) # число 119 в шестнацатиричной форме - это '0x77'
Out[12]:
In [17]:
'0x77', 0x77 # если мы зададим 0x77 без кавычек, то напечатано будет 119
Out[17]:
In [19]:
chr(119), chr(0x77)# буква в 119 ячейке - это w... и в 0x77 ячейке
Out[19]:
На самом деле все в двоичной форме (в памяти компьютера), а почему все используют шестнадцатиричную запись?¶
In [27]:
bin(1),bin(2),bin(3),bin(4),bin(7),bin(8),bin(127),bin(255)
Out[27]:
В памяти "железного" компьютера числа хранятся в виде последовательностей нулей и единиц. Это двоичный код. Чтобы попрактиковаться в переводе чисел в разные представления я вспомнил, что в windows есть программа "каклькулятор" (для программистов).
В строке примеров выше видно,что значениям 1,3,7,127, 255 соответствуют последовательности, состоящие только из единиц.
Если вспомнить, что 1 bit - это ячейка, в которой либо ноль, либо единица, то можно сказать (про 127 и 255), что максимальное число, которое можно записать при помощи 7бит - 127, а максимальное значение для 8бит - это 255.
В строке примеров выше видно,что значениям 1,3,7,127, 255 соответствуют последовательности, состоящие только из единиц.
Если вспомнить, что 1 bit - это ячейка, в которой либо ноль, либо единица, то можно сказать (про 127 и 255), что максимальное число, которое можно записать при помощи 7бит - 127, а максимальное значение для 8бит - это 255.
In [29]:
hex(127), hex(0b1111111),hex(255), hex(0b11111111)
Out[29]:
Некоторые сведения из жизни нумерологов:
В байте 8 бит - '0b11111111' и два символа [0,1], всех возможных сочетаний нулей и единиц в 8-битовой строчке только 256.
А в 16-ричной записи '0xff' симовлов 16 [0-9,a,b,c,d,e,f]; посредством всего двух символов можно записать все числа от 0 до 255 (всего 256)
В байте 8 бит - '0b11111111' и два символа [0,1], всех возможных сочетаний нулей и единиц в 8-битовой строчке только 256.
А в 16-ричной записи '0xff' симовлов 16 [0-9,a,b,c,d,e,f]; посредством всего двух символов можно записать все числа от 0 до 255 (всего 256)
Между делом мы освоили команды ord(),char(),hex(),bin(). Запомним и форматы '23'- десятичный, '0x7f'- шестнадцатиричный '0b1...'- двоичный. Обратите внимание, числа в любом формате в этих командах используются без кавычек.
Итак, для того, чтобы начать кодирование нам необходимы всего три вещи:
1. Набор символов (букв, цифр, знаков)
2. Ящик с пронумерованными ячейками (таких "разных ящиков" около сотни)
3. Соглашение о том, в какой ячейке какие символы (таблицы соответствия).
Все вместе можно назвать..., например кодировка 'cp1251'(названия "ящика").
1. Набор символов (букв, цифр, знаков)
2. Ящик с пронумерованными ячейками (таких "разных ящиков" около сотни)
3. Соглашение о том, в какой ячейке какие символы (таблицы соответствия).
Все вместе можно назвать..., например кодировка 'cp1251'(названия "ящика").
Если кодирование - это как набор текста, то откуда столько сложностей?¶
Сложностей хватает. А все из-за того, что поначалу для кодирования выделяли 1 байт (в ящике с 255 ячейками, поначалу использовали только 127 ячеек), потом стали использовать два байта, ... и так дело дошло до 8 байтов (8х8=64 разряда). И захотели сделать вместо компактных маленьких ящиков один большой для всех... Да не тут-то было ... (подробности в Википедии)
Или, вот пример из документации python unicode. The first encoding you might think of is an array of 32-bit integers. In this representation, the string “Python” would look like this:
P y t h o n 0x50 00 00 00 79 00 00 00 74 00 00 00 68 00 00 00 6f 00 00 00 6e 00 00 00 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
In [36]:
hex(ord('P')), hex(ord('y')), hex(ord('t')), hex(ord('h')), hex(ord('o')), hex(ord('n'))
Out[36]:
Здесь вместо 1байта используются 4, как много ненужных нулей... А можно ли использовать для кодирования последовательности байтов разной длинны? Например, для латиницы - однобайтовую кодировку, а для кириллицы - двухбайтовую?
Говоря техническим языком, преобразования между последовательностями байтов и строками обозначаются двумя терминами:
• Кодирование – процесс преобразования строки символов в последовательность простых байтов в соответствии с желаемой кодировкой.
• Декодирование – процесс преобразования последовательности байтов в строку символов в соответствии с желаемой кодировкой.
• Кодирование – процесс преобразования строки символов в последовательность простых байтов в соответствии с желаемой кодировкой.
• Декодирование – процесс преобразования последовательности байтов в строку символов в соответствии с желаемой кодировкой.
Правила кодирования для UTF-8¶
Для некоторых кодировок процесс преобразования тривиально прост – в кодировках ASCII и Latin-1, например, каждому символу соответствует единственный байт, поэтому фактически никакого преобразования не требуется. Для других кодировок процедура отображения может оказаться намного сложнее и порождать по несколько байтов для каждого символа. Вот описание UTF-8 из документации Python
UTF-8 is one of the most commonly used encodings. UTF stands for “Unicode Transformation Format”, and the ‘8’ means that 8-bit numbers are used in the encoding. (There’s also a UTF-16 encoding, but it’s less frequently used than UTF-8.)
UTF-8 uses the following rules:
If the code point is < 128, it’s represented by the corresponding byte value.
If the code point is between 128 and 0x7ff, it’s turned into two byte values between 128 and 255.
Code points >0x7ff are turned into three- or four-byte sequences, where each byte of the sequence is between 128 and 255.
UTF-8 uses the following rules:
If the code point is < 128, it’s represented by the corresponding byte value.
If the code point is between 128 and 0x7ff, it’s turned into two byte values between 128 and 255.
Code points >0x7ff are turned into three- or four-byte sequences, where each byte of the sequence is between 128 and 255.
На первый взгляд условия кажутся какими-то несуразными. Ну зачем использовать для двухбайтовых кодировок только байты с 128 по 255, а не все?
Ответ находится быстро - чтобы было легко по первому байту определить, добавлять ли к нему следующий(ие), т.е., отличать где однобайтовая кодировка, а где двух(много) байтовая.
Ответ находится быстро - чтобы было легко по первому байту определить, добавлять ли к нему следующий(ие), т.е., отличать где однобайтовая кодировка, а где двух(много) байтовая.
UTF-8 has several convenient properties:
It can handle any Unicode code point.
A Unicode string is turned into a string of bytes containing no embedded zero bytes. This avoids byte-ordering issues, and means UTF-8 strings can be processed by C functions such as strcpy() and sent through protocols that can’t handle zero bytes.
A string of ASCII text is also valid UTF-8 text.
UTF-8 is fairly compact; the majority of code points are turned into two bytes, and values less than 128 occupy only a single byte.
If bytes are corrupted or lost, it’s possible to determine the start of the next UTF-8-encoded code point and resynchronize. It’s also unlikely that random 8-bit data will look like valid UTF-8.
It can handle any Unicode code point.
A Unicode string is turned into a string of bytes containing no embedded zero bytes. This avoids byte-ordering issues, and means UTF-8 strings can be processed by C functions such as strcpy() and sent through protocols that can’t handle zero bytes.
A string of ASCII text is also valid UTF-8 text.
UTF-8 is fairly compact; the majority of code points are turned into two bytes, and values less than 128 occupy only a single byte.
If bytes are corrupted or lost, it’s possible to determine the start of the next UTF-8-encoded code point and resynchronize. It’s also unlikely that random 8-bit data will look like valid UTF-8.
Unicode Literals in Python Source Code¶
Прежде, чем переходить к конкретным примерам работы с объектом 'unicode', нам нужно освоить новые обозначения для этой кеодировки.
In Python source code, Unicode literals are written as strings prefixed with the ‘u’ or ‘U’ character: u'abcdefghijk'.
Specific code points can be written using the scape sequence, which is followed by four hex digits giving the code point. The escape sequence is similar, but expects 8 hex digits, not 4.
Unicode literals can also use the same escape sequences as 8-bit strings, including ,
but only takes two hex digits
so it can’t express an arbitrary code point. Octal escapes can go up to U+01ff, which is octal 777.
In Python source code, Unicode literals are written as strings prefixed with the ‘u’ or ‘U’ character: u'abcdefghijk'.
Specific code points can be written using the scape sequence, which is followed by four hex digits giving the code point. The escape sequence is similar, but expects 8 hex digits, not 4.
Unicode literals can also use the same escape sequences as 8-bit strings, including ,
but only takes two hex digits
so it can’t express an arbitrary code point. Octal escapes can go up to U+01ff, which is octal 777.
>>> s = u"a\xac\u1234\u20ac\U00008000" ... # ^^^^ two-digit hex escape ... # ^^^^^^ four-digit Unicode escape ... # ^^^^^^^^^^ eight-digit Unicode escape >>> for c in s: print ord(c), ... 97 172 4660 8364 32768
Однобайтовая кодировка Python ASCII¶
Проведем эксперимент с интерепретатором Python. По умолчанию он использует 7-битовую кодировку (!!!) для строк. Т.е. может адресоваться только к 127 ячейкам, в которых символы разложены в порядке, соответствующему кодировке ASCII. Но память выделяется байтами, а не битами... Значит остальные ячейки байта должны быть свободны...?
In [82]:
chr(31),chr(32),chr(48), chr(58),chr(64), chr(65),chr(90),chr(91),chr(94),chr(96), chr(97),chr(122), chr(126), chr(127)
Out[82]:
Как мы установили, при помощи одного байта (8 бит) можно (пронумеровать) кодировать 256 символов. Примеры выше показывают, что в кодировке по умочанию заняты толко номера с 32 по 126
Естественно, если мы обратимся к первому адресу во втором байте, то получим ошибку:
In [68]:
chr(256)
Пустые места в байте всем хотелось заполнить, потому появились варианты стандарта. Подробности можно посмотреть в википедии. ASCII
Однобайтовая кодировка Python Latin-1¶
У названия кодировки есть синонимы: "...‘latin-1’, ‘iso_8859_1’ and ‘8859’ are all synonyms for the same encoding"...
Как следует из названия, кодировка была ориентирована на охват европейских языков и была кодировкой по умолчанию в старых версиях Python:
"Versions of Python before 2.4 were Euro-centric and assumed Latin-1 as a default encoding for string literals"
Она, по видимому и подключается по умолчанию, когда интерпретатор пытается обработать шестнадцатиричные адреса, находящиеся за пределами диапазона ASCII...
Как следует из названия, кодировка была ориентирована на охват европейских языков и была кодировкой по умолчанию в старых версиях Python:
"Versions of Python before 2.4 were Euro-centric and assumed Latin-1 as a default encoding for string literals"
Она, по видимому и подключается по умолчанию, когда интерпретатор пытается обработать шестнадцатиричные адреса, находящиеся за пределами диапазона ASCII...
Здесь обнаруживается важный аспект понимания работы интерпретатора, который нужно понять: 1. Когда интерпретатор считывает шестнадцатиричные (hex) числа вида 0x1d, он трактует их, как одно (hex) число - один символ 2. Но в двухбайтовых кодировках символ кодируется двумя байтами, которые записываются двумя (hex) числами (например: 0x0d 0x1с) 3. Кириллица кодируется двумя байтами..., а интерпертатор считывает по байту (по пол-букве) Вот так и получаются крякозябы...
На многих форумах написано, что Питон 2.x не поддерживает операции кодирования-декодирования для строк. Это не точно. Смена кодировки для объекта "строка" возможна, но только на однобайтную. Вот доказательство - в методах объекта str есть decode(...),enecode(...)
In [147]:
help(str)
Объект unicode в Python 2.x¶
Полагаю, это плата за то, что Питон представляет строки посимвольными кортежами ... Но как быть с двухбайтовыми кодировками и юникодом? Для этого используют не объект "строка", а обект "юникод". Поначалу это решение кажется даже изящным... Но в третьем питоне объекты "строки" и "юникод" объединили, а еще ввели обекты "байт" и "массив байтов". Говорят, это сняло почти все проблемы с кодировками и крякозябами... Однако, нам надо научиться обращатся с объектом "Юникод"
In [3]:
help(unicode)
The unicode() constructor has the signature unicode(string[, encoding, errors]). All of its arguments should be 8-bit strings. The first argument is converted to Unicode using the specified encoding; if you leave off the encoding argument, the ASCII encoding is used for the conversion, so characters greater than 127 will be treated as errors
Если дословно, то "Первый аргумент конвертируется в Юникод с использованием специфицированной кодировки" ...формулировочка не очень понятная,.. может быть потому, что сбивает с толку статья на Хабре? (ссылка в начале поста)
Потому начнем с конца (в конце поста команды чтения из файла). Там все понятно. Файл имеет какую-то кодировку..., её нужно указать, и осуществится перекодировка в utf-8 (или utf-16, в зависимости от настроек по умолчанию) а здесь мы напечатали строку (на экране), в какой она кодировке? Если она в ASCII, то почему мы видим кирилицу?
Вот здесь я сцепил латиницу с кириллицей... и iPython Notebok, и Spyder ее показывают:
Потому начнем с конца (в конце поста команды чтения из файла). Там все понятно. Файл имеет какую-то кодировку..., её нужно указать, и осуществится перекодировка в utf-8 (или utf-16, в зависимости от настроек по умолчанию) а здесь мы напечатали строку (на экране), в какой она кодировке? Если она в ASCII, то почему мы видим кирилицу?
Вот здесь я сцепил латиницу с кириллицей... и iPython Notebok, и Spyder ее показывают:
In [167]:
sstr='q'+'ы'
print sstr
In [168]:
sstr, type(sstr)
Out[168]:
Очевидно, что выводом на экран командует Windows... Поэтому в консоли мы видим "ы"... Так или иначе, в интерпретатор поступает строка 'q18b' в формате ASCII, консоль забирает ее из интерпретатора и выводит на экран, а Windows в процессе вывода преобразует ее используя свою кодировку...
Таким образом, кодировка, для команды unicode может зависеть от настроек и особенностей работы IDLE, от настроек Windows, и... когда используем вызов командной строки из консоли ipython, например "!dir"... Со всеми этими вопросами еще предстоит разобратся, а здесь лишь заметим, что в документации черным по белому написано, что первый агумент - объект "строка" берется с экрана и конвертируется в Unicod (и не во что другое), а второй параметр - это кодировка строки...
Таким образом, кодировка, для команды unicode может зависеть от настроек и особенностей работы IDLE, от настроек Windows, и... когда используем вызов командной строки из консоли ipython, например "!dir"... Со всеми этими вопросами еще предстоит разобратся, а здесь лишь заметим, что в документации черным по белому написано, что первый агумент - объект "строка" берется с экрана и конвертируется в Unicod (и не во что другое), а второй параметр - это кодировка строки...
In [164]:
s=unicode(sstr,'utf-8')
In [165]:
s
Out[165]:
In [166]:
print s
Что мы только что сделали? Мы взяли объект "строка" 'q18b'
и конвертировали его в объект "юникод" u'q44b'
Интерпретатор обрабатывает эти два набора символов по разному. И обозначения в первом случае - шестнадцатиричные послеодвательности, а во втором - послеовательности для Юникода (подробности в документации)
и конвертировали его в объект "юникод" u'q44b'
Интерпретатор обрабатывает эти два набора символов по разному. И обозначения в первом случае - шестнадцатиричные послеодвательности, а во втором - послеовательности для Юникода (подробности в документации)
In []:
Посмотрим, как можно преоразовывать и распечатывать символы юникода
In [182]:
ord(u'\u044b')
Out[182]:
In [189]:
unichr(1099)
Out[189]:
In [188]:
print unichr(1099)
Есть еще вот такая библиотека, подроности в документации Unicode HOWTO
In [190]:
import unicodedata
u = unichr(233) + unichr(0x0bf2) + unichr(3972) + unichr(6000) + unichr(13231)
for i, c in enumerate(u):
print i, '%04x' % ord(c), unicodedata.category(c),
print unicodedata.name(c)
# Get numeric value of second character
print unicodedata.numeric(u[1])
Здесь, помимо основных функций, хороший пример использования enumerate(u)... И конечно же интересно, что еще моежет класс unicodedata
In [191]:
help(unicodedata)
Далее надо было бы ознакомится с таблицами кодировок... Но нельзя объять необъятное. Важнее тема о конвертации файлов.
Как конвертировать файлы?¶
Для примера возьмем файл, который был записан при работе программы-парсера на Python. При открытии файла виден список строк, в первой и третьей есть символы кириллицы, но вместо них в файле мы находим шеснадцатириные последоватлености:
In [174]:
filepath="C:\\Users\\kiss\\Documents\\IPython Notebooks\\web\\Spyder\\mail\\mail_pages\\0.csv"
f = open(filepath)
In [175]:
f.readlines()
Out[175]:
Запрос кодировки выдает "None", попытка конвертации приводит к ошибке... Да еще и объект не читается. А почему? Потому что объект "string" однобайтовый...
In [179]:
f.encoding, f.readlines()
Out[179]:
Но есть волшебная библиотека, которая конвертирует объект 'string' в объект 'unocode'
In [180]:
import codecs
f1 = codecs.open(filepath, encoding='cp1251')
for line in f1:
print line, repr(line)
Здесь каждая строка печатается два раза, потому, что мы так задали print line, repr(line)
Но что это за чудеса, оказалось, что файл на диске в кодировке 'cp1251'. Пока это не понятно, чуть выше при конвертации с экрана использовали UTF-8.
In [181]:
f1.encoding
Out[181]:
По сути объект юникода - это парсер строки, который производит замену ее некоторых частей:
In [199]:
# из Out[180] первая строчка с кириллицей из файла с кодировкой 'cp1251'
print '"\u0410\u0432\u0442\u043e@Mail.Ru";85837;"http://auto.mail.ru"\n'
А теперь превратим строку в объект юникода - добавим 'u'
In [200]:
# из Out[180] первая строчка с кириллицей из файла с кодировкой 'cp1251'
print u'"\u0410\u0432\u0442\u043e@Mail.Ru";85837;"http://auto.mail.ru"\n'
Итак, почти все ясно, остался, пожалуй, вопрос о том, как читать кириллицу из консоли Windows... Но об этом в другой раз, ...хотя оставим здесь небольшой "задел" для изучения sys и os... они нам понадобятся не только для получения информации о кодировках..., но и для перехвата потоков входных и выходных данных.
In [122]:
import sys
sys.getfilesystemencoding()
Out[122]:
Windows uses a configurable encoding; on Windows, Python uses the name “mbcs” to refer to whatever the currently configured encoding is.Unicode HOWTO
In [48]:
!chcp
In [140]:
sys.getdefaultencoding()
Out[140]:
In [141]:
help(sys)
Посты чуть ниже также могут вас заинтересовать
На этот пост я угрохал дней пять. Сначала гордился тем, что прочитал две сотни страниц на двух языках всего за два дня... Но потом выяснилось, что эти страницы еще нужно уложить в голове, иначе получится ..." чукча не читатель, чукча-писатель"
ОтветитьУдалитьВ целом, написание поста помогло. Я не просто законспектировал процесс изучения, а сначала плохо законспектировал, а потом переписал все набело..., но до хорошей статьи все-таки не дотянул...
Чтобы статья была хорошей, нужно мастерство, а я только учусь..., остается конспект..., но с праутикумами - упражнениями
Пожалуй, конспект сразу надо писать набело, а для экспериментов открывать второй файл Notebook
Nice blog it is informative thank you for sharing Python Online Training
ОтветитьУдалитьотличный пост спасибо вам огромное
ОтветитьУдалить