web-dev-qa-db-ja.com

std :: wstring VS std :: string

std::stringstd::wstringの違いを理解できません。私はwstringがUnicode文字のようなワイド文字をサポートすることを知っています。以下の質問があります。

  1. std::wstringの上でstd::stringをいつ使うべきですか?
  2. std::stringは特殊文字を含むASCII文字セット全体を保持できますか?
  3. std::wstringはすべての一般的なC++コンパイラでサポートされていますか?
  4. まさに " ワイド文字 "とは何ですか?
691
Appu

私は、Windowsまたは他の場所でstd::wstringを避けることをお勧めします。ただし、インターフェースが必要な場合、またはWindows API呼び出しおよびそれぞれの構文変換としてのエンコード変換に近い場所を除きます。

私の見解は http://utf8everywhere.org に要約されており、そのうち私は共著者です。

アプリケーションがAPIコール中心のものでない限り、主にUIアプリケーションでは、提案はUnicode文字列をstd :: stringに格納し、UTF-8でエンコードし、API呼び出しの近くで変換を実行することです。この記事で概説した利点は、特に複雑なアプリケーションにおいて、見かけの変換の煩わしさを上回るものです。これは、マルチプラットフォームやライブラリ開発にとって二重にそうです。

そして今、あなたの質問に答える:

  1. いくつかの弱い理由これは、ワイド文字がUnicodeをサポートするための適切な方法であると考えられていた歴史的な理由で存在します。現在はUTF-16文字列を好むAPIとのインターフェースに使用されています。私はそのようなAPI呼び出しのすぐ近くでのみそれらを使用します。
  2. これはstd :: stringとは関係ありません。それはあなたがそれに入れたどんなエンコーディングでも保持することができます。唯一の質問は、 あなた の内容の扱い方です。私のお勧めはUTF-8なので、すべてのUnicode文字を正しく保持できるでしょう。これはLinux上では一般的な方法ですが、Windowsプログラムでも同様に行うべきだと思います。
  3. いいえ.
  4. ワイド文字はわかりにくい名前です。 Unicodeの初期の頃は、文字は2バイトでエンコードできるという信念がありました。今日では、「2バイト長の文字の任意の部分」を表します。 UTF-16は、このようなバイトペアのシーケンス(ワイド文字とも呼ばれます)と見なされます。 UTF-16の文字は1つか2つのペアを取ります。
57

ですから、ここにいるすべての読者は、事実、状況について明確に理解しているはずです。そうでなければ、それから あなたはpaercebalの非常に包括的な答えを読む必要があります [btw:ありがとう!]。

私の実用的な結論は驚くほど単純です。C++(およびSTL)の「文字エンコーディング」に関するものはすべて壊れていて役に立ちません。 Microsoftのせいかどうか、それはとにかく助けにはなりません。

私の解決策は、徹底的な調査の結果、多くのフラストレーションとその結果としての経験は以下の通りです。

  1. あなたは自分自身でエンコーディングや変換について責任を負う必要があることを認めてください(そしてその大部分はかなり簡単であることがわかるでしょう)。

  2. uTF-8でエンコードされた文字列にはstd :: stringを使用してください(typedef std::string UTF8Stringのみ)。

  3. そのようなUTF8Stringオブジェクトは単なるダメですが安いコンテナであることを受け入れてください。その中の文字に直接アクセスしたり、文字を操作したりしないでください(検索、置換など)。マルチバイト文字列のテキスト操作アルゴリズムを書くのに時間を無駄にしたくないかもしれません。たとえ他の人がすでにそのようなばかなことをしたとしても、それをしないでください!なるがままに! (まあ、それが意味をなすシナリオがあります...それらのためにICUライブラリを使うだけです)。

  4. uCS-2でエンコードされた文字列(typedef std::wstring UCS2String)にはstd :: wstringを使用してください - これは妥協であり、WIN32 APIが導入した混乱に対する譲歩です)。 UCS-2は私たちの大部分にとって十分です(詳細は後ほど...)。

  5. 文字ごとのアクセスが必要な場合は必ずUCS2Stringインスタンスを使用してください(読み取り、操作など)。文字ベースの処理はすべて、非マルチバイト表現で行う必要があります。それは簡単、速く、簡単です。

  6. uTF-8とUCS-2の間で相互に変換するための2つのユーティリティ関数を追加します。

    UCS2String ConvertToUCS2( const UTF8String &str );
    UTF8String ConvertToUTF8( const UCS2String &str );
    

変換は簡単です。Googleがここで助けになるはずです...

それでおしまい。メモリが貴重であり、すべてのUTF-8 I/Oに対して、UTF8Stringを使用してください。文字列を解析または操作する必要がある場合は、必ずUCS2Stringを使用してください。これら2つの表現はいつでも変換できます。

代替案と改善策

  • &からシングルバイト文字エンコーディング(例:ISO-8859-1)への変換は、プレーンな変換テーブルを使って実現できます。 const wchar_t tt_iso88951[256] = {0,1,2,...};、およびUCS2からの変換用の適切なコード。

  • uCS-2で十分でない場合は、UCS-4(typedef std::basic_string<uint32_t> UCS2String)に切り替えます。

ICUまたは他のUnicodeライブラリ

上級者向け。

37
Frunsi
  1. 文字列にワイド文字を保存したいとき。 wideは実装に依存します。正しく覚えていれば、Visual C++はデフォルトで16ビットになりますが、GCCはターゲットに応じてデフォルトになります。ここでは32ビット長です。 wchar_t(ワイド文字タイプ)はUnicodeとは関係がないことに注意してください。実装がそのロケールでサポートする最大の文字セットのすべてのメンバーを、少なくともcharと同じ長さで格納できることが単に保証されているだけです。 std::stringエンコーディングを使用して、Unicode文字列をutf-8にうまくstoreすることもできます。しかし、Unicodeコードポイントの意味は理解できません。したがって、str.size()は、文字列内の論理文字の量ではなく、その文字列/ wstringに格納されているcharまたはwchar_t要素の量だけを提供します。そのため、gtk/glib C++ラッパーの人々は、utf-8を処理できる Glib::ustring クラスを開発しました。

    wchar_tが32ビット長の場合、utf-32をUnicodeエンコードとして使用でき、固定(utf-32は固定長)エンコーディングを使用してUnicode文字列を処理します。これは、wstringのs.size()関数がthen適切な量のwchar_t要素and論理文字。

  2. はい、charは常に少なくとも8ビット長です。つまり、ASCII値をすべて格納できます。
  3. はい、すべての主要なコンパイラがサポートしています。

私は何の問題もなくutf-8文字を保持するためにstd :: stringを頻繁に使用します。ネイティブの文字列型としてutf-8を使用しているAPIとインターフェースする場合は、これを行うことを強くお勧めします。

たとえば、私のコードとTclインタプリタとのインタフェースには、utf-8を使用します。

主な注意点はstd :: stringの長さで、もはや文字列の文字数ではありません。

5
Juan
  1. ワイド(Unicode)文字を保存したい場合.
  2. はい:それらのうち255(0を除く)。
  3. はい。
  4. これが紹介記事です: http://www.joelonsoftware.com/articles/Unicode.html
3
ChrisW

256種類の文字だけで満足できないアプリケーションには、ワイド文字(8ビット以上)またはUTF-8などの可変長エンコーディング(C++用語ではマルチバイトエンコーディング)のいずれかを使用するオプションがあります。ワイド文字は一般に可変長エンコーディングよりも多くのスペースを必要としますが、処理は高速です。大量のテキストを処理する多言語アプリケーションは、通常、テキストを処理するときにワイド文字を使用しますが、ディスクに格納するときはUTF-8に変換します。

stringwstringの唯一の違いは、それらが格納する文字のデータ型です。文字列は少なくとも8ビットであることが保証されているcharsを格納します。 ASCII、ISO-8859-15、またはUTF-8のテキスト。規格は、文字セットやエンコーディングについては何も述べていません。

事実上すべてのコンパイラは、最初の128文字がASCIIに対応する文字セットを使用します。これは、UTF-8エンコーディングを使用するコンパイラにも当てはまります。 UTF-8やその他の可変長エンコードで文字列を使用するときに注意する必要がある重要なことは、インデックスと長さは文字数ではなくバイト数で測定されるということです。

Wstringのデータ型はwchar_tです。サイズは標準では定義されていません。ただし、少なくともcharと同じくらいの大きさ、通常16ビットまたは32ビットである必要があります。 wstringは、実装定義のワイド文字エンコーディングでテキストを処理するために使用できます。エンコーディングは標準で定義されていないため、文字列とwstringの間の変換は簡単ではありません。 wstringが固定長エンコーディングを持つと仮定することもできません。

多言語サポートが不要な場合は、通常の文字列だけを使用しても問題ありません。一方、グラフィカルアプリケーションを作成している場合は、APIがワイド文字のみをサポートすることがよくあります。その場合、おそらくテキストを処理するときに同じワイド文字を使用したいでしょう。 UTF-16は可変長エンコーディングであることに注意してください。つまり、length()を使用して文字数を返すことはできません。 APIがUCS-2などの固定長エンコードを使用している場合は、処理が簡単になります。ワイド文字とUTF-8との間の変換は、移植性の高い方法で行うのは困難ですが、やはり、ユーザーインターフェースAPIがおそらくその変換をサポートしています。

2
Seppo Enarvi
  1. aSCIIだけでなくUnicode文字列を使用したい場合、国際化に役立ちます。
  2. はい、でも0ではうまくいかない
  3. そうでないものを意識していない
  4. ワイド文字は、Unicode文字の固定長表現を処理するためのコンパイラ固有の方法です。MSVCの場合は2バイト文字、gccの場合は4バイトです。 http://www.joelonsoftware.com/articles/Unicode.html には+1
1
Greg Domjan

1)Gregが述べたように、wstringは国際化に役立ちます、それはあなたが英語以外の言語であなたの製品をリリースするときです。

4)ワイド文字でこれをチェックしてくださいhttp://en.wikipedia.org/wiki/Wide_character

0
Raghu

いい質問ですね。 DATA ENCODING (時々 _ charset _ も含まれる)は MEMORY EXPRESSION MECHANISMだと思うので、ネットワーク経由でデータを転送するので、この質問に答えるとして:

1. std :: stringの上にstd :: wstringを使用するのはいつですか?

プログラミングプラットフォームやAPI関数がシングルバイトのもので、Windowsの.REGファイルやネットワークの2バイトストリームなどのUnicodeデータを処理または解析する場合は、std :: wstring変数を宣言して簡単にできます。それらを処理します。例:wstring ws = L "中国a"(6オクテットメモリ:0x4E2D 0x56FD 0x0061)、ws [0]を使用して文字 '中'を取得し、ws [1]を使用して文字 '中'を取得し、ws [2]を取得します。文字 'a'などを取得する.

2. std :: stringは、特殊文字を含むASCII文字セット全体を保持できますか。

はい。しかし注意してください:アメリカのASCIIは、 "123abc&* _&"のような印刷可能なテキストを含むそれぞれの0x00〜0xFFオクテットが1文字を意味することを意味し、あなたは特別なものを言った。編集者や端末を混同しないでください。そして他のいくつかの国では独自の "ASCII"文字セットを拡張しています。中国語、1文字を表すのに2オクテットを使用します。

3. std :: wstringは、一般的なすべてのC++コンパイラでサポートされていますか。

たぶん、あるいはほとんど。私が使ったことがある:VC++ 6とGCC 3.3、YES

4.「ワイド文字」って何ですか?

ワイド文字は、主に2オクテットまたは4オ​​クテットを使用してすべての国の文字を保持することを示します。 2オクテットのUCS2は代表的なサンプルであり、さらに、例えば、C。英語の 'a'、そのメモリは0x0061の2オクテット(対[ASCII 'aのメモリは1オクテットの0x61)

0
Leiyi.China

ここには非常に良い答えがいくつかありますが、Windows/Visual Studioに関して追加できることがいくつかあると思います。 TisはVS2015での私の経験に基づいています。 Linuxでは、基本的に答えはどこでもUTF-8でエンコードされたstd::stringを使うことです。 Windows/VSでは、それはより複雑になります。これが理由です。 Windowsはcharsを使用して格納された文字列がロケールコードページを使用してエンコードされることを想定しています。これはほとんどの場合、ASCII文字セットの後に、場所に応じて128個の他の特殊文字が続いたものです。これは、Windows APIを使用しているときだけではなく、これらの文字列が標準C++と対話する場所が他に3つあることを述べておきます。これらは文字列リテラルで、std::coutを使用して<<に出力され、ファイル名をstd::fstreamに渡します。

私はプログラマーであり、言語の専門家ではないことをここで前もって説明します。 USC2とUTF-16は同じではないことを私は理解しています、しかし私の目的のためにそれらは互換性があるのに十分に近く、そして私はここでそのようにそれらを使用します。どちらのWindowsが使用されているのかは実際にはわかりませんが、一般的にどちらかを知る必要はありません。私はこの答えでUCS2を述べました、それで私がこの問題の私の無知で誰かを動揺させたならば私は前もって申し訳ありません。

文字列リテラル

コードページで表現できる文字のみを含む文字列リテラルを入力した場合、VSはコードページに基づいて1文字あたり1バイトのエンコーディングでそれらをファイルに保存します。コードページを変更したり、別のコードページを使用してソースを別の開発者に提供したりした場合は、文字が異なることになると思います(ただしテストされていません)。あなたが別のコードページを使っているコンピュータであなたのコードを走らせるならば、私は文字も変わるかどうかわからない。

コードページで表現できない文字列リテラルを入力した場合、VSはファイルをUnicodeとして保存するように要求します。ファイルはUTF-8としてエンコードされます。これは、すべてのNon ASCII文字(コードページにあるものも含む)が2バイト以上で表されることを意味します。これは、あなたがあなたの情報源を他の誰かに与えた場合、その情報源は同じに見えるということを意味します。しかし、ソースをコンパイラーに渡す前に、VSはUTF-8エンコード・テキストをコード・ページ・エンコード・テキストに変換し、コード・ページに欠けている文字はすべて?に置き換えられます。

VSでUnicode文字列リテラルを正しく表現することを保証する唯一の方法は、文字列リテラルの前にLを付けてそれをワイド文字列リテラルにすることです。この場合、VSはUTF-8でエンコードされたテキストをファイルからUCS2に変換します。その後、この文字列リテラルをstd::wstringコンストラクタに渡すか、またはそれをutf-8に変換してstd::stringに入れる必要があります。あるいは、あなたがそれをstd::stringに入れるためにあなたのコードページを使ってそれをエンコードするためにあなたが望むならWindows API関数を使うことができます、しかしあなたは同様にワイド文字列リテラルを使わないかもしれません。

std :: cout

<<を使用してコンソールに出力する場合は、std::stringのみを使用でき、std::wstringは使用できず、テキストはロケールコードページを使用してエンコードする必要があります。 std::wstringがある場合は、Windows API関数の1つを使用して変換する必要があります。コードページにない文字は?に置き換えられます(おそらく文字を変更できますが、覚えていません)。

std :: fstreamのファイル名

Windows OSでは、ファイル名にUCS2/UTF-16を使用しているため、コードページに関係なく、任意のUnicode文字のファイルを使用できます。しかし、これはあなたのコードページにない文字でファイルにアクセスしたりファイルを作成するためにはstd::wstringを使わなければならないことを意味します。他に方法はありません。これはstd::fstreamに対するMicrosoft固有の拡張機能なので、おそらく他のシステムではコンパイルできません。あなたがstd :: stringを使うなら、あなたはあなたのコードページの文字だけを含むファイル名だけを利用することができます。

あなたのオプション

Linuxで作業しているだけでは、おそらくこれまでのところ到達していないでしょう。どこでもUTF-8 std::stringを使うだけです。

Windowsで作業しているだけの場合は、どこでもUCS2 std::wstringを使用するだけです。純粋主義者の中には、UTF8を使用してから必要に応じて変換すると言う人もいるかもしれませんが、なぜ面倒なことに悩むのでしょう。

あなたがクロスプラットフォームであるならば、それは率直であることは混乱です。 Windowsの至る所でUTF-8を使用しようとするなら、あなたはあなたの文字列リテラルとコンソールへの出力に本当に注意する必要があります。あなたは簡単にそこにあなたの文字列を壊すことができます。 Linuxの至る所でstd::wstringを使用するのであれば、ワイドバージョンのstd::fstreamにアクセスできない可能性があるので、変換を行う必要がありますが、破損の危険はありません。個人的には、これがより良い選択肢だと思います。多くの人は同意しないでしょうが、私は一人ではありません - それは例えばwxWidgetsがたどる道です。

もう1つの選択肢は、Linuxではunicodestringstd::string、Windowsではstd::wstringとして入力し、WindowsではLの前にUNI()というマクロを追加し、Linuxでは何もしないことです。

#include <fstream>
#include <string>
#include <iostream>
#include <Windows.h>

#ifdef _WIN32
typedef std::wstring unicodestring;
#define UNI(text) L ## text
std::string formatForConsole(const unicodestring &str)
{
    std::string result;
    //Call WideCharToMultiByte to do the conversion
    return result;
}
#else
typedef std::string unicodestring;
#define UNI(text) text
std::string formatForConsole(const unicodestring &str)
{
    return str;
}
#endif

int main()
{

    unicodestring fileName(UNI("fileName"));
    std::ofstream fout;
    fout.open(fileName);
    std::cout << formatForConsole(fileName) << std::endl;
    return 0;
}

どちらのプラットフォームでも問題ないでしょう。

答え

だからあなたの質問に答えるために

1)あなたがWindows用にプログラミングしているならば、もしあなたがWindows上で起こり得る破損の問題に対処したいなら、あるいは違いを回避するためにプラットフォーム特有の#ifdefsでコードを書きたくなければ、その後Linuxを使用することはありません。

2)はい。さらにLinuxでは、すべてのUnicodeにも使用できます。 Windowsでは、UTF-8を使用して手動でエンコードすることを選択した場合にのみ、すべてのUnicodeに使用できます。しかし、Windows APIと標準C++クラスは、std::stringがロケールコードページを使用してエンコードされることを期待します。これには、すべてのASCIIと、お使いのコンピュータが使用するように設定されているコードページに応じて変わる128文字が含まれます。

3)私はそう信じていますが、そうでなければそれはcharの代わりにwchar_tを使った 'std :: basic_string'の単純なtypedefです。

4)ワイド文字は、1バイト標準のchar型より大きい文字型です。 Windowsでは2バイト、Linuxでは4バイトです。

0
Phil Rosenberg