VC在GCC中是否有像'-fexec-charset'这样的编译选项来设置执行字符集?

问题描述 投票:11回答:4

GCC有-finput-charset-fexec-charset-fwide-exec-charset三个编译选项,用于指定“编译链”中涉及的特定编码。如下:

+--------+   -finput-charset     +----------+    -fexec-charset (or)    +-----+
| source | ------------------->  | compiler |  -----------------------> | exe |
+--------+                       +----------+    -fwide-exec-charset    +-----+

参考:GCC compiler options

我在这里找到了一个关于-finput-charset的问题:Specification of source charset encoding in MSVC++, like gcc “-finput-charset=CharSet”。但我想知道VC是否在GCC中有像-fexec-charset这样的编译器选项来指定执行字符集。

我在Visual Studio中找到了一个相似的选项:Project Properties/Configuration Properties/General/Character Set。而值是Use Unicode Character Set。它和GCC中的-fexec-charset做的一样吗?这样我想将执行字符集设置为UTF-8。如何?

为什么我要设置执行的编码?

我正在用C ++编写一个需要与db服务器通信的应用程序。桌子的字符集是utf8。在构建一些测试之后,测试将捕获在db表上的插入操作周围抛出的异常。例外告诉我他们遇到不正确的字符串值。我想它是由错误的编码造成的吗?顺便说一句,有没有其他方法来处理这个问题?

c++ visual-c++ gcc utf-8 character-encoding
4个回答
7
投票

AFAIK,VC ++没有命令行标志,可以指定UTF-8执行字符集。然而,它(偶尔)支持无证件

#pragma execution_character_set("utf-8")

提到here

要使用此pragma获取命令行标志的效果,您可以在头文件中编写pragma,例如preinclude.h,并通过传递标志/FI preinclude.h在每个编译中预先包含此标头。有关如何从IDE设置此标志,请参阅this documentation

该编译指示在VC ++ 2010中得到支持,然后在VC ++ 2012中被遗忘,并在VC ++ 2013中再次得到支持


2
投票

应该注意的是,pragma execution_character_set似乎只适用于字符串文字("Hello World")而不适用于宽字符串文字(L"Hello World")。

我做了一些实验来了解如何在MSVC中实现源和执行字符集。我在Windows系统上使用Visual Studio 2015进行了实验,其中CP_ACP是1252,并总结如下结果:

字符文字

  • 如果MSVC将源文件确定为Unicode文件,即它以UTF-8或UTF-16编码,则会将字符转换为CP_ACP。如果Unicode字符不在CP_ACP的范围内,则MSVC发出C4566警告(“由通用字符名''U0001D575表示的字符'无法在当前代码页(1252)中表示”)。 MSVC假定编译软件的执行字符集是编译器的CP_ACP。这意味着你应该在目标环境的CP_ACP下编译软件,即如果你想在代码页为1252的Windows系统上执行软件,你应该在代码页1252下编译它而不是在任何其他系统上执行它。代码页。实际上,如果您的文字是ASCII编码(C0控制和基本拉丁语Unicode块),它可能会起作用,因为大多数常见的SBCS代码页扩展了这种编码。但是,有一些没有,特别是DBCS代码页
  • 如果MSVC确定源文件不是Unicode文件,它将根据CP_ACP解释源文件,并假定执行字符集为CP_ACP。与Unicode文件一样,您应该在目标环境的CP_ACP下编译软件并遇到相同的问题。

所有“ANSI”Windows API函数(例如CreateFileA)根据LPSTRCP_ACP(默认为CP_THREAD_ACP)解释CP_ACP类型的字符串。要找出哪些函数使用CP_ACPCP_THREAD_ACP并不容易,所以最好永远不要改变CP_THREAD_ACP

宽字符文字

宽字符文字的执行字符集始终为Unicode,编码为UTF-16LE。所有宽字符Windows API函数(例如CreateFile)都将LPWSTR类型的字符串解释为UTF-16LE字符串。这也意味着wcslen不返回Unicode字符的数量,而是返回宽字符串的wchar_t数字。在某些情况下,UTF-16也与UCS-2不同。

  • 如果MSVC将源文件确定为Unicode文件,则会将字符转换为UTF-16LE。
  • 如果MSVC确定源文件不是Unicode文件,则它根据CP_ACP读取文件并将字符扩展为两个字节而不解释它们。也就是说,如果一个字符在0xFF中被编码为CP_ACP,它将被写为0x00 0xFF,无论CP_ACP字符0xFF是否是Unicode字符U+00FF

我没有机会在DBCS Windows系统上重复我的实验,因为我不会说通常使用这些代码页的语言。也许有些人可以在这样的系统上重复实验。

对我来说,实验的结论是你应该避免使用字符文字,即使你使用execution_character_set编译指示。

该pragma只是更改了二进制文件中字符串文字的编码方式,但不会更改您使用的库或内核的执行字符集。如果你想使用execution_character_set编译指示,你必须使用pragma重新编译Windows和你完全使用的所有其他库,这当然是不可能的。所以我建议不要使用它。它可能适用于某些系统,因为UTF-8适用于CRT中的大多数字符串函数,而CP_ACP通常包含ASCII,但您应该检查这些假设是否真的存在于您的目标环境中,以及这种误用所需的努力是否真的值得。此外,该pragma似乎没有文档,我可能不会在将来的版本中工作。

否则,您必须为目标系统中使用的所有代码页编译单独的二进制文件。避免多个二进制文件的唯一方法是将所有字符串外部化为UTF-16LE编码的资源,并在需要时将字符串转换为CP_ACP。在这种情况下,您必须将资源脚本(.rc文件)保存为UTF-8,使用rc调用/c65001(UTF-16LE不起作用)并包含目标系统中正在使用的所有代码页的字符串。

我建议使用Unicode编码(例如UTF-8或UTF-16LE)对文件进行编码,如果不能将字符串外部化为资源并使用UNICODE_UNICODE定义编译,则使用宽字符文字。不管怎么说,不建议使用字符串和字符文字,而不喜欢资源。使用WideCharacterToMultiByteMultiByteToWideChar作为期望根据CP_ACP或其他代码页编码的字符串的函数。

MSVC的源编码检测启发式最适合启用BOM(即使在UTF-8中)。

我不是亚洲语言的专家,但我读到汉语中的统一是有争议的。因此,使用Unicode可能不是所有问题的解决方案,并且可能存在不符合要求的情况,但我会说,对于大多数语言,Unicode在Windows下最有效。

微软没有明确这一点并记录其编译器和操作系统的行为是错误的。


1
投票

Visual Studio 2015 Update 2及更高版本supports setting the execution character set

您可以使用选项/utf-8,它结合了选项/source-charset:utf-8/execution-charset:utf-8。从上面的链接:

在那些已经存在无BOM的UTF-8文件或更改为BOM的问题的情况下,使用/ source-charset:utf-8选项来正确读取这些文件。

在Linux和Windows之间使用/ execution-charset或/ utf-8可以提供帮助,因为Linux通常使用无BOM的UTF-8文件和UTF-8执行字符集。

Project Properties/Configuration Properties/General/Character Set仅设置宏Unicode / MBCS,但不设置源字符集或执行字符集。


0
投票

感谢@ user3998276的回答和伟大的实验。

结论告诉了我很多

  • 遇到L“字符串”时,宽字符串: 编译器首先检测cpp-file-encoding,然后: Unicode - >只需使用utf-16 //这里也可能有转换,比如u8到u16。 ACP - >将Unicode字符串转换为ACP
  • 遇到“字符串”时,普通的字符串文字: 编译器首先检测cpp-file-encoding,然后 Unicode - >将Unicode字符转换为ACP字符 ACP - >只需根据ACP读取源文件

至于你的问题,我认为'对db表的插入操作'只是对db插入API的调用。因此,您需要做的就是在UTF8中组织命令,如SQL。一旦API可以理解您的命令,它就可以为您编写正确的值(想象二进制蒸汽)。

尝试:

  • 在c ++ 11及更高版本中,您可以通过前缀'u8'指定utf-8字符串,如

u8"INSERT INTO table_name (col1, col2,...) VALUES (v1, v2,....)"

http://en.cppreference.com/w/cpp/language/string_literal

  • 使用第三方字符串包装器,如QT的QString。 首先将您的SQL包装到QString,然后它可以很容易地转换为utf8,QByteArray x = mySql.toUtf8()。 QByteArray只是'byte of array',因此您可以将static_cast转换为插入API所需的类型。

再次仔细阅读@ user3998276的答案,如果有一些字符无法在您的ANSI代码页中表示,则可能需要将cpp文件的编码更改为Unicode。

© www.soinside.com 2019 - 2024. All rights reserved.