Mar*_*tin 2 utf-8 utf-16 wstring visual-c++ c++17
我创建了这个最小的工作 C++ 示例代码片段来比较 astd::string和 a中的字节(通过它们的十六进制表示)在std::wstring定义一个带有德国非 ASCII 字符的字符串时。
#include <iostream>
#include <iomanip>
#include <string>
int main(int, char**) {
std::wstring wstr = L"äöüß";
std::string str = "äöüß";
for ( unsigned char c : str ) {
std::cout << std::setw(2) << std::setfill('0') << std::hex << static_cast<unsigned short>(c) << ' ';
}
std::cout << std::endl;
for ( wchar_t c : wstr ) {
std::cout << std::setw(4) << std::setfill('0') << std::hex << static_cast<unsigned short>(c) << ' ';
}
std::cout << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这个片段的输出是
c3 a4 c3 b6 c3 bc c3 9f
00c3 00a4 00c3 00b6 00c3 00bc 00c3 0178
Run Code Online (Sandbox Code Playgroud)
我在自己运行Windows 10 64-bit Pro的 PC 上运行它,使用版本 16.8.1 中的MSVC 2019 社区版编译,使用构建系统cmake和以下内容CMakeLists.txt
cmake_minimum_required(VERSION 3.0.0)
project(wstring VERSION 0.1.0)
set(CMAKE_CXX_STANDARD 17)
include(CTest)
enable_testing()
add_executable(wstring main.cpp)
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
Run Code Online (Sandbox Code Playgroud)
我读到,这std::string是基于char单字节的类型。我看到我的代码片段的输出表明str(std::string变量)是UTF-8编码的。我读到,Microsoft 编译器使用wchar_ts 和 2 个字节来组成std::wstrings(而不是 4 个字节wchar_ts,例如 GNU gcc),因此希望wstr(std::wstring变量)是(任何类型的)UTF-16编码。但我无法弄清楚为什么“ß”(拉丁文尖 s)按照0x00c30178我的预期进行编码0x00df。请有人告诉我:
std::wstrings(Big Endian 没问题,我不介意 BOM)?我可能需要以某种方式告诉编译器吗?更改了标题,因为它不适合问题(实际上 UTF-8 和 UTF-16 是不同的编码,所以我自己已经找到了新的答案......)
忘了提:我使用amd64提到的编译器的目标
如果添加/utf-8dxiv 在评论中指出的标志(请参阅他链接的 SO-Post),我会得到所需的输出
c3 a4 c3 b6 c3 bc c3 9f
00e4 00f6 00fc 00df
Run Code Online (Sandbox Code Playgroud)
对我来说看起来像 UTF-16-BE(没有 BOM)。由于我对 cmake 命令的正确顺序有疑问,这是我当前的CmakeLists.txt文件。将add_compile_options命令放在命令之前很重要add_executable(为了方便,我添加了通知)
cmake_minimum_required(VERSION 3.0.0)
project(enctest VERSION 0.1.0)
set(CMAKE_CXX_STANDARD 17)
include(CTest)
enable_testing()
if (MSVC)
message(NOTICE "compiling with MSVC")
add_compile_options(/utf-8)
endif()
add_executable(enctest main.cpp)
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
Run Code Online (Sandbox Code Playgroud)
我发现这种if-endif方式比生成器语法更具可读性,但写作add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")也可以。
注意:对于 Qt-Projects,.pro文件有一个很好的开关(请参阅此 Qt-Form 帖子)
win32 {
QMAKE_CXXFLAGS += /utf-8
}
Run Code Online (Sandbox Code Playgroud)
我的问题的第一部分仍然是开放的:0x00c30178“ß”(拉丁文尖 s)的编码是什么?
如注释中所述,源.cpp文件采用 UTF-8 编码。如果没有 BOM 和显式/source-charset:utf-8开关,Visual C++ 编译器默认假设源文件以活动代码页编码保存。从设置源字符集文档:
默认情况下,Visual Studio 检测字节顺序标记以确定源文件是否采用编码的 Unicode 格式,例如 UTF-16 或 UTF-8。如果未找到字节顺序标记,则假定源文件使用当前用户代码页进行编码,除非您使用 /source-charset 选项指定字符集名称或代码页。
的 UTF-8 编码äöüß是C3 A4 C3 B6 C3 BC C3 9F,因此该行:
std::wstring wstr = L"äöüß";
Run Code Online (Sandbox Code Playgroud)
被编译器视为:
std::wstring wstr = L"\xC3\xA4\xC3\xB6\xC3\xBC\xC3\x9F"`;
Run Code Online (Sandbox Code Playgroud)
假设活动代码页是通常的Windows-1252,(扩展)字符映射为:
win-1252 char unicode
\xC3 Ã U+00C3
\xA4 ¤ U+00A4
\xB6 ¶ U+00B6
\xBC ¼ U+00BC
\x9F Ÿ U+0178
Run Code Online (Sandbox Code Playgroud)
因此L"\xC3\xA4\xC3\xB6\xC3\xBC\xC3\x9F"被翻译为:
std::wstring wstr = L"\u00C3\u00A4\u00C3\u00B6\u00C3\u00BC\u00C3\u0178"`;
Run Code Online (Sandbox Code Playgroud)
为了避免这种(错误)翻译,需要通过显式/source-charset:utf-8(或/utf-8)编译器开关告诉 Visual C++ 源文件编码为 UTF-8 。对于基于 CMake 的项目,这可以使用add_compile_options如可能强制 CMake/MSVC 对没有 BOM 的源文件使用 UTF-8 编码中所示来完成?C4819。