我应该如何正确使用g ++的-finput-charset编译器选项来编译非UTF-8源文件?

时间:2012-04-27 06:28:59

标签: c++ gcc character-encoding g++

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

我的环境(在CentOS Linux中):

  • g ++:4.1.2
  • iconv: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-字符集的字符集=       设置输入字符集,用于从输入文件的字符集转换为使用的源字符集   GCC。如果区域设置未指定,或GCC无法获取此信息   来自语言环境的信息,默认值为UTF-8 。这可以   由语言环境或此命令行选项覆盖。   目前,如果有a,命令行选项优先   冲突。 charset可以是系统支持的任何编码   &#34;的的iconv &#34;图书馆例程。

  • 因此我按如下方式输入了命令:
  

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

我收到了这些错误:

  

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

     

/usr/lib/gcc/i386-redhat-linux/4.1.2 /../../../../包括/ C ++ / 4.1.2 / iostream的:1:   错误:在程序中错误'\ 342'

     

/usr/lib/gcc/i386-redhat-linux/4.1.2 /../../../../包括/ C ++ / 4.1.2 / iostream的:1:   错误:在程序中错误'\ 274'

     

......(反复地,A LOT,大约4000 +)......

     

/usr/lib/gcc/i386-redhat-linux/4.1.2 /../../../../包括/ C ++ / 4.1.2 / iostream的:1:   错误:在程序中错误'\ 257'

     

main.cpp:在函数'int main()'中:

     

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

     

main.cpp:5:错误:'endl'不是'std'的成员

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

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

A&#34; main_utf8.cpp&#34;文件按预期生成。然后我尝试编译它:

  

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

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

最后...

我无法弄清楚我做错了什么。我在网上搜索试图找到这个编译器选项的一些例子,但我不能。

请指教!谢谢!

进一步编辑:

谢谢,伙计们!你的回复很快!一些更新:

  1. 当我说&#34; UTF-16&#34;我的意思是&#34; UTF-16 + BOM&#34;。事实上我使用的是UTF-16BE。我已经更新了上面的文字。
  2. 有些回答说错误是由非UTF-16头文件引起的。以下是我的想法:如果是这样的话:在编写C / C ++项目时,我们总是会包含一些标准的头文件,对吧?比如stdio.h或iostream。如果G ++编译器只处理我们创建的源文件的编码,但从不处理标准库中的源文件,那么这个-finput-charset选项是什么?
  3. 最终编辑:

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

    1. 一开始,我将源文件的编码更改为GB2312,&#34; Mr Lister&#34;下面说。这个工作正常一段时间,但后来我发现它不适合我的情况,因为系统中的大多数其他部分仍然使用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 个答案:

答案 0 :(得分:5)

编码蓝调

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

几乎没有理由将UTF-16用于任何事情,所以这也是一样。

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

Windows上的g ++完全支持与Linux上的g ++相同的所有编码,因为它是相同的程序,除非你在Windows上使用的g ++版本依赖于一个深度破解的iconv库。

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

作为替代方案;不要在源文件中使用中文,而是使用英语文字或简单的TOKEN_STYLE_PLACEHOLDER用英语编写,使用l10ni18n替换正在运行的可执行文件中的这些文件

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

Reedit:下次;记住一句简单的口头禅:“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或类似工具进行交叉编译。

此选项需要一些设置,但可能会很好地减少未来的开销和头痛。

答案 1 :(得分:2)

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

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

修改 当且仅当没有系统源文件包含任何非ASCII字符时,可能有GB编码。然后你可以告诉编译器他们是GB编码没有问题。

答案 2 :(得分:0)

这不起作用,因为编译器也会尝试将头文件读取为UTF-16,但它们不是。

答案 3 :(得分:-1)

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

如果要以字节顺序存储UTF-16,则必须在UTF-16BE和UTF-16LE之间进行选择。