"Широкий символ в записи подпрограммы" - кириллические слова в кодировке UTF-8 как последовательность байтов

Я работаю над игрой слов в Android с большим словарем -

скриншот приложения

Слова (более 700 000) хранятся в виде отдельных строк в текстовом файле (а затем помещаются в базу данных SQLite).

Чтобы конкуренты не могли извлечь мой словарь, я бы хотел закодировать все слова длиной более 3 символов с помощью md5. (Я не запутываю короткие слова и слова редкими русскими буквами ъ а также эпотому что я хотел бы перечислить их в моем приложении).

Итак, вот мой скрипт, который я пытаюсь запустить с Perl v5.18.2 на Mac Yosemite:

#!/usr/bin/perl -w

use strict;
use utf8;
use Digest::MD5 qw(md5_hex);

binmode(STDIN, ":utf8");
#binmode(STDOUT, ":raw");
binmode(STDOUT, ":utf8");

while(<>) {
        chomp;
        next if length($_) < 2; # ignore 1 letter junk
        next if /жы/;           # impossible combination in Russian
        next if /шы/;           # impossible combination in Russian

        s/ё/е/g;

        #print "ORIGINAL WORD $_\tENCODED WORD: ";

        if (length($_) <= 3 || /ъ/ || /э/) { # do not obfuscate short words
                print "$_\n";                # and words with rare letters
                next;
        }

        print md5_hex($_) . "\n";            # this line crashes
}

Как вы можете видеть, я должен использовать буквы кириллицы в исходном коде моего скрипта Perl - вот почему я поставил use utf8; на его вершине.

Однако моя настоящая проблема заключается в том, что length($_) сообщает о слишком высоких значениях (вероятно, сообщает количество байтов вместо количества символов).

Итак, я попытался добавить:

binmode(STDOUT, ":raw");

или же:

binmode(STDOUT, ":utf8");

Но сценарий затем умирает с широким символом в записи подпрограммы в строке с print md5_hex($_),

Пожалуйста, помогите мне исправить мой сценарий.

Я запускаю это как:

perl ./generate-md5.pl < words.txt > encoded.txt

и вот пример данных words.txt для вашего удобства:

а
аб
абв
абвг
абвгд
съемка

4 ответа

Решение

md5_hex ожидает строку байтов для ввода, но вы передаете декодированную строку (строку кодовых точек Unicode). Явно закодировать строку.

use strict;
use utf8;
use Digest::MD5;
use Encode;
# ....
# $_ is assumed to be utf8 encoded without check
print Digest::MD5::md5_hex(Encode::encode_utf8($_)),"\n";
# Conversion only when required:
print Digest::MD5::md5_hex(utf8::is_utf8($_) ? Encode::encode_utf8($_) : $_),"\n";

Моя настоящая проблема в том, что длина ($_) сообщает о слишком высоких значениях

Да, вы читаете из ARGV дескриптор файла и не установил его кодировку в UTF-8

Вы можете использовать open Прагма, чтобы исправить это. Вместо всех ваших binmode заявления, использовать

use open qw/ :std :encoding(utf8) /;

который изменит режим открытия по умолчанию для всех файловых дескрипторов, включая стандартные, на :encoding(utf8)

если вы используете Mojolicious, замените to_json на encode_json, это решит проблему.

Из документации модуля JSON ключевое слово to_json: если вы хотите написать современный код Perl, который взаимодействует с внешним миром, вы должны использовать encode_json (предполагается, что данные JSON закодированы в UTF-8). и я не могу предвидеть мир без UTF-8.

если вы используете Perl версии 5.0 и выше, это можно решить, изменив to_json на encode_json

Другие вопросы по тегам