7

我正在尝试使用 -finput-charset 编译器选项在 g++ 中编译 UTF-16BE C++ 源文件,但我总是遇到一堆错误。更多细节如下。

我的环境(在 CentOS Linux 中):

  • g++:4.1.2
  • 图标v:2.5
  • Linux 语言(在终端中):LANG="en_US.UTF-8"

我的示例源文件(以 UTF-16BE 编码存储):

// main.cpp:

#include <iostream>

int main()
{
    std::cout << "Hello, UTF-16" << std::endl;
    return 0;
}

我的步骤:

  • 我阅读了关于 -finput-charset 选项的 g++ 手册。 g++ 手册说:

-finput-charset=charset 设置输入字符集,用于从输入文件的字符集转换为GCC使用的源字符集。如果没有指定区域设置,或者 GCC 无法从区域设置中获取此信息,则默认为 UTF-8。这可以被语言环境或此命令行选项覆盖。目前,如果存在冲突,命令行选项优先。charset 可以是系统的“ iconv ”库例程支持的任何编码。

  • 因此,我输入了如下命令:

g++ -finput-charset=UTF-16BE main.cpp

我得到了这些错误:

在 main.cpp:1 包含的文件中:

/usr/lib/gcc/i386-redhat-linux/4.1.2/../../../../include/c++/4.1.2/iostream:1: 错误:程序中出现“\342”错误

/usr/lib/gcc/i386-redhat-linux/4.1.2/../../../../include/c++/4.1.2/iostream:1: 错误:程序中出现杂散“\274”

...(重复,很多,大约 4000+)...

/usr/lib/gcc/i386-redhat-linux/4.1.2/../../../../include/c++/4.1.2/iostream:1: 错误:程序中出现杂散“\257”

main.cpp:在函数“int main()”中:

main.cpp:5:错误:“cout”不是“std”的成员

main.cpp:5:错误:“endl”不是“std”的成员</p>

  • 手册文本表明字符集可以是“iconv”例程支持的任何编码,因此我猜测编译错误可能是由我的 iconv 库引起的。然后我测试了iconv:

iconv --from-code=UTF-16BE --to-code=UTF-8 --output=main_utf8.cpp main.cpp

按预期生成“main_utf8.cpp”文件。然后我尝试编译它:

g++ -finput-charset=UTF-8 main_utf8.cpp

请注意,我明确指定了输入字符集以查看我是否做错了什么,但这次生成了“a.out”而没有任何错误。当我运行它时,它可以产生正确的输出。

最后...

我不知道我哪里做错了。我在网上搜索试图找出这个编译器选项的一些例子,但我找不到。

请指教!谢谢!

进一步编辑:

多谢你们!您的回复很快!一些更新:

  1. 当我说“UTF-16”时,我的意思是“UTF-16 + BOM”。事实上,我使用的是 UTF-16BE。我已经更新了上面的文字。
  2. 一些答案说错误是由非 UTF-16 头文件引起的。如果是这种情况,以下是我的想法:在编写 C/C++ 项目时,我们总是会包含一些标准头文件,对吧?例如 stdio.h 或 iostream。如果 G++ 编译器只处理我们创建的源文件的编码,而不处理标准库中的源文件,那么这个 -finput-charset 选项的存在是为了什么?

最终编辑:

最后,我的解决方案是这样的:

  1. 一开始,我把我的源文件的编码改成了GB2312,正如下面“李斯特先生”所说。用了一阵子还行,后来发现不适合我的情况,因为系统中的其他部分大部分还是用UTF-8进行通信和接口的,所以很多地方都得转换编码……不仅我的工作开销,它也可能导致我的程序的一些性能下降。
  2. 后来我尝试将所有源文件转换为 UTF-8 + BOM。这样,Windows 中的 Visual Studio 可以愉快地编译它们,但 Linux 中的 GCC 会报错。然后我写了一个 shell 脚本来删除 BOM,在我想用 GCC 编译我的代码之前,我先运行这个脚本。
  3. 幸运的是,我不必在 Linux 中手动构建代码,因为我的项目中使用了持续集成工具 TeamCity 来自动生成构建。我可以更改 TeamCity 中的构建步骤,以帮助我在每日构建开始之前运行此脚本。
  4. 使用这种 UTF-8 + BOM + 脚本方法,我决定不在 Linux 中编辑我的源代码,因为如果我想这样做,我必须确保我的代码在提交之前可以成功构建,这意味着我必须运行在我构建代码之前删除 BOM 的脚本,这意味着 SVN 会报告每个文件都被修改(BOM 被删除),因此很容易错误地提交错误的文件。为了解决这个问题,我编写了另一个 shell 脚本来将 BOM 添加回源文件。虽然我仍然不会经常在 Linux 中编辑我的代码,但是当我真的需要时,我不必面对提交对话框中非常长的更改列表。
4

4 回答 4

5

编码蓝调

源代码文件不能使用 UTF-16;因为您包含的标头<iostream>, 不是 UTF-16 编码的。正如#include逐字记录文件一样,这意味着您突然有了一个 UTF-16 编码的文件,其中包含大量无效数据(显然约为 4k)。

几乎没有充分的理由在任何事情上使用 UTF-16,所以这也是一样的。

编辑:关于编码支持的问题:操作系统本身不负责提供编码支持,这归结为使用的编译器。

Windows 上的 g++ 完全支持与 Linux 上的 g++ 相同的所有编码,因为它是同一个程序,除非您在 Windows 上使用的任何版本的 g++ 都依赖于严重损坏的 iconv 库。

检查您的工具链并确保您的所有工具都处于正常工作状态。

作为备选; 不要在源文件中使用中文,而是用英文编写它们,使用英文文字或简单TOKEN_STYLE_PLACEHOLDER的 s,在运行的可执行文件中使用l10nandi18n替换它们。

Threedit: -finput-charset几乎可以肯定是代码页和其他类似废话时代的遗留物;然而; ISO-8859-n 文件几乎总是与 UTF-8 标准标头兼容,但是,请参阅下面的重新编辑。

重新编辑:下次;记住一个简单的口头禅:“N'DUUH!”;“永远不要使用 UTF-8!”


I18N

此类问题的常见解决方案是完全消除问题,例如,通过gettext

使用 gettext 时,您通常会得到一个loc(char *)抽象出大部分翻译工具特定代码的函数。所以,而不是

#include <iostream>

int main () {
  std::cout << "瓜田李下" << std::endl;
}

你将会拥有

#include <iostream>

#include "translation.h"

int main () {
  std::cout << loc("DEEPER_MEANING") << std::endl;
}

并且,在zh.po

msgid DEEPER_MEANING
msgstr "瓜田李下"

当然,你也可以有一个en.po

msgid DEEPER_MEANING
msgstr "Still waters run deep"

这可以扩展,并且 gettext 包具有用于使用变量等扩展字符串的工具,或者您可以使用printf,来解释不同的语法。


第三种选择

不必处理对文件编码、文件结尾、字节顺序标记和其他此类问题有不同要求的多个编译器;可以使用MinGW或类似工具进行交叉编译。

此选项需要一些设置,但可以很好地减少未来的开销和令人头疼的问题。

于 2012-04-27T06:39:48.290 回答
2

错误消息说问题出在包含文件中,所以我认为发生的情况是包含文件是普通的 UTF-8,但由于编译器切换,编译器希望将它们视为 UTF-16。

所以恐怕解决方案是始终先将源转换为UTF-8;也许在makefile中。或者找到一个不包含其他编码的包含文件的解决方案......

编辑: 当且仅当系统源文件都不包含任何非 ASCII 字符时,GB 编码可能会起作用。然后你可以告诉编译器它们是 GB 编码的,没有问题。

于 2012-04-27T06:43:25.157 回答
0

这不起作用,因为编译器还将尝试将头文件读取为 UTF-16,但事实并非如此。

于 2012-04-27T06:38:46.043 回答
-1

UTF-16不是字节编码。这是一种基本存储单元为 16 位大的编码。

当您想以字节序列存储 UTF-16 时,您必须在 UTF-16BE 和 UTF-16LE 之间进行选择。

于 2012-04-27T06:36:09.653 回答