1

悲しいことに、今週私が質問を投稿しなければならないのはこれで 3 回目です。

Unicode エンコーディング (または UTF8) でテキストをファイルに書き込む必要があります。
これが私がすることです:

作成してから、このようwofstream mystream;に入れますwstringmystream << L"hello world";

最初の質問: 私の場合、ストリームはどのようなエンコーディングを使用していますか?

第二に、新しいファイルをロードしたいのですが、どのように行を読むのですか? ifstreamラインがgetline明らかに台無しになってしまうため、 は機能していません。

4

2 に答える 2

8

「ユニコードエンコーディング」とは、UTF-16を意味していると思います。実際には、Unicode エンコーディングと呼ばれる可能性のあるエンコーディングがいくつかありますが、Unicode に慣れていないほとんどの人は、それを UTF-16 を意味すると考えています (Microsoft がすべてのドキュメントでこの間違いを犯しているためだと思います)。私の答えは、あなたが Windows 用のコードを書いていることを前提としているため、内部データは wchar_t 文字列に格納された UTF-16 であると仮定しています。


ワイド ストリーム オブジェクトを使用しても、ファイルの入力または出力がワイド文字を使用して行われるとは限りません。実際、ワイド ストリームは、ストリームの文字型 (wchar_t) と char の間で変換するために、ストリームのロケールの codecvt ファセットを使用します。

C++11 には、UTF-16 または UTF-8 の入出力を行うために使用できる codecvt ファセットがいくつかあります。codecvt_utf8, codecvt_utf16, codecvt_utf8_utf16.

codecvt_utf8外部 UTF-8 マルチバイト シーケンスと内部 UTF-32/UCS4 または UCS2 データの間で変換します。codecvt_utf16外部 UTF-16 マルチバイト シーケンスと内部 UTF-32/UCS4 または UCS2 データの間で変換します。codecvt_utf8_utf16外部 UTF-8 マルチバイト シーケンスと内部 UTF-16 データの間で変換します。

外部の UTF-16 マルチバイト シーケンスと内部の UTF-16 データの間で変換する組み込みの方法はありません。これは、UTF-16 でエンコードされた wchar_t 文字列を内部で使用し、UTF-16 でエンコードされたファイルを外部で使用する場合に必要です。

しかし、あなたは UTF-8 出力が受け入れられることを示したので、codecvt_utf8_utf16 ファセットはうまく機能します。

#include <fstream>
#include <codecvt>

int main() {
    std::wofstream mystream("test.txt");
    mystream.imbue(std::locale(std::locale(),
                   new std::codecvt_utf8_utf16<wchar_t, 0x10ffff, std::codecvt_mode(std::consume_header|std::generate_header)>));
    mystream << "Hello, World!\n";
}

また、この例では codecvt_utf8_utf16 ファセットのオプションを設定して、いわゆる「UTF-8 BOM」を生成して読み取ることにも注意してください。これは、ファイルのエンコーディングを推測するための Microsoft の規則であり、通常、他のプラットフォームでは不適切です。


以下は当面の質問には関係ありませんが、ファセットのライフタイム管理は、他のほとんどの最新の C++ ライフタイム管理とは異なります。

ファセットは参照カウントされ、特定のファセットを持つ最後のロケールが破棄されると、 の refs パラメータでファセットを構築することによって特に無効にされていない限り、ファセットは削除されます1。上記のコード例では、有効期間の管理をロケールに任せているため、メモリ リークのように見えます。ただし、コードは正しいです。例外の安全性の観点から、割り当てが成功してから、割り当てられたオブジェクトの所有権がロケールによって想定されるまでの間に潜在的に実行できる唯一のコードは、std::locale()noexcept と宣言された式です。

もう 1 つのオプションは、ロケールによって管理されていないファセットを使用し、それがロケールとすべてのコピーより長く存続するようにすることです。静的ストレージ期間を持つファセットを使用するのは簡単ですが、参照カウントを 1 に設定して、ロケールがファセットを削除してはならないことを示すことを忘れないでください。

static std::codecvt_utf8_utf16<wchar_t, 0x10ffff, std::codecvt_mode(std::consume_header|std::generate_header)> mycodecvt(1);
mystream.imbue(std::locale(std::locale(), mycodecvt));

ロケールが特定のスコープに短期間しか存在しない場合は、通常のローカル変数を使用できます。これは上記と同じですが、 がありませんstatic。ファセットが範囲外になる前に、ロケール (およびすべてのコピー) が破棄されていることを確認してください。

これは、スマート ポインターを無視したオブジェクトへの所有権の引き渡しが難しいため、スマート ポインターがうまくいかない場合の 1 つです。ロケールがファセットを受け取って所有権を取得した後、スマート ポインターが所有権を放棄する前に発生する例外を手動で処理する方法を理解する必要があります。

于 2012-10-08T21:42:10.103 に答える
1

wchar_t、バックアップするタイプwstreamおよびwstringは、プラットフォームに依存します。Windows では 2 バイト、一部の (すべて?) Linux では 4 バイトです。したがって、「Unicode」と書くことになりますが、正確にはどの Unicode が多くの変数の影響を受けます。UTF32/UCS4 と書いても、UTF16/UCS2 になってしまうかもしれません。

特定の適切に制御されたエンコーディング (エンディアンを制御するための UTF8、または UCS-2LE と UCS-2BE など) を使用して書き込みたい場合は、iconvのようなものが必要です。ストリームに使用することもできます。 https://stackoverflow.com/a/1275260/105929 を参照std::localeしてくださいimbue

于 2012-10-08T20:56:52.920 に答える