95264


Microsoft Visual Studio Community 2022  
Version 17.4.4  
VisualStudio.17.Release/17.4.4+33213.308  
Microsoft .NET Framework  
Version 4.8.04084

以下のソースを実行します。

[code]

    int main()
    {
        char c[] = "ソ";
        std::string s1 = "ソ";
        std::wstring s2 = L"ソ";
    
        return 0;
    }
    
[/code]

このとき、後述する設定であれば変数"c"内の値がUTF-8の解釈である「E3 82 BD」に  
なると思っていましたが、実際は下図の通り、Shift_JISの解釈である「83 5C」となっています。

実行時の"c"のメモリ  
[![画像の説明をここに入力](https://i.stack.imgur.com/O44UR.png)](https://i.stack.imgur.com/O44UR.png)

文字セットの設定：Unicode文字セットを使用する  
[![画像の説明をここに入力](https://i.stack.imgur.com/RboWi.png)](https://i.stack.imgur.com/RboWi.png)

/utf-8オプションは付けていません。  
[![画像の説明をここに入力](https://i.stack.imgur.com/ETuY3.png)](https://i.stack.imgur.com/ETuY3.png)

ファイルエンコード：UTF-8 BOM付  
[![画像の説明をここに入力](https://i.stack.imgur.com/mY2Zy.png)](https://i.stack.imgur.com/mY2Zy.png)

なぜ、Unicode文字セットを使用してファイルエンコードはUTF-8なのに  
Shift_JISの解釈がされているのでしょうか？

ちなみに、ファイルをUTF-8(BOM無し)で保存して実行すると  
以下のようになります。  
[![画像の説明をここに入力](https://i.stack.imgur.com/mGJxu.png)](https://i.stack.imgur.com/mGJxu.png)

しかし、この場合は「C4819: ファイルは、現在のコード ページ (932) で表示できない文字を含んでいます。」という警告が表示されます。  
挙動がよく理解できないのですが、ご教示頂けますでしょうか…


CC BY-SA 4.0
2023-06-16T10:22:51.863
95259
2023-06-16T13:59:10.477
53943
question
c++ unicode
Unicode文字セットでもShift_JISで解釈される


おそらくこちらの記事の内容が適用されているものと思われます。  
[文字列リテラルと文字リテラル (C++)](https://learn.microsoft.com/ja-jp/cpp/cpp/string-and-
character-literals-cpp?view=msvc-170)

> 文字リテラル  
>  文字リテラル は、定数文字で構成されます。 これは単一引用符で囲んだ文字によって表されます。 文字リテラルには次の 4 種類があります:
>
>   * `char`型の通常文字リテラル。たとえば`'a'`
>   * `char`型の UTF-8 文字リテラル (C++20 では`char8_t`)。たとえば`u8'a'`
>   * `wchar_t`型のワイド文字リテラル。たとえば`L'a'`
>   * `char16_t`型の UTF-16 文字リテラル。たとえば`u'a'`
>   * `char32_t`型の UTF-32 文字リテラル。たとえば`U'a'`
>

> エンコード  
>  文字リテラルは、プレフィックスに基づいて異なる方法でエンコードされます。
>
>   * プレフィックスのない文字リテラルは、通常の文字リテラルです。 実行文字セットで表すことができる単一の文字、エスケープ
> シーケンス、またはユニバーサル文字名を含む通常の文字リテラルの値には、実行文字セットにおけるそのエンコードの数値と同じ値が設定されています。
> 複数の文字、エスケープ シーケンス、またはユニバーサル文字名を含む通常の文字リテラルは、多文字リテラルです。
> 実行文字セットで表現できない多文字リテラルまたは通常の文字リテラルには型`int`があり、その値は実装定義されています。 MSVC
> については、以下のMicrosoft 固有の項を参照してください。
>

> 文字列リテラル  
>  文字列リテラルは文字のシーケンスを表し、その全体が、null で終わる文字列を形成します。 文字列は二重引用符で囲む必要があります。
> 文字列リテラルの種類は次のとおりです。

> ナロー文字列リテラル  
>  ナロー文字列リテラルは、プレフィックスがなく、二重引用符で区切られ、null で終わる`const char[n]`型の配列です。

> UTF-8 でエンコードされた文字列  
>  UTF-8 でエンコードされた文字列は、u8 プレフィックスが付き、二重引用符で区切られ、null で終わる`const
> char[n]`型の配列です。

`多文字リテラル`は英語版の記事では`multicharacter
literal`であり、Windowsでは`MBCS`に該当すると思われます。`ナロー文字列リテラル`も同様でしょう。  
そして日本語WindowsではMBCSはCodePage932(いわゆるShiftJIS)として扱われるのでしょう。

なので質問のように、`char c[]`や`std::string
s1`の初期化をUTF-8で行いたいならば、リテラルの前に`u8`を付ける必要があるのでしょう。

[code]

        char c[] = u8"ソ";
        std::string s1 = u8"ソ";
    
[/code]

* * *

なお、プロジェクトのプロパティでUNICODEかMBCSかを決める場合は、`char`そのものを使うのではなく、`TCHAR`や`_TCHAR`に関連する型・マクロ・関数を使うことで実現されています。  
[_MBCSでの tchar.h データ型の使用](https://learn.microsoft.com/ja-jp/cpp/c-runtime-
library/using-tchar-h-data-types-with-mbcs?view=msvc-170)  
[汎用テキストのプログラム例](https://learn.microsoft.com/ja-jp/cpp/c-runtime-
library/a-sample-generic-text-program?view=msvc-170)


CC BY-SA 4.0
2023-06-16T12:29:54.677
95262
2023-06-16T12:44:57.290
2023-06-16T12:44:57.290
26370
26370
95259
answer


MSVCには、[ソースコードのエンコーディング](https://learn.microsoft.com/ja-
jp/cpp/build/reference/source-charset-set-source-character-
set?view=msvc-170)(`/source-charset`)と、  
[実行時の文字エンコーディング](https://learn.microsoft.com/ja-
jp/cpp/build/reference/execution-charset-set-execution-character-
set?view=msvc-170)(`/execution-charset`)があります。  
`/utf-8`オプションも`/execution-
charset`オプションも付けていなければ、実行時の文字エンコーディングはCP932(≒Shift_JIS)のはずです。

> バイト順マークが見つからない場合は、/source-charset オプションまたは /utf-8 オプションを使用して文字セット名またはコード
> ページを指定していない限り、現在のユーザー コード ページでソース ファイルがエンコードされていると想定されます。

とあるので、ソースコードにBOMが付いていればソースコードはUTF-8と解釈され、実行時の文字エンコーディングはCP932なので`ソ`は`83
5C`となります。

ソースコードにBOMが付いてなければCP932して扱われ、エディターで`ソ`に見えているものはコンパイラーにとっては(無理やり解釈して)CP932の`繧ｽ`として扱われ、実行時のエンコーディングもCP932なので多文字リテラルとしてそのまま`繧ｽ`(`E3
82 BD`)というバイト列にエンコーディングされています。


CC BY-SA 4.0
2023-06-16T13:01:14.540
95263
2023-06-16T13:31:02.423
2023-06-16T13:31:02.423
9113
9113
95259
answer


ソースコードの文字コードとコンパイル後の実行バイナリ上でのプリフィックスの無い文字列リテラルの文字コードは無関係に決定します。

まず、ソースコードの文字コードは、オプションで指定しない限り、ソースコードの先頭にあるバイト列によって変わります。

  * [`/source-charset`オプション](https://learn.microsoft.com/ja-jp/cpp/build/reference/source-charset-set-source-character-set?view=msvc-170)が設定されている場合は、指定の文字コードとして解釈されます。
  * オプションが無く、先頭にUTF-8やUTF-16のBOM(U+FEFF)で始まる場合は、BOM付きUTF-8、BOM付きUTF-16 BE、BOM付きUTF-16 BEとして解釈されます。(UTF-32も対応しているかは不明)
  * オプションが無く、先頭にUTF-8やUTF-16のBOM(U+FEFF)で始まらない場合は、その実行環境のシステムロケールで設定されたエンコード(日本語の場合はShfit_JISの亜種であるCP932(Windows-31J))として解釈されます。

たとえば、BOM無しのUTF-8の場合、日本語環境ではShift_JISとして解釈しようとします。UTF-8の"ソ"(U+30BD)は、[E3 82
BD]のバイト列になりますが、これをShfit_JISとみなすので、"繧"(E382)と"ｽ"(BD)になります。これが、「ファイルをUTF-8(BOM無し)で保存して」の時に文字化けした文字が出ている理由です。

対して、コンパイル後の実行バイナリ上のプリフィックスの無い文字列リテラルの文字コードは、次のようになります。

  * [`/execution-charset`オプション](https://learn.microsoft.com/ja-jp/cpp/build/reference/execution-charset-set-execution-character-set?view=msvc-170)※が設定されている場合は、指定の文字コードになります。
  * オプションが無い場合は、その実行環境のシステムロケールで設定された文字コード(日本語の場合はShfit_JISの亜種であるCP932(Windows-31J))になります。

※ リンク先の説明の二段目は`/source-charset`の説明のコピペのようで、`/execution-
charset`についての既定の動作の説明が抜けているようです。

なお、[`/utf-8`オプション](https://learn.microsoft.com/ja-
jp/cpp/build/reference/utf-8-set-source-and-executable-character-sets-to-
utf-8?view=msvc-170)は上記の両方のオプションでUTF-8を指定するショートカットです。

ということで、BOM付きUTF-8であっても、"ソ"(U+30BD)は、実行バイナリ上はCP932の表現である[83
5C]になります。もし、`/execution-charset:utf-8`をつければ、ソースコードがCP932であっても[E3 82 BD]になります。

なお、プリフィックスが付いた文字列リテラルについてはまた違う動作になるので、それについてはkunifさんの回答を参考にしてください。


CC BY-SA 4.0
2023-06-16T13:59:10.477
95264
2023-06-16T13:59:10.477
7347
95259
answer