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


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

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

参考: GCC编译器选项

我发现了一个问题 -finput-charset 这里: 在MSVC ++中编码源字符集编码,如gcc“-finput-charset = CharSet”。但我想知道是否 VC 有一个编译器选项,如 -fexec-charset 在海湾合作委员会 指定执行字符集

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

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

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


7836
2018-05-12 11:30


起源



答案:


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

#pragma execution_character_set("utf-8")

提到 这里

要使用此pragma获取命令行标志的效果,可以在标头中编写pragma 文件,比方说, preinclude.h 并通过传递在每个编译中预先包含此标头 国旗 /FI preinclude.h。看到 这个文件  有关如何从IDE设置此标志。

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


7
2018-05-12 14:01



谢谢:)它在VS2013中完美运行。在db操作之前,我不需要使用boost :: locale将字符串值转换为utf-8。 - Ggicci


答案:


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

#pragma execution_character_set("utf-8")

提到 这里

要使用此pragma获取命令行标志的效果,可以在标头中编写pragma 文件,比方说, preinclude.h 并通过传递在每个编译中预先包含此标头 国旗 /FI preinclude.h。看到 这个文件  有关如何从IDE设置此标志。

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


7
2018-05-12 14:01



谢谢:)它在VS2013中完美运行。在db操作之前,我不需要使用boost :: locale将字符串值转换为utf-8。 - Ggicci


应该注意的是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)解释类型的字符串 LPSTR 根据 CP_ACP 要么 CP_THREAD_ACP (默认为 CP_ACP)。找出哪些功能使用起来并不容易 CP_ACP 要么 CP_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,您必须使用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 定义。不管怎样,不建议使用字符串和字符文字,而是更喜欢资源。使用 WideCharacterToMultiByte 和 MultiByteToWideChar 对于期望根据编码的字符串的函数 CP_ACP 或其他一些代码页。

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

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

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


2
2018-01-27 12:01





Visual Studio 2015 Update 2及更高版本 支持设置 执行字符集

您可以使用该选项 /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但不设置 源字符集 要么 执行字符集


1
2018-05-02 13:56





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

结论告诉了我很多

  • 遇到L“字符串”时,宽字符串:

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

    • 编译器首先检测cpp-file-encoding,然后
      • Unicode - >将Unicode字符转换为ACP字符
      • ACP - >只需根据ACP读取源文件

至于你的问题,我认为'对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只是'字节数组',所以你可以将static_cast转换为类型 插入API 想。

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


0
2017-08-12 14:58