Comments 19
Не перехожу на критику, поскольку не знаю внутренней кухни, однако всё же спрошу — что мешало сделать на Unicode изначально? Думается, было бы проще все сделать по-человечески с самого начала. Или же для этого были свои причины?
+4
Причина, думаю, у большинства проектов, использующих морально устаревшие кодировки, одна: рождение проекта (кодовой базы) в те времена, когда использование многобайтовых кодировок требовало слишком много усилий, а эффект от этого в пределах Рунета бал минимальный. У нас в коде до сих пор встречаются строки, датированные 2000 годом :)
+6
Поправьте меня, если я ошибаюсь, но мне казалось, что МойМир зародился где-то в районе 2005-2006 годов — по меньшей мере, я не помню, чтобы он существовал сильно раньше этих дат. Или же он просто разрабатывался всё это время, начиная с самого начала нулевых?
0
Невозможно по причине того, что максимальная длина ключа индекса в MySQL 767 байт, и индексы (особенно многоколоночные) перестают помещаться.
С версии 5.5.37 (кажется) можно указать в конфигурации:
innodb_large_prefix = ON
innodb_file_per_table = ON
innodb_file_format = Barracuda
Это позволяет хранить в индексе до 3072 байт
Кодировка utf8 в MySQL не умеет хранить 4-х байтные символы unicode, а, например, стандратные смайлы на стандартной клавиатуре в андроиде, успешно их отправляют пачками. Выход — перейти на кодировку utf8mb4 (поддерживается с версии 5.5.3)
Подробнее о проблеме можно почитать тут code.djangoproject.com/ticket/18392#comment:16
+3
Насчет увеличения длины ключа индекса думали, но решили, что не хотим увеличивать без лишней необходимости: все-таки индекс всегда съедает память по максимальному варианту. Пришлось, конечно, повозиться и убедиться в том, что наши строки уникальны на выбранную длину, и конфликтов на уникальных индексах не возникает.
Что касается utf8mb4, согласен, это неприятная проблема. Когда выбирали между utf8mb3 и utf8mb4, мы посчитали, что можем пожертвовать редкими языками в пользу экономии памяти в индексах. Но, к сожалению, не подумали про emoji. Хорошо, что в MySQL у нас почти что нет данных, в которых применение emoji было бы особенно критично (диалоги, лента, комментарии хранятся в специализированных хранилищах). Спасибо, что обратили на это внимание, мы проведем исследование на тему того, насколько это может быть критично для нас, и, возможно, придется принять меры.
Что касается utf8mb4, согласен, это неприятная проблема. Когда выбирали между utf8mb3 и utf8mb4, мы посчитали, что можем пожертвовать редкими языками в пользу экономии памяти в индексах. Но, к сожалению, не подумали про emoji. Хорошо, что в MySQL у нас почти что нет данных, в которых применение emoji было бы особенно критично (диалоги, лента, комментарии хранятся в специализированных хранилищах). Спасибо, что обратили на это внимание, мы проведем исследование на тему того, насколько это может быть критично для нас, и, возможно, придется принять меры.
+3
что в MySQL у нас почти что нет данных, в которых применение emoji было бы особенно критично
Да да, а потом оно там внезапно появляется и это в зависимости от архитектуры приложения может привести к чему угодно
0
В MySQL есть возможность указывать используемую кодировку для каждой конкретной колонки, поэтому для совсем новых данных это решается очень просто:
(и, разумеется, set names utf8mb4 вместо set names utf8)
Если вдруг символы с четырехбайтовым представлением в UTF-8 появляются в колонке, которая определена как utf8mb3, то такой символ просто заменяется на знак вопроса (при условии set names utf8mb4, если используется utf8mb3, то строка внезапно обрежется по такому символу).
alter table `my_table` add column `my_column` varchar(n) character set utf8mb4;
(и, разумеется, set names utf8mb4 вместо set names utf8)
Если вдруг символы с четырехбайтовым представлением в UTF-8 появляются в колонке, которая определена как utf8mb3, то такой символ просто заменяется на знак вопроса (при условии set names utf8mb4, если используется utf8mb3, то строка внезапно обрежется по такому символу).
0
Теперь понятно почему в аське от многих контактов не приходят смайлы а рисуются квадратики… конечно, оф.клиент их поддерживает а все остальные в очередной раз выброшены заборт.
+1
Это один из вариантов. Второй, вероятный вариант, использование смайликов, которые появились в последних версиях Unicode (7.0.0, например, добавляет приличное их количество) и которых нету в установленных на системе шрифтах. У меня, например, половина таблиц на странице en.wikipedia.org/wiki/Emoji в квадратиках. Скорее всего это решается установкой/обновлением пакетов с шрифтами.
+1
Но тогда это будут черно-белые смайлы-символы, причем такие какими их нарисовал разработчик шрифта. Куда там анимированные или специфические кастомные смайлы…
Печально. Перешел по ссылке, только 3 таблицы в смайлах и те не до конца…
Печально. Перешел по ссылке, только 3 таблицы в смайлах и те не до конца…
0
Квадратики рисуются скорее всего от того, что в шрифте, которым они отображаются, нет нужных символов.
0
Это как раз тот случай, когда несколько первых фраз вызывают сильнейшее желание немедленно прикрыть ладонью лицо.
-12
Не понял я про Perl часть, как всёже всё произошло.
вот есть у вас кусок кода, который зависит от того, в какой кодировке данные. Например, по бизнес-логике пишет отчёт на диск, в кодировке UTF-8
Было в CP1251:
Нужно после перехода на Unicode:
в какокй момент один исходник поменялся на другой?
(это просто пример с записью в файл, может быть любая другая операция, md5 от строки с русскими буквами, вызов ord, сериализация в какой-нибудь формат)
вот есть у вас кусок кода, который зависит от того, в какой кодировке данные. Например, по бизнес-логике пишет отчёт на диск, в кодировке UTF-8
Было в CP1251:
open my $f, ">", "report.file";
print $f from_to($data, 'cp1251', 'utf8');
close $f;
Нужно после перехода на Unicode:
open my $f, ">:encoding(UTF-8)", "report.file";
print $f $data;
close $f;
в какокй момент один исходник поменялся на другой?
(это просто пример с записью в файл, может быть любая другая операция, md5 от строки с русскими буквами, вызов ord, сериализация в какой-нибудь формат)
0
Совсем ничего не делать в таком случае не получилось. Мы завели функцию, которая в зависимости от конфигурации сервера (cp1251 или UTF-8) принимала решение о том, как именно перекодировать. Заменили все такие явные преобразования в коде (к счастью, их было не так много, в пределах 200, — это посильная задача), и только после этого стали переключать.
И ниже, собственно как эти функции реализованы (на самом деле их десяток на все случаи жизни). Сразу оговорюсь по стилю: отсутствие копирования входных параметров и оператора return — принесено в жертву производительности — это разумно для однострочных частовызываемых функций; модификация аргумента in-place и возвращаемый значения — для совместимости с utf8::encode/decode, в будущем это позволит удалить костылики почти не задумываясь.
open my $f, ">", "report.file";
my::utf8::encode_any($data, 1);
print $f $data;
close $f;
И ниже, собственно как эти функции реализованы (на самом деле их десяток на все случаи жизни). Сразу оговорюсь по стилю: отсутствие копирования входных параметров и оператора return — принесено в жертву производительности — это разумно для однострочных частовызываемых функций; модификация аргумента in-place и возвращаемый значения — для совместимости с utf8::encode/decode, в будущем это позволит удалить костылики почти не задумываясь.
sub enabled () { $utf8_enabled }
my $UTF8_ENC = Encode::find_encoding('UTF-8'); # Use strict version of UTF-8 to prevent invalid data
my $CP1251_ENC = Encode::find_encoding('cp1251');
if (enabled) {
*encode_any = sub ($$) { $_[0] = Encode::encode($_[1] ? $UTF8_ENC : $CP1251_ENC, $_[0]); () };
*decode_any = sub ($$) { $_[0] = Encode::decode($_[1] ? $UTF8_ENC : $CP1251_ENC, $_[0]); 1 };
} else {
*encode_any = sub ($$) { Encode::from_to($_[0], $CP1251_ENC, $UTF8_ENC) if $_[1]; () };
*decode_any = sub ($$) { Encode::from_to($_[0], $UTF8_ENC, $CP1251_ENC) if $_[1]; 1 };
}
+3
Очень интересно. Мы тоже переходим на unicode, и у нас Perl часть, похоже, точно такая же как у вас
(начиная с sub enabled () { $utf8_enabled }). Есть моё выступление на YAPC Russia про этот переход youtu.be/43vDtaKl71c ( и это единственное место
где описание процесса лежит в паблике, больше дать нечего)
У нас как раз таких явных преобразований (которых у вас в перделах 200) много. Каждый исходник отдельно переводится «на юникод»
т.е. добавляются эти преобразования (при этом сам код конвертируется в UTF-8, ставится use utf8, и любую строковую константу
тоже нужно обработать такими преобразованиями, перед любым не-ASCII регэкспом их тоже нужно выполнить над исходными данными).
Получается перед любым вызовом внешнего модуля нужно решать нужно ли такие преобразование или нет.
К ним относятся JSON/YAML/итд модули, md5/sha хэши и пр, модули ввода-вывода (т.е. там где текст превращается в бинарные данные):
LWP/другие http(s) библиотеки, любые библиотеки по работе с протоколами, либые print, say, syswrite, pack и пр.
Хранилища впринципе тоже — memcached,redis.
Нам тоже пришлось разделить данные на бинарные и текстовые перед работой с ними, т.к. используем больше Redis, и там нет такой фичи, как в Memcached.
Вот меня и удивляет что у вас таки преобразований меньше.
Как удалось обойтись только 200 местами, может специфика работы кода не предполагает обилие конвертации форматов, регэкспов и ввода-вывода?
Ещё, если не секрет, сколько строчек кода и сколько человек в течение какого срока занимались Perl частью?
(начиная с sub enabled () { $utf8_enabled }). Есть моё выступление на YAPC Russia про этот переход youtu.be/43vDtaKl71c ( и это единственное место
где описание процесса лежит в паблике, больше дать нечего)
У нас как раз таких явных преобразований (которых у вас в перделах 200) много. Каждый исходник отдельно переводится «на юникод»
т.е. добавляются эти преобразования (при этом сам код конвертируется в UTF-8, ставится use utf8, и любую строковую константу
тоже нужно обработать такими преобразованиями, перед любым не-ASCII регэкспом их тоже нужно выполнить над исходными данными).
Получается перед любым вызовом внешнего модуля нужно решать нужно ли такие преобразование или нет.
К ним относятся JSON/YAML/итд модули, md5/sha хэши и пр, модули ввода-вывода (т.е. там где текст превращается в бинарные данные):
LWP/другие http(s) библиотеки, любые библиотеки по работе с протоколами, либые print, say, syswrite, pack и пр.
Хранилища впринципе тоже — memcached,redis.
Нам тоже пришлось разделить данные на бинарные и текстовые перед работой с ними, т.к. используем больше Redis, и там нет такой фичи, как в Memcached.
Вот меня и удивляет что у вас таки преобразований меньше.
Как удалось обойтись только 200 местами, может специфика работы кода не предполагает обилие конвертации форматов, регэкспов и ввода-вывода?
Ещё, если не секрет, сколько строчек кода и сколько человек в течение какого срока занимались Perl частью?
+1
Каждый исходник отдельно переводится «на юникод» т.е. добавляются эти преобразования (при этом сам код конвертируется в UTF-8, ставится use utf8, и любую строковую константу тоже нужно обработать такими преобразованиями, перед любым не-ASCII регэкспом их тоже нужно выполнить над исходными данными).
Именно чтобы избежать этого мы и использовали перловые фильтры. Более того, забыл про это написать, мы вместо use utf8 использовали use my::utf8. Особенность нашей прагмы как раз в том, что она применяется условно, в зависимости от той самой константы enabled. Чтобы не быть голословным, приведу еще кусочек кода из этого модуля на эту тему (source_in_utf8 меняется с 0 на 1 при конвертации git-репо, это делается уже после переключения на utf8):
package my::utf8;
use Filter::Util::Call;
sub source_in_utf8 () { 0 }
sub enabled () { $utf8_enabled }
my %FILTERING;
my $UTF8_ENC = Encode::find_encoding('UTF-8'); # Use strict version of UTF-8 to prevent invalid data
my $CP1251_ENC = Encode::find_encoding('cp1251');
sub import {
my ($class, %args) = @_;
my $filename = ($args{level} ? caller($args{level}) : caller())[1];
if (source_in_utf8 && enabled) {
goto &utf8::import;
} elsif (source_in_utf8) {
unless ($FILTERING{$filename}) {
filter_add(sub {
my $status = filter_read();
Encode::from_to($_, $UTF8_ENC, $CP1251_ENC) if $status > 0;
return $status;
});
$FILTERING{$filename} = 1;
}
} elsif (enabled) {
unless ($FILTERING{$filename}) {
filter_add(sub {
my $status = filter_read();
Encode::from_to($_, $CP1251_ENC, $UTF8_ENC) if $status > 0;
return $status;
});
$FILTERING{$filename} = 1;
}
goto &utf8::import;
}
return;
}
sub unimport {
my ($class, %args) = @_;
my $filename = ($args{level} ? caller($args{level}) : caller())[1];
if (source_in_utf8 && enabled) {
goto &utf8::unimport;
} elsif (source_in_utf8) {
filter_del();
delete $FILTERING{$filename};
} elsif (enabled) {
filter_del();
delete $FILTERING{$filename};
goto &utf8::unimport;
}
return;
}
Вот меня и удивляет что у вас таки преобразований меньше.
Как удалось обойтись только 200 местами, может специфика работы кода не предполагает обилие конвертации форматов, регэкспов и ввода-вывода?
К счастью, у нас весь ввод/вывод большей частью сосредоточен в нескольких местах: ввод/вывод веб-сервера (разбор get/post параметров, шаблонизатор, аяксы, апишка), клиентики к хранилищам, взаимодействие с внешними системами.
сколько человек в течение какого срока занимались Perl частью?
Собственно perl-часть заняла не так много времени, если посчитать время всех задействованных разработчиков, то вряд ли больше шести-восьми человеконедель. Точно сложно сказать, так как чисто перловых задач было всего несколько, а большинство правок было связано с необходимостью обеспечить плавное и постепенное переключение хранилищ в другую кодировку.
+1
Sign up to leave a comment.
Практическое руководство по Unicode'изации