visual-c++ - const std::wstring 是如何编码的以及如何更改为 UTF-16

标签 visual-c++ utf-8 c++17 utf-16 wstring

我创建了这个最小工作 C++ 示例片段来比较 std::string 中的字节(通过十六进制表示)和一个 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;
}

此代码段的输出是

c3 a4 c3 b6 c3 bc c3 9f 
00c3 00a4 00c3 00b6 00c3 00bc 00c3 0178

我在运行Windows 10 64位专业版的电脑上运行此程序,使用版本16.8.1中的MSVC 2019 Community Edition进行编译,使用构建系统 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)

我读到,std::string s 基于char type 是一个单字节。我看到我的代码片段的输出表明 str (std::string 变量)采用 UTF-8 编码。我读到,微软编译器使用 wchar_t s用2个字节组成std::wstring s(而不是 4 字节 wchar_t s,例如 GNU gcc),因此期望 wstr (std::wstring 变量)进行(任何类型的)UTF-16 编码。但我不明白为什么“ß”(拉丁升音 s)被编码为 0x00c30178我预料到了0x00df反而。请有人告诉我:

  • 为什么会发生这种情况?
  • 如何才能得到 UTF-16 编码 std::wstring s(大端就可以了,我不介意 BOM)?我是否需要以某种方式告诉编译器?
  • 这是什么类型的编码?

编辑1

更改了标题,因为它不适合问题(实际上 UTF-8 和 UTF-16 是不同的编码,所以我自己已经有了新的答案......)

编辑2

忘了提:我用amd64上述编译器的目标

编辑3

如果添加/utf-8如 dxiv 评论中指出的标志(参见 his linked SO-Post ),我得到了所需的输出

c3 a4 c3 b6 c3 bc c3 9f
00e4 00f6 00fc 00df

对我来说看起来像 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)

我找到了if-endif比生成器语法更具可读性,但编写 add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")相反也可以。

注意:对于 Qt-Projects,有一个很好的开关 .pro文件(参见 this Qt-Form post )

win32 {
    QMAKE_CXXFLAGS += /utf-8
}

我的问题的第一部分仍然是开放的:什么编码是 0x00c30178代表“ß”(拉丁升音 s)?

最佳答案

正如注释中所阐明的,源 .cpp 文件是 UTF-8 编码的。如果没有 BOM,并且没有显式的 /source-charset:utf-8 开关,Visual C++ 编译器默认假设源文件以事件代码页编码保存。来自 Set Source Character Set 文档:

By default, Visual Studio detects a byte-order mark to determine if the source file is in an encoded Unicode format, for example, UTF-16 or UTF-8. If no byte-order mark is found, it assumes the source file is encoded using the current user code page, unless you specify a character set name or code page by using the /source-charset option.

äöüß 的 UTF-8 编码为 C3 A4 C3 B6 C3 BC C3 9F,因此该行:

    std::wstring wstr = L"äöüß";

被编译器视为:

    std::wstring wstr = L"\xC3\xA4\xC3\xB6\xC3\xBC\xC3\x9F"`;

假设事件代码页是通常的 Windows-1252 ,(扩展)字符映射为:

    win-1252    char    unicode

      \xC3       Ã       U+00C3
      \xA4       ¤       U+00A4
      \xB6       ¶       U+00B6
      \xBC       ¼       U+00BC
      \x9F       Ÿ       U+0178

因此 L"\xC3\xA4\xC3\xB6\xC3\xBC\xC3\x9F" 被翻译为:

    std::wstring wstr = L"\u00C3\u00A4\u00C3\u00B6\u00C3\u00BC\u00C3\u0178"`;

为了避免这种(错误)翻译,需要通过传递显式 /source-charset:utf-8 (或 /utf-8 )来告知 Visual C++ 源文件已编码为 UTF-8编译器开关。对于基于 CMake 的项目,可以使用 add_compile_options 来完成,如 Possible to force CMake/MSVC to use UTF-8 encoding for source files without a BOM? C4819 所示。

关于visual-c++ - const std::wstring 是如何编码的以及如何更改为 UTF-16,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65080667/

相关文章:

c++ - 在对象工厂中注册一个对象创建者

cocoa - NSString 和 UTF8 十六进制转换

c++ - 我可以像函数参数的 std::optional 那样使模板参数可选吗?

c++ - 如何处理递归?

c++ - VC++ 与 GCC 预处理器

php - rawurldecode 的 utf-8 问题和浏览器地址栏问题

c++ - 解析以 UTF-8 编码的 XML

c++ - 在 std::array 上使用 std::get 会提供更好的性能吗?

C++:线程竞争条件是否会破坏每字节级别的静态/全局整数值?

c++ - dllexport意外不会在引用项目时导致链接器错误