User avatar

Re: /newtover/1704094 («Кодировки и Питон») хочу во-первых сказать что «вызывают у них злобу и ощущение, что в питоне никак не могут починить какой-то назойливый баг, но им сейчас не до того, чтобы потратить свое время и сформулировать описание этого бага. Тем более баг довольно частый - наверняка, уже кто-то написал, а там всё починить не могут» это безупречно точная формулировка, во-вторых хочу добавить свои пять центов. Мой личный образовательный питч по поводу кодировок в legacy-системах выглядит так:

Comment

Начинаем с того, что да, рассказываем про два вида строк в языке (в моем случае это Perl, но конечно все рассуждение абсолютно языко-независимо) — октетные и юникодные. Показываем функцию, которая позволит их различить (в моем случае это Devel::Peek::Dump, обращаем внимание на флажок UTF8 (который на самом деле называется неправильно! и отдельно сбивает с толку).

 ‎· псы в рапиде
Comment

Далее напоминаем, что строки внутри программы появляются там из примерно пяти "внешних" мест: а) исходник б) файлы (и отдельно, кстати — имена файлов) в) результаты HTTP-запросов; г) значения query params и полей форм при POST-запросах; д) база данных. Рассказываем, что во всех этих местах тексты хранятся как бы в октетном виде, при этом зачастую они незаметно для пользователя перекодируются в unicode-строку драйвером БД, настройками локали, HTTP-библиотекой и прочим. Но иногда этого не происходит!

 ‎· псы в рапиде
Comment

Затем рассказываем, что есть несколько практических уровней мастерства при работе со строками текста: 1) взять строку из одного места и положить в другое (например, из БД в HTML-страницу); 2) конкатенировать две строки и как-то использовать результат конкатенации; 3) обращаться к отдельным элементам строки: в первую очередь это обращение к отдельным символам, также это получение длины строки, также это получение подстроки длины N по смещению M; 4) смена регистра строки — upper/lower/ucfirst; 4б) рассказ про канонические представления строк для нужд поиска — в общем случае надо использовать casefold, а также выбрать каноническое представление, чтобы обрабатывать текст и поисковый запрос в NFC/NFD; 5) дальше уже идут все истории про то, как обрабатываются человеческие языки — но если человеку это нужно, то ему уже эти рассказы не нужны.

 ‎· псы в рапиде
Comment

Теперь объясняем про то, почему все "иногда глючит": так как лучшие умы работали над тем, чтобы легаси-системы "зачастую" могли ничего не менять у себя, то многие вещи часто "просто работают", на самом деле они работают на честном слове. Например, I. если мы просто берем строку из базы и кладем её в HTML-страницу, то может оказаться так, что внутри программы она на самом деле представлена в октетном виде, но так как в HTML она пишется тоже в октетном виде, то все как бы работает, несмотря на то, что например если в программе посчитать её длину, то она будет неправильной. II. Также иногда на честном слове работает конкатенация, во-первых по предыдущей причине, а во-вторых по причине того, что если строка записана в ASCII, то она совместима и с октетными и с юникодными строками. Как только по какой-то причине туда попадает что-то не из ASCII, то баг мгновенно проявляется.

 ‎· псы в рапиде 1
Comment

Соответственно, рассказать про то, что в идеальном мире конвертация из внешних представлений в unicode должна происходить как можно ближе к периметру, но на практике может оказаться так, что придется создавать "пузыри", внутри которых как раз строки обрабатываются правильно, но живут в де-факто кривом виде в остальной системе. Одна из стратегий — писать параллельные функции, которые принимают строго unicode-строки и постепенно мигрировать на них систему, тестируя. Также четко рассказываем простые и надежные тест-кейсы: показываем как выглядит строка "Привет" в октетном (UTF-8) и в Unicode-виде (например, в виде \u-нотации); проверяем, что второй символ этой строки "р", а её длина — 6 букв. Для тестирования поиска пишем текст, в котором используем слово с умляутом в NFC, потом ищем его запросом, который записан в NFD и наоборот (также тестируем смену кейса). Вообще создаем в тестовой среде объектов, которые максимально используют Unicode — минимум русский, максимум — находим в интернете какое-нибудь изречение Лао-Цзы, например "ящик для говядины по-сычуаньски", и вписываем его иероглифами (реальными) во все поля которые можем найти.

 ‎· псы в рапиде 5
Comment

^ показываем, как протрейсить провенанс каждой строки от входа из базы до выхода в HTML, чтобы определить, в какой момент "ломается кодировка", и ищем там offending code.

 ‎· псы в рапиде
Comment

Как-то так!

 ‎· псы в рапиде
Comment

Золотые, бессмертные: https://github.com/minimaxir/big-list-of-naughty-strings/blob/master/blns.txt

 ‎· Smart social contract 1
Comment

@sorhed: это когда человек уже становится евангелистом. Мне больше нравится следующий уровень: https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt (открытка разработчикам Chrome btw).

 ‎· псы в рапиде 2
Comment

^ в Mozilla для представления подобного разработана кодировка WTF-8 (https://en.wikipedia.org/wiki/UTF-8#WTF-8)

 ‎· 9000 1
Comment

спасибо большое! вспомнил тут еще про два интересных случая: utf-8 BOM, к которому часто надо быть готовым, в питоне для его откусывания автоматического есть кодировка utf-8-sig. А еще перекодировки вот в такой конструкции: $ python -c 'print repr(u"М")' - она возвращает u'\xd0\x9c'. Последняя реально ломает мне мозг. UPD: чёрт, извиняюсь, думал, что последнее - это какая-то магия передачи параметров, а выяснилось, что это единственное место во втором питоне где на отсутствие указания кодировки не кидают SyntaxError. python -c $'#coding: utf-8\nprint repr(u"м")' возвращает всё правильно.

 ‎· лопата учёных из Омска 1

1 2 3 4 5 6 7 8 9 10