-3

我有一个文件,其中包含未说明编码的 Unicode 文本。我想扫描这个文件,寻找 U+0600 到 U+06FF 范围内的任何阿拉伯代码点,并将每个适用的 Unicode 代码点映射到一个 ASCII 字节,这样新生成的文件将由纯 ASCII 字符组成,所有代码点都在 128 以下。

我该怎么做呢?我尝试以与阅读 ASCII 相同的方式阅读它们,但我的终端显示 ??,因为它是一个多字节字符。

注意:该文件由 Unicode 字符集的子集组成,子集大小小于 ASCII 字符的大小。因此,我能够从这个特定的 Unicode 子集到 ASCII 进行 1:1 映射。

4

3 回答 3

4

这要么是不可能的,要么是微不足道的。以下是简单的方法:

  • 如果没有代码点超过 127,那么只需用 ASCII 写出来。完毕。

  • 如果某些代码点超过 127,那么您必须选择如何用 ASCII 表示它们。一种常见的策略是使用 XML 语法,如αU+03B1。对于转录的每个 trans-ASCII Unicode 代码点,这最多需要 8 个 ASCII 字符。

我留下的那些不可能作为原始海报的练习。我什至不会提及愚蠢但可能(读作:愚蠢)的方法,因为这些方法很多。数据破坏是数据处理中的死罪,应该这样对待。

请注意,我假设“Unicode 字符”实际上是指“Unicode 代码点”;也就是说,程序员可见的字符。对于用户可见的字符,您需要 'Unicode grapheme (cluster)' 代替。

此外,除非你先规范化你的文本,否则你会讨厌这个世界。我建议NFD。


编辑

经过原发帖人的进一步澄清,似乎他想做的事情很容易使用现有工具完成,而无需编写新程序。例如,这会将一组阿拉伯字符从 UTF-8 输入文件转换为 ASCII 输出文件:

$ perl -CSAD -Mutf8 -pe 'tr[ابتثجحخد][abttjhhd]' < input.utf8 > output.ascii

这只处理这些代码点:

U+0627 ‭ ا  ARABIC LETTER ALEF
U+0628 ‭ ب  ARABIC LETTER BEH
U+0629 ‭ ة  ARABIC LETTER TEH MARBUTA
U+062A ‭ ت  ARABIC LETTER TEH
U+062B ‭ ث  ARABIC LETTER THEH
U+062C ‭ ج  ARABIC LETTER JEEM
U+062D ‭ ح  ARABIC LETTER HAH
U+062E ‭ خ  ARABIC LETTER KHAH
U+062F ‭ د  ARABIC LETTER DAL

所以你必须将它扩展到你想要的任何映射。

如果你想在脚本而不是命令行工具中使用它,这也很简单,另外你可以通过设置映射来按名称讨论字符,例如:

 "\N{ARABIC LETTER ALEF}"   =>  "a",
 "\N{ARABIC LETTER BEH}"    =>  "b",
 "\N{ARABIC LETTER TEH}"    =>  "t",
 "\N{ARABIC LETTER THEH}"   =>  "t",
 "\N{ARABIC LETTER JEEM}"   =>  "j",
 "\N{ARABIC LETTER HAH}"    =>  "h",
 "\N{ARABIC LETTER KHAH}"   =>  "h",
 "\N{ARABIC LETTER DAL}"    =>  "d",

如果这应该是一个更大的 C++ 程序中的一个组件,那么也许你会想要在 C++ 中实现它,可能但不是必须使用包含音译支持的 ICU4C 库。

但是如果你只需要一个简单的转换,我不明白你为什么要编写一个专门的 C++ 程序。好像工作量太大了。

于 2012-02-17T18:47:49.683 回答
1

除非您知道格式,否则您无法读取数据。用microsoft word打开文件,依次选择“另存为”、“其他格式”、“纯文本(.txt)”,保存。在转换框中,选择“其他编码”、“Unicode”(即UTF16LE)和“确定”。该文件现在保存为 UTF16LE。

std:ifstream infile("myfile.txt", std::ios::binary); //open stream
infile.seekg (0, ios::end); //get it's size
int length = infile.tellg();
infile.seekg (0, ios::beg);
std::wstring filetext(length/2); //allocate space
ifstream.read((char*)&filetext[0], length); //read entire file
std::string final(length/2);
for(int i=0; i<length/2; ++i) { //"shift" the variables to "valid" range
    if (filetext[length/2] >= 0x600 && filetext[length/2] <= 0xFF)
        final[length/2] = filetext[length/2]-0x600;
    else
        throw std::exception("INVALID CHARACTER");
}
//done

到处都是警告:我非常怀疑这会产生你想要的结果,但这是可以管理的最好的,因为你没有告诉我们需要做的翻译或文件的格式。另外,我假设您的计算机和编译器与我的相同。如果不是这样,部分或全部可能是错误的,但这是我能用你没有告诉我们的缺失信息做的最好的事情。

于 2012-02-17T19:16:04.230 回答
0

为了解析出 Unicode 代码点,您必须首先将文件解码为其未编码的 Unicode 表示(相当于 UTF-32)。为此,您首先需要知道文件是如何编码的,以便可以对其进行解码。例如,Unicode 代码点U+0600U+06FF编码为UTF-80xD8 0x800xDB 0xBFUTF-8、 UTF-16LE0x00 0x060xFF 0x06UTF-16LE、0x06 0x00UTF 0x06 0xFF-16BE 等。

如果文件以 BOM 开头,那么您知道所使用的确切编码并可以相应地解释文件的其余部分。例如,UTF-8 BOM 是0xEF 0xBB 0xBF,UTF-16LE 是0xFF 0xFE,UTF-16BE 是0xFE 0xFF,等等。

如果文件不是以 BOM 开头,那么您必须分析数据并对其执行启发式算法以检测编码,但这并不是 100% 可靠的。尽管检测 UTF 编码相当容易,但几乎不可能以任何可靠性度量来检测 Ansi 编码。即使在没有 BOM 的情况下检测 UTF 编码有时也会导致错误结果(请阅读thisthisthis)。

永远不要猜测,您将面临数据丢失的风险。如果您不知道使用的确切编码,请向用户询问。

于 2012-02-18T02:18:43.000 回答