我正在将我们大学汉学系基于 DOS 的古老图书馆程序生成的文件转换为更有用和更易于访问的文件。
我正在处理的问题之一是导出的文本文件(大小约为 80MB)采用混合编码。我在 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 字符。它执行以下操作(除其他外):
用字母数字代码替换一些高阶 ASCII 字符(š、á 等)(不太可能自然地出现在文件中的其他任何位置)。例如:
-Min, Jie / (šbers.)
->-Min, Jie / (uumlautgrossbers.)
注意:我手工制作了“转换表”,所以我只考虑了文档中实际出现的特殊字符。因此,转换并未完全完成,但在我的情况下产生了足够的结果,因为我们的书大多是德文、英文和中文的,只有很少的语言,如意大利语、西班牙语、法语等,几乎没有捷克语等。仅当它们前面或后面没有高 ASCII 范围中的另一个字符时,才替换
á, £, ¢, ¡, í
为字母数字代码。(这些是和“ ”的 cp1252 编码版本,出现在 cp1252 和 GB18030 编码的字符串中。)\x80-\xFF
ß, ú, ó, í
small nordic o with cross-stroke
读入整个文件并将其从GB18030转换为UTF8,从而将编码的汉字转换为真正的汉字。
将字母数字代码转换回它们的 Unicode 等价物。
虽然脚本大部分都可以工作,但会出现以下问题:
- 将原来的 80MB 文件转换后,Notepad++ 仍然认为它是一个 ANSI 文件并按原样显示。我需要按“Encoding->Encode in UTF-8”才能正确显示。
我想知道的是:
一般来说,有没有更好的方法将混合编码文件转换为 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;
哇,这很长。我希望它不会太复杂
编辑:
这是上面示例字符串的十六进制转储:
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。在第一种情况下,它代表德语的Sharp-s(ß,“Rußland”="Russia"),在第二种情况下,它是多字节CJK字符柔的一部分(读作:“rou”)。
在库程序中,汉字是通过一个附加程序输入和显示的,该程序必须首先加载,据我所知,它在底层连接到图形驱动程序,捕获编码的汉字并显示它们作为角色,同时不理会其他一切。德语变音等由图书馆程序本身处理。
我不完全理解这是如何工作的,即程序如何知道 HexE1 是否被视为单个字符á
并因此根据codepage X
以及何时转换为多字节字符的一部分并因此根据codepage Y
我发现的最接近的近似值是,如果特殊字符之前或之后有其他特殊字符,则它很可能是中文字符串的一部分。(例如ÎÂÈá¶Øºñ
)