Проблема в Perl с использованием функции split() на символах табуляции, когда строка содержит не латинские символы
Я работаю над модификацией сценария Perl, который читает в последовательности файлов в кодировке UCS-2LE со строками в формате с разделителями табуляции, но у меня возникают проблемы с разбиением строк на символе табуляции, когда строка содержит символы вне расширенного Латинский набор символов.
Вот пример строки, которую я читаю из этих файлов (с разделителями табуляции):
adını transcript asr turkish
Когда мой скрипт записал эти строки в выходной файл, чтобы попытаться отладить эту проблему, он пишет:
ad1Ů1ĉtranscript asr turkish
Похоже, что он не распознает символ табуляции после турецкого символа. Это происходит только тогда, когда слово заканчивается нелатинским символом (и поэтому находится рядом с вкладкой).
Вот часть блока кода, где происходит запись в выходной файл и происходит разбиение строки:
for my $infile (@ARGV){
if (!open (INFILE, "<$infile")){
die "Couldn't open $infile.\n";
}
binmode (OUTFILE, ":utf8");
while (<INFILE>) {
chomp;
$tTot++;
if ($lineNo == 1) {
$_ = decode('UCS-2LE', $_);
}
else {
$_ = decode('UCS-2', $_);
}
$_ =~ s/[\r\n]+//g;
my @foo = split('\t');
my $orth = $foo[0];
my $tscrpt = $foo[1];
my $langCode = $foo[3];
if (exists $codeHash{$langCode}) {
unless ($tscrpt eq '') {
check($orth, $tscrpt, $langCode);
}
}
else {
print OUTFILE "Unknown language code $langCode at line $lineNo.\n";
print OUTFILE $_; # printing the string that's not being split correctly
print OUTFILE "\n";
$tBad++;
}
}
Цель этого сценария состоит в том, чтобы проверить, что для каждой строки входного файла код языка является действительным, и на основе этого кода проверить, является ли транскрипция для каждого слова "законной" в соответствии с нашей системой транскрипции.
Вот что я пробовал до сих пор:
- Изменение кодировки входных строк при их чтении в UTF-8, UTF-16 или UTF-16LE
- Изменение символа split() на '\w', /[[:blank:]]/, \p{Blank}, \x{09} и \N{U+0009}.
- Чтение документации по Perl Unicode и perlrebackslash, а также любых других сообщений, имеющих отношение к удаленному сайту, которые я смог найти на различных сайтах
У кого-нибудь есть какие-либо предложения относительно других вещей, которые я мог бы попробовать? Заранее спасибо!
Я должен также упомянуть, что я не контролирую ни кодировку входного файла, ни кодировку выходного файла; Я должен читать в UCS-2LE и выводить UTF-8.
2 ответа
Благодаря всем комментариям и дальнейшим исследованиям я выяснил, как решить проблему, и она немного отличалась от моей; Оказалось, что это была комбинация проблемы split() и проблемы с кодировкой. Мне пришлось как добавить кодировку в явном операторе открытия вместо использования неявного открытия в цикле for, так и пропустить первые два байта в начале файла.
Вот как выглядит исправленный рабочий код для раздела, который я разместил в своем вопросе:
for my $infile (@ARGV){
my $outfile = $infile . '.out';
# SOLUTION part 1: added explicit open statement
open (INFILE, "<:raw:encoding(UCS-2le):crlf", $infile) or die "Error opening $infile: $!";
# SOLUTION part 2: had to skip the first two bytes of the file
seek INFILE, 2, 0;
if (!open (OUTFILE, ">$outfile")) {
die "Couldn't write to $outfile.\n";
}
binmode (OUTFILE, ":utf8");
print OUTFILE "Line#\tOriginal_Entry\tLangCode\tOffending_Char(s)\n";
$tBad = 0;
$tTot = 0;
$lineNo = 1;
while (<INFILE>) {
chomp;
$tTot++;
# SOLUTION part 3: deleted the "if" block I had here before that was handling encoding
# Rest of code in the original block is the same
}
Мой код теперь правильно распознает символы табуляции рядом с символами, не входящими в расширенный латинский набор, и разбивается на вкладки, как и должно быть.
ПРИМЕЧАНИЕ. Другим решением было бы заключить иностранные слова в двойные кавычки, но в нашем случае мы не могли гарантировать, что наши входные файлы будут отформатированы таким образом.
Спасибо всем, кто прокомментировал и помог мне!
Вы должны начать с открытия файла с правильной кодировкой (не то, чтобы я знал, правильная ли это или нет, но я беру ваше слово за это). Тогда вам не нужно вызывать decode():
open(my $fh, "<:encoding(UCS-2LE)", $file) or die "Error opening $file: $!";
while (<$fh>) {
...
}