scr*_*345 7 perl encoding mixed utf-8
我正在将我们大学中国研究系古老的基于DOS的图书馆程序生成的文件转换为更有用和可访问的内容.
我正在处理的问题之一是导出的文本文件(大小约为80MB)采用混合编码.我在Windows上.
德语变音符号和其他高位ASCII字符用cp1252编码,我认为是GB18030中的CJK字符.由于"重叠"编码,我不能将整个文件拖到Word或其他东西并让它进行转换,因为我会得到这样的东西:
原稿:
+Autor:
-Yan, Lianke / ÑÖÁ¬¿Æ # encoded Chinese characters
+Co-Autor:
-Min, Jie / (šbers.) # encoded German U-umlaut (Ü)
Run Code Online (Sandbox Code Playgroud)
结果:
+Autor:
-Yan, Lianke / ??? # good
+Co-Autor:
-Min, Jie / (?ers.) # bad... (should be: "Übers.")
Run Code Online (Sandbox Code Playgroud)
所以我编写了一个带有几个子程序的脚本,这些子程序分几步转换非ASCII字符.它做了以下事情(其中包括):
用字母数字代码替换一些高阶ASCII字符(š,á等)(不太可能自然地出现在文件中的任何其他位置).例如:-Min, Jie / (šbers.)- > -Min, Jie / (uumlautgrossbers.)
注意:我手工完成了"转换表",所以我只考虑了实际出现在我文档中的特殊字符.因此,转换并不完全,但在我的案例中产生了足够的结果,因为我们的书籍主要是德语,英语和中文,只有很少的语言,如意大利语,西班牙语,法语等,而且几乎没有捷克语等.
仅当á, £, ¢, ¡, í字母数字代码不在高ASCII范围内的其他字符之前或之后时,才\x80-\xFF替换它们.(这些是ß, ú, ó, í和" small nordic o with cross-stroke" 的cp1252编码版本,并且出现在cp1252和GB18030编码的字符串中.)
读取整个文件并将其从GB18030转换为UTF8,从而转换为真实汉字的编码汉字.
将字母数字代码转换回其Unicode等效代码.
虽然脚本大多有效,但会出现以下问题:
我想知道的是:
通常,有没有更好的方法将混合编码文件转换为UTF-8?
如果没有,我应该使用,use utf8以便我可以直接在codes2char子程序中输入字符而不是它们的十六进制表示?
文件开头的BOM会解决NP ++最初将其显示为ANSI文件的问题吗?如果是这样,我应该如何修改我的脚本,以便输出文件有BOM?
在转换之后,我可能想要调用更多子例程(例如,将整个文件转换为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;
Run Code Online (Sandbox Code Playgroud)
哇,这很长.我希望它不会太复杂
编辑:
这是上面示例字符串的hexdump:
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.)
Run Code Online (Sandbox Code Playgroud)
另外两个来说明:
1.
000036B3 2D 52 75 -Ru
000036C6 E1 6C 61 6E 64 0D 0A áland
Run Code Online (Sandbox Code Playgroud)
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).
Run Code Online (Sandbox Code Playgroud)
在这两种情况下,都有十六进制值E1.在第一种情况下,它代表德国尖头(ß,"Rußland"="俄罗斯"),在第二种情况下,它是多字节CJK字符柔性的一部分(读作:"rou").
在库程序中,输入和显示中文字符时需要先加载一个附加程序,据我所知,它是以低级别挂钩到图形驱动程序中,捕获编码后的汉字并显示它们作为角色而留下其他一切.德语变音符号等由图书馆程序本身处理.
我不完全理解这是如何工作的,即程序如何知道是否将HexE1视为单个字符á并因此根据codepage X何时转换为多字节字符的一部分并因此根据codepage Y
我发现的最接近的近似值是,如果在它之前或之后有其他特殊字符,则特殊字符可能是中文字符串的一部分.(例如ÎÂÈá¶Øºñ)
ef bb bf)。如果您能找出 NP++ 错误检测文件的确切原因,那就更好了。<:utf8是 更短的等价物< :encoding(UTF-8)。至于最初的混乱是如何工作的,似乎“附加程序”只是将任何看起来像汉字的东西转换为中文,而忽略其他任何东西(然后标准驱动程序使用欧洲编码显示),而“库程序”只输出它收到的任何代码。因此,转换文件的更直接方法可能是镜像这一点:使用(或其他)读取文件:encoding(latin-1),然后替换中文字符(例如s/\\xc8\\xe1/\xe6\x9f\x94/)。