const std :: wstring 인코딩 방법 및 UTF-16으로 변경하는 방법

Nov 30 2020

이 최소 작동 C ++ 예제 스 니펫을 작성 하여 두 유형의 독일어 비 ASCII 문자로 문자열을 정의 할 때 a std::string와 a의 바이트 (16 진수 표현으로)를 비교했습니다 std::wstring.

#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-bit Pro를 실행하는 PC에서 실행 하고 다음과 같이 빌드 시스템 cmake 를 사용하여 버전 16.8.1의 MSVC 2019 Community Edition 으로 컴파일했습니다.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::strings가 char단일 바이트 유형을 기반으로 한다는 것을 읽었습니다 . 내 스 니펫의 출력은 str( std::string변수)가 UTF-8로 인코딩 되었음을 나타냅니다 . 필자는 Microsoft 컴파일러가 wchar_ts를 구성하기 위해 2 바이트의 s를 사용 하므로 (예 : GNU gcc std::wstring의 4 바이트 대신) wchar_ts를 사용하므로 wstr( std::wstring변수)가 (모든 종류의) UTF-16으로 인코딩 될 것으로 예상 합니다 . 그러나 "ß"(라틴 샤프 s)가 0x00c30178예상대로 인코딩 된 이유를 알 수 없습니다 0x00df. 누군가 저에게 말해주세요 :

  • 왜 이런 일이 발생합니까?
  • 어떻게 UTF-16으로 인코딩 된 std::wstrings로 끝날 수 있습니까 (Big Endian은 괜찮을 것입니다. BOM은 상관 없습니다)? 어떻게 든 컴파일러에게 알려야합니까?
  • 이것은 어떤 종류의 인코딩입니까?

편집 1

질문에 제대로 맞지 않았기 때문에 제목이 변경되었습니다 (실제로 UTF-8과 UTF-16은 인코딩이 다르기 때문에 내 자신이 이미 답을 얻었습니다 ...)

2 편집

언급하는 것을 잊었습니다 : amd64언급 된 컴파일러 의 타겟을 사용합니다.

3 편집

/utf-8dxiv의 주석에서 지적한대로 플래그를 추가하면 ( 링크 된 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- 프로젝트의 경우 .pro파일에 대한 멋진 스위치가 있습니다 ( 이 Qt-Form 게시물 참조 ).

win32 {
    QMAKE_CXXFLAGS += /utf-8
}

여전히 내 질문의 첫 번째 부분이 열려 있습니다. 0x00c30178"ß"(라틴 샤프 s)의 인코딩은 무엇입니까 ?

답변

5 dxiv Dec 01 2020 at 16:41

주석에서 명확히 알 수 있듯이 소스 .cpp파일은 UTF-8로 인코딩됩니다. BOM이없고 명시 적 /source-charset:utf-8스위치가 없는 경우 Visual C ++ 컴파일러는 기본적으로 소스 파일이 활성 코드 페이지 인코딩에 저장되어 있다고 가정합니다. 로부터 설정 소스 문자 세트 문서 :

기본적으로 Visual Studio는 바이트 순서 표시를 감지하여 소스 파일이 인코딩 된 유니 코드 형식 (예 : UTF-16 또는 UTF-8)인지 확인합니다. 바이트 순서 표시가 발견되지 않으면 / source-charset 옵션을 사용하여 문자 세트 이름 또는 코드 페이지를 지정하지 않는 한 소스 파일이 현재 사용자 코드 페이지를 사용하여 인코딩 된 것으로 간주합니다.

의 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"`;

이러한 (잘못된) 번역을 피하기 위해 Visual C ++는 명시 적 /source-charset:utf-8(또는 /utf-8) 컴파일러 스위치 를 전달하여 소스 파일이 UTF-8로 인코딩되었음을 알려야합니다. CMake 기반 프로젝트의 경우 CMake / MSVC에서 BOM이없는 소스 파일에 UTF-8 인코딩을 사용하도록 강제 할 수 있음에 add_compile_options표시된 대로이 작업을 수행 할 수 있습니다 . C4819 .

MarshallClow Nov 30 2020 at 20:50

따라서 wstr (std :: wstring 변수)이 (모든 종류의) UTF-16으로 인코딩 될 것으로 예상합니다.

std::wstring인코딩을 지정하지 않습니다. 일종의 와이드 문자 (구현 정의 됨)에 대한 "와이드 문자"의 시퀀스입니다.

다른 인코딩으로 /에서 변환하기 위해 표준 라이브러리 에 정의 된 변환 패싯이 있습니다 .