Как мне обработать широкий символ в Perl, используя UTF-16? - PullRequest
0 голосов
/ 11 января 2019

Я читаю в файле (текст Юникода с прямым порядком байтов UTF-16, с очень длинными строками, со строкой CRLF), затем я выполняю некоторую обработку этого файла, затем я использую некоторые данные из входного файла и вывод в новый файл. Я перепробовал много вещей из различных вопросов и постов в блогах, и я признаюсь, что был полностью запутан в этот момент. Во время написания этого вопроса я застрял в ошибке спецификации, но по предложению из другого вопроса я изменил свое «открытое» утверждение, добавив в него: кодирование (UTF-16le), и теперь моя ошибка - «Широкий символ в записи подпрограммы», которую я тоже не могу решить.

ОС: Windows 10
Оболочка: cmd
Perl: это perl 5, версия 14, subversion 2 (v5.14.2), созданная для многопоточности MSWin32-x86

Я пробовал со слоями и без (: encoding (UTF-16le): crlf) как на входе, так и на выходе. Я пытался с и без кодирования / декодирования. Результаты включали ошибки спецификации, ошибку с широкими символами, в которой я сейчас нахожусь, и экспортированный файл, который (при открытии с помощью Libre Office) показывает что-то вроде азиатских символов при импорте с UTF-16, но выглядит более нормально с UTF -8 (хотя все еще неверно). Лучше всего мне удалось управлять выходными данными файла, который в основном правильный, но содержит бессмысленный символ вместо правильного акцентированного символа (c cedilla). К сожалению, из-за плохого протокола эксперимента у меня больше нет ни этого файла, ни шагов для его воспроизведения.

use strict;
use warnings;
use Encode qw(encode decode);

use POSIX 'strftime'; # because I like timestamps for lots of things

# removed :crlf per instructions
open(my $input_fh, '<:encoding(UTF-16le)', $path."/".$inputFile)
 or die "Could not open file "."'".$path."/".$inputFile." $!";

while (my $line = <$input_fh>) {
  #$line = decode ('UTF-16le', $line); # removed per instructions
  chomp $line;
  my @lineArray;
  my $last_char = "";
  my $current_char = "";
  my $current_string = "";
  my $field_count = 0;
  my $inside_quote = 0;

  for my $i (0..length($line)-1) {
    $last_char = $current_char;
    $current_char = substr($line, $i, 1);

    # Catch first char in the string?
    if ($current_char eq "," && $inside_quote == 0) { # if you find a comma and we're not inside quotes, it's a new field
      # put the whole string into the array as one field
      $lineArray[$field_count] = $current_string;
      $current_string = "";
      $field_count++;
    }
    elsif ($current_char eq '"' && $inside_quote == 0) { # found the first of two quotes
      $inside_quote = 1;
      # no need to update $current_string
      # no need to update $field_count
    }
    elsif ($current_char eq '"' && $inside_quote == 1) { # found a second quote, need to decide if it's in-field or an end quote
      $inside_quote++;
      $current_string .= '"';
      # no need to update $field_count
    }
    elsif ($current_char eq "," && $inside_quote >= 2) { # we are at the end of a string, but there was more than 1 quote
      # removes the trailing quote, if there was one
      if ($last_char eq '"') { $lineArray[$field_count] = chop($current_string); }
      else { $lineArray[$field_count] = $current_string; }
      $current_string = "";
      $field_count++;
      $inside_quote = 0;
    }
    else {
      $current_string .= $current_char;
    }
  } # for my $i (0..length($line)-1)
  my $id = $lineArray[0];
  my $name = $lineArray[1];
  my $campus = $lineArray[2];
  my $building = $lineArray[3];

  $output .= '"'.$id.'","'.$name.'","'.$campus.'","'.$building.'"'."\r\n";
}
my $output_fh;

# removed :crlf per instructions
open($output_fh, '>:encoding(UTF-16le)', $outputFileName) 
 or die "Could not open file '$outputFileName' $!";

#$output = encode ('UTF-16le', $output); #removed per instructions

print $output_fh $output;  

Ошибка: Широкий символ в записи подпрограммы в C: /Dwimperl/perl/lib/Encode.pm строка 176, строка 1.

Я надеюсь, что файл останется таким же, как и ввод (текст с кодировкой UTF-16 в юникоде Little-endian, с очень длинными строками, со строкой CRLF), при этом сохраняя «правильные» специальные символы, такие как cedilla на c. Я бью стену, и любая помощь будет принята с благодарностью.

Обновление (2019-01-14): обновлен код для включения «обработки» и изменений, предложенных комментаторами. Моя цель - обработать CSV-файл и вывести несколько разных файлов. Я пытался использовать библиотеки csv-processing, но не смог заставить их работать, потому что входной csv не правильно сформирован (и я не могу его контролировать). Поэтому я делаю классическую ошибку, делая свой собственный парсер. То, что вы видите выше, является началом этого парсера. Есть много других полей и много других действий, которые нужно выполнить над этими полями (именно поэтому я сохранил их в переменных с красивыми именами, а не оставлял их в трудно запоминаемых точках массива). Спасибо всем, кто откликнулся. Вы определенно помогаете мне пройти мимо стены.

Обновление 2 (2019-01-14): после загрузки своего кода я попытался снова, и у меня есть больше информации отладки. Во-первых, мое «тестирование» пытается открыть выведенный файл в LibreOffice Calc. Как я уже отмечал, импорт UTF-16 показал азиатские символы, а импорт UTF-8 выглядел более нормальным, но все еще неправильно (в этом случае некоторые искаженные символы и все в одной длинной строке). ОДНАКО, когда я открываю файл в текстовом редакторе (например, Atom), файл выглядит нормально (за исключением того, что после каждого символа есть пробел, который, как я понимаю, следует ожидать с UTF-16).

РЕШЕНИЕ (2019-01-14): последнее замечание @ikegami было решением. Оставив один мой код и добавив raw к открытому входу и открытому выходу, мы создали файл UTF-16, который LibreOffice Calc может правильно импортировать. Интересно, что запуск утилиты «file» в выходном файле приводит к: «test.csv: data», что не очень обнадеживает. Если кто-то захочет попытаться ответить, почему он не совпадает с входным файлом, я хотел бы знать, но в любом случае я буду считать этот вопрос ответом. Спасибо всем, кто помог! Я постараюсь выяснить, как вы можете сказать, что вы. С благодарностью! Кроме того, любые комментарии, которые говорят мне, как правильно закрыть это и / или должным образом вознаграждать тех, кто помог, приветствуются.

1 Ответ

0 голосов
/ 13 января 2019

Следующий скрипт работает в моей системе (Ubuntu 18.04) как минимум.

use Encode qw(encode decode);
use utf8;

open(my $input_fh, '<:encoding(UTF-16le):crlf', $path."/".$inputFile)
 or die "Could not open file "."'".$path."/".$inputFile." $!";

while (my $line = <$input_fh>) {
  # some operations on the input text
  $line =~ s/フォルダー?/folder/g;
  $line =~ s/Windows/ウィンドウズ/g;
  $output .= $line;
}

open(my $output_fh, '>:encoding(UTF-16le):crlf', $outputFileName)
 or die "Could not open file '$outputFileName' $!";

print $output_fh $output;
  • Я не тестировал скрипт в Windows10, но вводимый текст создается в Windows с кодировкой UTF-16LE.
  • Сам скрипт имеет кодировку UTF-8.

Если у вас все еще есть проблема, вам будет полезно предоставить минимальный набор входного текста и средства обработки для воспроизведения проблемы.

...