const std :: wstringはどのようにエンコードされ、UTF-16に変更する方法
どちらかのタイプのドイツ語の非ASCII文字で文字列を定義するときに、astd::string
とaのバイトを(16進表現で)比較するために、この最小限の動作をするC ++サンプルスニペットを作成しました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
私はこれをWindows10 64ビットProを実行しているPCで実行し、バージョン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
1バイトの型に基づいています。スニペットの出力は、str
(std::string
変数)がUTF-8でエンコードされていることを示していることがわかります。Microsoftコンパイラはwchar_t
sを2バイトで使用してstd::wstring
(wchar_t
たとえばGNU gccによる4バイトの代わりに)sを構成するため、wstr
(std::wstring
変数)は(あらゆる種類の)UTF-16でエンコードされると予想されることを読みました。しかし、なぜ「ß」(ラテンシャープs)が0x00c30178
期待どおり0x00df
にエンコードされているのか理解できません。誰か教えてください:
- なぜこれが起こっているのですか?
- どうすればUTF-16でエンコードされたものに
std::wstring
なりますか(ビッグエンディアンで問題ありません。BOMは気にしません)?おそらくどういうわけかコンパイラに伝える必要がありますか? - これはどのようなエンコーディングですか?
編集1
質問に適切に適合しなかったため、タイトルを変更しました(実際、UTF-8とUTF-16は異なるエンコーディングであるため、私はすでに新しい答えです...)
編集2
言及するのを忘れた:私amd64
は言及されたコンパイラのターゲットを使用します
編集3
/utf-8
dxivによるコメントで指摘されているようにフラグを追加すると(彼のリンクされた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
ファイル用の優れたスイッチがあります(このQt-Formの投稿を参照してください)。
win32 {
QMAKE_CXXFLAGS += /utf-8
}
それでも私の質問の最初の部分は開いています: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"äöüß";
コンパイラーは次のように認識します。
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)コンパイラスイッチを渡すことにより、ソースファイルがUTF-8としてエンコードされていることをVisual C ++に通知する必要があります。CMakeベースのプロジェクトの場合、これは、CMake / MSVCにBOMなしのソースファイルにUTF-8エンコーディングを使用するように強制add_compile_options
する可能性に示されているように使用できますか?C4819。
したがって、wstr(std :: wstring変数)は(あらゆる種類の)UTF-16でエンコードされることが期待されます
std::wstring
エンコーディングを指定しません。これは、ある種のワイド文字(実装で定義されている)の「ワイド文字」のシーケンスです。
異なるエンコーディングとの間で変換するために、標準ライブラリで定義されている変換ファセットがあります。