В 2000-х определилась тенденция переводить проекты в национальных кодировках в utf-8. Однако не везде их перевели одним махом, а решили рубить собаке хвост постепенно. В результате во многих проектах часть файлов c кодом в utf-8, а часть осталась в национальной кодировке (например, cp1251).
Поэтому я сделал утилиту ru-perltidy, которая определяет кодировку файлов, конвертирует в utf-8, а после форматирования переводит обратно.
Из вкусносей тут то, что ru-perltidy может отформатировать только изменённые в репозитории git файлы (Рис.1).

Если же файлы были закомичены: не беда — укажите опцию —in-branch и ru-perltidy отформатирует все изменённые файлы в ветке воспользовавшись командой :
git diff --name-only --diff-filter=AM origin/master...
Имеет команда и несколько иных опций:
# Отформатировать указанные файлы perltidy: $ ru-perltidy file1 file2 # Указать кодировку: $ ru-perltidy file1 file2 -e utf-8,cp1251 # Форматирует только изменённые файлы в репозитории git: $ ru-perltidy # Форматирует изменённые файлы в ветке # (на случай, если забыл отформатировать перед комитом): $ ru-perltidy --in-branch # Указать расширения файлов: $ ru-perltidy --ext 'pl,pm,'
Впрочем может понадобится и другая операция, а не только perltidy. Поэтому я добавил в пакет утилиту ru-utf8. Она аналогична ru-perltidy, но требует указания обязательной опции -с в которой передаётся команда для обработки переведённых в utf-8 файлов. А после выполнения команды файлы переписывается обратно, в той же кодировке, в которой и были.
# Перевести файлы во временные в кодировке utf-8 (в /tmp) и после выполнения команды # и их изменения переписать обратно в определённой кодировке: # (тут $1 - первый файл, $2 - второй и т.д., $* - все файлы через пробел. # Так же работают подстановки ${1} и т.д.) $ ru-utf8 file1 file2 -c 'perltidy $1 -st > /tmp/1 && mv /tmp/1 $1'
Алгоритм определения кодировки файла
Для определения кодировки файла используется следующий алгоритм:
-
Файл считывается и декодируется в указанных кодировках (по-умолчанию это utf-8, cp1251 и koi8-r).
-
Затем в декодированных вариантах текста подсчитываются длины русских слов начинающиеся с прописной или строчной буквы и далее из строчных.
-
Длины русских слов не подпадающих под указанный критерий вычитаются из результата.
-
Набравший наибольший балл декодированный результат и будет правильным.
Вот реализация этого алгоритма на perl:
#@category Кодировка # Определяет кодировку. # В koi8-r и в cp1251 большие и малые буквы как бы поменялись местами, # поэтому у правильной кодировки вес будет больше sub bohemy($) { my ($s) = @_; my $c = 0; while ( $s =~ /[а-яё]+/gi ) { my $x = $&; if ( $x =~ /^[А-ЯЁа-яё][а-яё]*$/ ) { $c += length $x } else { $c -= length $x } } $c; } # Определить кодировку и декодировать sub decode(@) { my ( $octets, $encodings ) = @_; return if !length $octets; utf8::encode($octets) if utf8::is_utf8($octets); $encodings //= [qw/utf-8 cp1251 koi8-r/]; my @x = grep length $_->[0], map { # TODO: В случае ошибки Encode::decode помещает пустую строку в свой # второй аргумент. Сейчас это исправлено копированием значения в # дополнительную переменную, но было бы неплохо разобраться в причине. my $save = $octets; eval { [ Encode::decode( $_, $save, Encode::FB_CROAK ), $_ ] }; } @$encodings; my ( $unicode, $mem_encoding ); ( $unicode, $mem_encoding ) = @{ $x[0] } if @x == 1; if ( @x > 1 ) { ( $unicode, $mem_encoding ) = @{ ( sort { bohemy( $b->[0] ) <=> bohemy( $a->[0] ) } @x )[0] }; } wantarray ? ( $unicode, $mem_encoding ) : $unicode; }
Заключение
Во время разработки у нас накапливается множество мест за которыми приходится следить и исправлять их рутинными, но трудоёмкими действиями. Поэтому, если такие места невозможно, по каким-то причинам, убрать из проекта, то лучше слежку и связанные с ними действия автоматизировать.
Ссылки
-
Модуль Octets::To::Unicode на CPAN / https://metacpan.org/pod/Octets::To::Unicode.
-
ru-perltidy на github / https://github.com/darviarush/perl-octets-to-unicode.
ссылка на оригинал статьи https://habr.com/ru/post/664308/
Добавить комментарий