LINUX.ORG.RU

Perl. Выравнивание строк UTF8 и запись их в файл с помощью sprintf.

 ,


0

1

Доброго времени суток!

У меня есть скрипт на Perl, который должен выравнивать строки и записывать их в файл, в UTF8. Для этого использую sprintf.

Часть скрипта представлена ниже:

#!/usr/bin/perl
use strict;
use utf8;
use locale;
use warnings;
...
my $length_sv = 9;
open my $out, '>>:encoding(UTF-8)', "filename" or warn "Could not open file - $!" and exit(1);
my ($tid, $cid, $v3, $l, $v5, $sub) = $_ =~ /^\{"id":(\d+),"customer_id":(\d+)(.*?)_login":"(\w{1,10})"(.*?)"subject":"(.*?)"/;
my $subc = substr($sub, 0, $length_sv);
say $subc;
my $string = sprintf "| %-5s | %-1s | %-9s | %-${length_sv}s | %-11s | %-10s|","$time","$num","$tid","$subc","$cid","$l";
say $string;
say $out $string;
close $out;

После запуска скрипта в STDOUT получаю правильный вывод, такой, как должен записаться в файл:

Тест Mark
| 11:00 | 1 | 1234567   | Тест Mark | 10101012      | login   |

Но в файл запись происходит с ошибкой (в столбце с кириллицей):

$ cat filename
| 11:00 | 1 | 1234567   | ТеÑÑ Mark | 10101012      | login   |

В файл должен записаться столбец Тест Mark, но этого не происходит, при этом та же самая строка в STDOUT с помощью say или printf выводится корректно.

Я пробовал добавлять:

binmode($out,':utf8');

К сожалению, это не помогло. Я не совсем понимаю, из-за чего это происходит, ведь при записи в $out указывается явно кодировка UTF-8.

Подскажите, пожалуйста, можно как-то исправить это?

Подсказка

$ echo $LANG
ru_RU.UTF-8

$ echo "Тест" | iconv -f latin1
ТеÑÑ

gremlin_the_red ★★★★★ ()

не касаясь сабжа, но уж для перла лучше приводить целый рабочий кусок, где у тебя затык, а не "...", кто его знает, что там.
Вообще, не читал, но у перла внутреннее представление о юникуде, откуда и во что потом делаешь просто внимательней смотри.

madcore ★★★★★ ()
Ответ на: комментарий от paran0id

К сожалению, не помогло.

Также пробовал добавлять:

use feature     qw< unicode_strings >;

Безрезультатно.

ameame2021 ()

По коду всё ок и должно работать как надо. То есть ты о чём-то не договариваешь +).

vitus@vitus-home:~$ cat /tmp/ll.pl
#!/usr/bin/perl
use strict;
use utf8;
use locale;
use warnings;
binmode(STDOUT,':utf8');
binmode(STDIN,':utf8');

open my $in, '<:encoding(UTF-8)', '/tmp/in.txt' or die('error');

my $ff=<$in>;
my $length_sv = 9;
open my $out, '>>:encoding(UTF-8)', "/tmp/file.txt" or warn "Could not open file - $!" and exit(1);

my $subc = substr($ff, 0, $length_sv);

my $string = sprintf "| %-5s | %-1s | %-9s | %-${length_sv}s | %-11s | %-10s|\n","ыфыф","ыфыфыф","ыфыфыфыф","$subc","ыфыфыфы","фыфыфыф";

print $string;
print $out $string;

close $out;
close $in;
vitus@vitus-home:~$ echo -n "Тест Mark" > /tmp/in.txt
vitus@vitus-home:~$ /tmp/ll.pl
| ыфыф  | ыфыфыф | ыфыфыфыф  | Тест Mark | ыфыфыфы     | фыфыфыф   |
vitus@vitus-home:~$ cat /tmp/file.txt 
| ыфыф  | ыфыфыф | ыфыфыфыф  | Тест Mark | ыфыфыфы     | фыфыфыф   |
vtVitus ★★★★★ ()
Последнее исправление: vtVitus (всего исправлений: 1)
Ответ на: комментарий от vtVitus

По коду всё ок и должно работать как надо.

Вроде бы, да. Но по факту, так не работает.

Я пробовал создать второй скрипт, читать из файла и писать в файл, как ты показал на своем примере (пытался воспроизвести и понять, из-за чего ошибка). Во втором скрипте, как и у тебя, запись в файл проходила успешно в UTF8.

То есть ты о чём-то не договариваешь +).

Возможно, эта информация поможет, но может нет. В исходном примере скрипта строка $_ читается не из файла, а из внешней программы, я выполняю curl к удаленному ресурсу, получаю JSON, разбиваю его на элементы массива, после чего в цикле работаю с ними таким образом:

#!/usr/bin/perl
use strict;
use utf8;
use locale;
use warnings;
...
my $curl = qq( curl -s "https://example.com/all" -H "authorization: Bearer $token" \\
    --data-raw '{"scope":[{"by_subject":{"login":['"$id"']}}],"page":0,"page_size":10}' \\
    --compressed 2>/dev/null );
my $response = `$curl`;

# заменяю разделитель в JSON, чтобы разбить его на отдельные элементы массива
$response =~ s/\},\{/\}###\{/g;
my @array = split(/###/, $response);

my $length_sv = 9;
open my $out, '>>:encoding(UTF-8)', "filename" or warn "Could not open file - $!" and exit(1);

foreach(@array) {
    my ($tid, $cid, $v3, $l, $v5, $sub) = $_ =~ /^\{"id":(\d+),"customer_id":(\d+)(.*?)_login":"(\w{1,10})"(.*?)"subject":"(.*?)"/;
    my $subc = substr($sub, 0, $length_sv);
    say $subc;
    my $string = sprintf "| %-5s | %-1s | %-9s | %-${length_sv}s | %-11s | %-10s|","$time","$num","$tid","$subc","$cid","$l";
    say $string;
    say $out $string;
}

close $out;

Может есть способ как-то явно задать кодировку для строки, которую мы получаем от curl? В целом она отображается корректно, при выводе в STDOUT через print, printf, say (но и преобразованная тоже). Проблема только с записью в файл.

ameame2021 ()
Ответ на: комментарий от gremlin_the_red

Ты мой коммент прочитал?

Да, конечно.

Осознал?

Нет, я сначала не понял.

use locale и :encoding(UTF-8) убрал?

Теперь убрал. Заработало. Большое спасибо!

Я не понял, почему это было из-за локали ru_RU.UTF-8?

Буду думать.

ameame2021 ()
Ответ на: комментарий от ameame2021

Нет, я сначала не понял

ТеÑÑ — Это «Тест» в utf8, повторно преобразованный из latin1 в utf8. Я не знаю точно, что конкретно у тебя вызвало такой эффект, но это явно связано с флагом use locale и последующим принудительным перекодированием выводимого в файл текста в utf8.

gremlin_the_red ★★★★★ ()
Ответ на: комментарий от ameame2021

безотносительно твоего вопроса:

  1. лучше указывать curl формат и кодировку, которую ты ожидаешь -H "Content-Type: application/json; charset=UTF-8", так же лучше явно сказать, что без кеширования -H "Cache-Control: no-cache" ибо get;

  2. perl прекрасно работает с json без регэкспов. use JSON; и понеслась душа в рай - https://metacpan.org/release/MAKAMAKA/JSON-2.90/view/lib/JSON.pm#encode_json .

vtVitus ★★★★★ ()
Последнее исправление: vtVitus (всего исправлений: 3)
Ответ на: комментарий от vtVitus

лучше указывать curl формат и кодировку, которую ты ожидаешь -H «Content-Type: application/json; charset=UTF-8», так же лучше явно сказать, что без кеширования -H «Cache-Control: no-cache» ибо get;

Спасибо, так и сделаю!

perl прекрасно работает с json без регэкспов. use JSON; и понеслась душа в рай - https://metacpan.org/release/MAKAMAKA/JSON-2.90/view/lib/JSON.pm#encode_json .

Попробую его использовать.

ameame2021 ()

От внешних программ приходят байты. Чтобы преобразовать их в строки делай так.

utf8::decode($curl_output);
Olegymous ★★ ()
Последнее исправление: Olegymous (всего исправлений: 1)
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.