Вопросы о преобразовании файла со смешанной кодировкой в ​​UTF8 в Perl

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

Среди проблем, с которыми я сталкиваюсь, заключается в том, что экспортированные текстовые файлы (размером около 80 МБ) имеют смешанную кодировку. Я на Windows.

Немецкие умлауты и другие символы более высокого ASCII кодируются в cp1252, я думаю, а CJK-символы в GB18030. Из-за «перекрывающихся» кодировок я не могу просто перетащить весь файл в Word или что-то еще и позволить ему выполнить преобразование, потому что я получу что-то вроде этого:

ориг:

+Autor:
-Yan, Lianke / ÑÖÁ¬¿Æ      # encoded Chinese characters
+Co-Autor:
-Min, Jie / (šbers.)       # encoded German U-umlaut (Ü)

результат:

+Autor:
-Yan, Lianke / 阎连科       # good
+Co-Autor:
-Min, Jie / (歜ers.)       # bad... (should be: "Übers.")

Поэтому я написал сценарий с несколькими подпрограммами, которые преобразуют не-ASCII-символы в несколько шагов. Он выполняет следующие действия (среди прочего):

  1. замените некоторые символы ASCII более высокого порядка (š, á и т. д.) буквенно-цифровыми кодами (вряд ли они естественным образом появятся где-либо еще в файле). Пример: -Min, Jie / (šbers.) -> -Min, Jie / (uumlautgrossbers.)
    Примечание: я сделал "таблицу преобразования" вручную, поэтому я принял во внимание только специальные символы, фактически появляющиеся в моем документе. Таким образом, преобразование не полностью завершено, но в моем случае дает адекватные результаты, поскольку наши книги в основном на немецком, английском и китайском языках, и лишь очень немногие на таких языках, как итальянский, испанский, французский и т. д., и почти нет на чешском и т. д.

  2. замените á, £, ¢, ¡, í буквенно-цифровыми кодами только в том случае, если им не предшествует или не следует другой символ из старшего диапазона ASCII \x80-\xFF. (это версии ß, ú, ó, í и "small nordic o with cross-stroke" в кодировке cp1252, которые появляются в строках в кодировке cp1252 и GB18030.)

  3. прочитать весь файл и преобразовать его из GB18030 в UTF8, таким образом преобразовав закодированные китайские символы в настоящие китайские символы.

  4. Преобразуйте буквенно-цифровые коды обратно в их эквиваленты Unicode.

Хотя скрипт в основном работает, возникает следующая проблема:

  • После преобразования исходного файла размером 80 МБ Notepad++ по-прежнему считает, что это файл ANSI, и отображает его как таковой. Мне нужно нажать «Кодировка->Кодировать в UTF-8», чтобы отобразить его правильно.

Что я хотел бы знать:

  1. Как правило, есть ли лучший подход для преобразования файла со смешанной кодировкой в ​​UTF-8?

  2. Если нет, следует ли мне использовать use utf8, чтобы я мог напрямую вводить символы вместо их шестнадцатеричного представления в подпрограмме codes2char?

  3. Решит ли спецификация в начале файла проблему отображения NP++ изначально как файла ANSI? Если да, то как мне изменить мой скрипт, чтобы в выходном файле была спецификация?

  4. После преобразования я могу вызвать еще несколько подпрограмм (например, для преобразования всего файла в формат CSV или ODS). Нужно ли мне продолжать использовать вступительный оператор из подпрограммы codes2char?

Код состоит из нескольких подпрограмм, которые вызываются в конце:

!perl -w
use strict; 
use warnings;
use Encode qw(decode encode); 
use Encode::HanExtra;

our $input = "export.txt";
our $output = "export2.txt";

sub switch_var {                # switch Input and Output file between steps
    ($input, $output) = ($output, $input);
}

sub specialchars2codes {
open our $in, "<$input" or die "$!\n"; 
open our $out, ">$output" or die "$!\n"; 

while( <$in> )  {   
    ## replace higher ASCII characters such as a-umlaut etc. with codes.
    s#\x94#oumlautklein#g;
    s#\x84#aumlautklein#g;
    s#\x81#uumlautklein#g;
    ## ... and some more. (ö, Ö, ä, Ä, Ü, ü, ê, è, é, É, â, á, à, ì, î, 
    ## û, ù, ô, ò, ç, ï, a°, e-umlaut and ñ in total.)

    ## replace problematic special characters (ß, ú, ó, í, ø, ') with codes.
    s#(?<![\x80-\xFF])\xE1(?![\x80-\xFF])#eszett#g;
    s#(?<![\x80-\xFF])\xA3(?![\x80-\xFF])#uaccentaiguklein#g;
    s#(?<![\x80-\xFF])\xA2(?![\x80-\xFF])#oaccentaiguklein#g;
    s#(?<![\x80-\xFF])\xA1(?![\x80-\xFF])#iaccentaiguklein#g;
    s#(?<![\x80-\xFF])\xED(?![\x80-\xFF])#nordischesoklein#g;

    print $out $_;
    }   
close $out;
close $in;
}

sub convert2unicode {

open(our $in,  "< :encoding(GB18030)", $input)  or die "$!\n";
open(our $out, "> :encoding(UTF-8)",  $output)  or die "$!\n";

print "Convert ASCII to UTF-8\n\n";

while (<$in>) {         
        print $out $_;      
}

close $in;
close $out;
}

sub codes2char {

open(our $in,  "< :encoding(UTF-8)", $input)    or die "$!\n";
open(our $out, "> :encoding(UTF-8)", $output)   or die "$!\n";

print "replace Codes with original characters.\n";


    while (<$in>) {
        s#lidosoumlautklein#\xF6#g;
        s#lidosaumlautklein#\xE4#g;
        s#lidosuumlautklein#\xFC#g;
        ## ... and some more.
        s#eszett#\xDF#g;
        s#uaccentaiguklein#\xFA#g;
        s#oaccentaiguklein#\xF3#g;
        s#iaccentaiguklein#\xED#g;
        s#nordischesoklein#\xF8#g;

        print  $out $_;
    }
close($in)   or die "can't close $input: $!";
close($out)  or die "can't close $output: $!";
}

##################
## Main program ##
##################

&specialchars2codes;
&switch_var;
&convert2unicode;
&switch_var;
&codes2char;

вау, это было долго. надеюсь не слишком запутано

ИЗМЕНИТЬ:

Это шестнадцатеричный дамп приведенной выше строки примера:

01A36596                                                        2B 41                    +A
01A365A9   75 74 6F 72 3A 0D 0A 2D  59 61 6E 2C 20 4C 69 61  6E 6B 65   utor:  -Yan, Lianke
01A365BC   20 2F 20 D1 D6 C1 AC BF  C6 0D 0A 2B 43 6F 2D 41  75 74 6F    / ÑÖÁ¬¿Æ  +Co-Auto
01A365CF   72 3A 0D 0A 2D 4D 69 6E  2C 20 4A 69 65 20 2F 20  28 9A 62   r:  -Min, Jie / (šb
01A365E2   65 72 73 2E 29 0D 0A                                         ers.)  

и еще два для иллюстрации:

1.

000036B3                                                     2D 52 75                   -Ru
000036C6   E1 6C 61 6E 64 0D 0A                                         áland  

2.

015FE030            2B 54 69 74 65  6C 3A 0D 0A 2D 57 65 6E  72 6F 75      +Titel:  -Wenrou
015FE043   64 75 6E 68 6F 75 20 20  CE C2 C8 E1 B6 D8 BA F1  20 28 47   dunhou  ÎÂÈá¶Øºñ (G
015FE056   65 6E 74 6C 65 6E 65 73  73 20 61 6E 64 20 4B 69  6E 64 6E   entleness and Kindn
015FE069   65 73 73 29 2E 0D 0A                                         ess).  

В обоих случаях имеется шестнадцатеричное значение E1. В первом случае он заменяет немецкий диез (ß, «Rußland» = «Россия»), а во втором случае он является частью многобайтового символа CJK 柔 (читается: «rou»).

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

Я не совсем понимаю, как это работает, т.е. как программы узнают, следует ли HexE1 обрабатывать как одиночный символ á и таким образом преобразовывать в соответствии с codepage X и когда он является частью многобайтового символа и таким образом преобразовывать в соответствии с codepage Y

Самое близкое приближение, которое я нашел, состоит в том, что специальные символы, вероятно, будут частью китайской строки, если перед ней или после нее есть другие специальные символы. (например, ÎÂÈá¶Øºñ)


person screen12345    schedule 01.08.2011    source источник
comment
Вы вводите дополнительный уровень сложности, просматривая строки, декодированные Windows. Избегайте этого, это приведет вас к неверным предположениям. Предоставьте шестнадцатеричные дампы, они не двусмысленны. š — это CP850, закодированный как Windows-1252. ÑÖÁ¬¿Æ имеет кодировку GB18030, если смотреть как Windows-1252.   -  person daxim    schedule 01.08.2011
comment
ответ дополнен шестнадцатеричным дампом и дальнейшими разъяснениями   -  person screen12345    schedule 01.08.2011
comment
эм, это должно было звучать так: ВОПРОС изменен с шестнадцатеричным дампом и дальнейшими разъяснениями   -  person screen12345    schedule 01.08.2011


Ответы (1)


  1. Если смешанная кодировка такова, что каждая строка/запись/поле/что-либо находится в согласованной кодировке, вы можете читать и преобразовывать каждую строку/запись/поле/что-то отдельно. Но это звучит не так, как здесь.
  2. Было бы неплохо.
  3. UTF-8 обычно не использует спецификацию, хотя, если вы действительно хотите попробовать, выведите символ U+FEFF (в UTF-8 это 3 байта ef bb bf). Было бы лучше, если бы вы могли выяснить, почему именно NP++ неправильно определяет файл.
  4. При чтении файла в кодировке UTF-8 хорошей идеей будет открыть его с входным слоем UTF-8. Если хотите, <:utf8 — это более короткий эквивалент < :encoding(UTF-8).

Что касается того, как работает оригинальный беспорядок, кажется, что «дополнительная программа» просто преобразует все, что выглядит как китайский иероглиф, в китайский и оставляет все остальное в покое (которое стандартные драйверы затем отображают с использованием европейской кодировки), в то время как « библиотечная программа» просто выводит любые коды, которые она получает. Таким образом, более простой способ преобразовать ваш файл может заключаться в том, чтобы отразить это: прочитать файл, используя :encoding(latin-1) (или что-то еще), а затем заменить китайские символы (например, s/\xc8\xe1/柔/).

person Anomie    schedule 01.08.2011