c++ - method - wstring operator+




std:: wstring VS std:: string (8)

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

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

stringwstring

std::stringcharにテンプレート化されたbasic_stringであり、 wchar_tではstd::wstringです。

charwchar_t

charは文字(通常は8ビット文字)を保持することになっています。
wchar_tはワイド文字を保持することになっています。そして、状況はトリッキーになります:
Linuxでは、 wchar_tは4バイトですが、Windowsでは2バイトです。

Unicodeはどうですか?

問題は、 charwchar_tどちらもUnicodeに直接結びついていないことです。

Linuxでは?

Linux OSを使いましょう:私のUbuntuシステムはすでにユニコード対応です。 私がchar文字列を扱うとき、それはネイティブにUTF-8 (すなわちUnicode文字列の文字列)でコード化されます。 次のコード:

#include <cstring>
#include <iostream>

int main(int argc, char* argv[])
{
   const char text[] = "olé" ;


   std::cout << "sizeof(char)    : " << sizeof(char) << std::endl ;
   std::cout << "text            : " << text << std::endl ;
   std::cout << "sizeof(text)    : " << sizeof(text) << std::endl ;
   std::cout << "strlen(text)    : " << strlen(text) << std::endl ;

   std::cout << "text(ordinals)  :" ;

   for(size_t i = 0, iMax = strlen(text); i < iMax; ++i)
   {
      std::cout << " " << static_cast<unsigned int>(
                              static_cast<unsigned char>(text[i])
                          );
   }

   std::cout << std::endl << std::endl ;

   // - - - 

   const wchar_t wtext[] = L"olé" ;

   std::cout << "sizeof(wchar_t) : " << sizeof(wchar_t) << std::endl ;
   //std::cout << "wtext           : " << wtext << std::endl ; <- error
   std::cout << "wtext           : UNABLE TO CONVERT NATIVELY." << std::endl ;
   std::wcout << L"wtext           : " << wtext << std::endl;

   std::cout << "sizeof(wtext)   : " << sizeof(wtext) << std::endl ;
   std::cout << "wcslen(wtext)   : " << wcslen(wtext) << std::endl ;

   std::cout << "wtext(ordinals) :" ;

   for(size_t i = 0, iMax = wcslen(wtext); i < iMax; ++i)
   {
      std::cout << " " << static_cast<unsigned int>(
                              static_cast<unsigned short>(wtext[i])
                              );
   }

   std::cout << std::endl << std::endl ;

   return 0;
}

次のテキストを出力します。

sizeof(char)    : 1
text            : olé
sizeof(text)    : 5
strlen(text)    : 4
text(ordinals)  : 111 108 195 169

sizeof(wchar_t) : 4
wtext           : UNABLE TO CONVERT NATIVELY.
wtext           : ol�
sizeof(wtext)   : 16
wcslen(wtext)   : 3
wtext(ordinals) : 111 108 233

char "olé"テキストは実際には110,108,195,169の4つの文字で構成されています(末尾の0は含まない)。 (私はwchar_tコードを練習として勉強させておきます)

したがって、Linux上でcharを操作する場合、通常、Unicodeを知らなくても使用する必要があります。 std::stringcharと連動するので、 std::stringはすでにUnicode対応です。

Cの文字列APIのように、 std::stringは "olé"文字列を3文字ではなく4文字と見なします。 したがって、UTF-8では文字の組み合わせが禁止されているため、Unicode文字を切り捨てる/再生する際には注意が必要です。

Windowsでは?

Windowsでは、これは少し異なります。 Win32は、Unicodeの登場前に、 charとUnicodeの出現前に世界中で生成された異なるcharsets / codepages動作する多くのアプリケーションをサポートしなければなりcharsetsでした。

彼らのソリューションは興味深いものでした。アプリケーションがcharで動作する場合、char文字列は、マシン上のローカルcharset / codepageを使用してGUIラベルにエンコード/印刷/表示されます。 たとえば、 "olé"はフランス語にローカライズされたWindowsでは "olé"になりますが、キリル文字でローカライズされたWindows( Windows-1251を使用する場合は "ol")では異なるものになります。 したがって、 "歴史的なアプリ"は通常同じ古い方法で動作します。

Unicodeベースのアプリケーションでは、Windowsは2バイト幅のwchar_t使用し、2バイト文字でUnicodeでエンコードされたUTF-16でエンコードされています(または少なくともほとんど互換性のあるUCS-2です。同じことをIIRC)。

charを使用するアプリケーションは、各グリフが1つ以上のchar構成されているため "マルチバイト"と呼ばれ、 wchar_tを使用するアプリケーションは "widechar"と呼ばれます(各グリフは1つまたは2つのwchar_t構成されるためです)。MultiByteToWideCharおよびWideCharToMultiByte Win32変換API詳細については。

したがって、あなたがWindowsで作業する場合、あなたはひどく wchar_tを使いたい思うGTK+QTようなフレームワークを隠さない限り)。 実際、Windowsはwchar_t文字列で動作するので、履歴アプリケーションでも、 SetWindowText() (Win32 GUI上でラベルを設定するための低レベルAPI関数SetWindowText()ようなAPIを使用すると、 char文字列がwchar_tに変換されます。

メモリの問題?

UTF-32は1文字あたり4バイトなので、追加する必要はほとんどありません。UTF-8テキストとUTF-16テキストは、UTF-32テキストと同じ量のメモリを常に使用します)。

メモリの問題がある場合は、ほとんどの西洋言語よりも、UTF-8テキストが同じUTF-16より少ないメモリを使用することがわかります。

それでも、他の言語(中国語、日本語など)の場合、使用されるメモリはUTF-8の場合と同じか、UTF-8の場合よりも若干大きくなります。

UTF-16は何らかの難解な言語グリフ(Klingon-Elvish?)を扱っているのでなければ、UTF-16は主に2バイト、時には4バイトの文字を使用しますが、UTF-8は1バイトから4バイトを使います。

詳細はhttp://en.wikipedia.org/wiki/UTF-8#Compared_to_UTF-16を参照してください。

結論

  1. std :: stringよりstd :: wstringを使用する必要がありますか?

    Linuxでは? ほとんどは決してない (§)。
    Windowsでは? ほとんどいつも (§)。
    クロスプラットフォームコードでは? あなたのツールキットに依存します...

    (§):そうでないと言っているツールキット/フレームワークを使用しない限り

  2. std::string特殊文字を含むすべてのASCII文字セットを保持できますか?

    注意: std::stringは 'binary'バッファを保持するのに適していますstd::wstringはそうではありません!

    Linuxでは? はい。
    Windowsでは? Windowsユーザーの現在のロケールで使用できる特殊文字のみです。

    編集( Johann Gerellからのコメントの後):
    std::stringはすべてのcharベースの文字列(各charは0から255までの数字)を扱うのに十分です。 しかし:

    1. ASCIIは0から127になるはずです。上位のcharはASCIIではありません。
    2. 0〜127のcharは正しく保持されます
    3. 128から255までのcharは、エンコード(Unicode、非Unicodeなど)に応じて意味がありますが、UTF-8でエンコードされている限り、すべてのUnicodeグリフを保持することができます。
  3. ほぼすべての一般的なC ++コンパイラでstd::wstringサポートされていますか?

    主に、Windowsに移植されたGCCベースのコンパイラは例外です。
    私のg ++​​ 4.3.2(Linux上)で動作し、Visual C ++ 6以降、Win32でUnicode APIを使用しました。

  4. まさにワイドキャラクターとは何ですか?

    C / C ++では、 wchar_tで書かれた文字型であり、単純なchar型よりも大きい。 これは、(Unicodeのグリフのような)インデックスが255より大きい(または127に応じて...)内側の文字を配置するために使用されるはずです。



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

    wchar_tの長さが32ビットの場合は、Unicodeエンコーディングとしてutf-32を使用できます。また、固定(utf-32は固定長です)エンコーディングを使用してユニコード文字列を格納および処理できます。 つまり、wstringのs.size()関数は正しい量のwchar_t要素論理文字を返します。

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

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

4)ワイドキャラクターはwchar_t


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

私はプログラマーであり、言語スペシャリストではないと私はここで前に立つだろう。 私はUSC2とUTF-16が同じではないことを感謝しますが、私の目的のためには、それらは交換可能なほど近くにあり、私はここでそのように使用します。 私は実際にどのWindowsが使用されているかはわかりませんが、一般的にどちらかを知る必要はありません。 私はこの答えにUCS2を明記していますので、あらかじめご了承ください。私はこの事実を知らずに誰かを怒らせてしまいます。

文字列リテラル

コードページで表現できる文字のみを含む文字列リテラルを入力すると、VSはコードページに基づいて1文字あたり1バイトの文字コードでファイルに格納します。 コードページを変更したり、別のコードページを使って別の開発者にソースを与えると、文字が別のものになることは考えられますが、テストしていないことに注意してください。 別のコードページを使用してコンピュータ上でコードを実行すると、文字が変更されるかどうかはわかりません。

あなたのコードページで表現できない文字列リテラルを入力すると、VSはファイルをUnicodeとして保存するように求めます。 ファイルはUTF-8としてエンコードされます。 これは、すべての非ASCII文字(コードページにある文字を含む)が2バイト以上で表されることを意味します。 つまり、ソースを他の人に与えると、ソースは同じように見えます。 ただし、ソースをコンパイラに渡す前に、VSはUTF-8エンコードされたテキストをコードページのエンコードされたテキストに変換し、コードページにない文字は?

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

std :: cout

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

std :: fstreamファイル名

Windows OSはファイル名にUCS2 / UTF-16を使用しているため、コードページが何であれ、どのUnicode文字でもファイルを作成できます。しかし、これはコードページにない文字を使ってファイルにアクセスしたり作成したりすることを意味しますstd::wstring。他の方法はありません。これはMicrosoft固有の拡張でstd::fstream、他のシステムではコンパイルされない可能性があります。std :: stringを使用すると、コードページの文字のみを含むファイル名しか利用できません。

あなたのオプション

あなたがLinux上で作業しているだけなら、おそらくこれほど遠くはないでしょう。std::stringどこでもUTF-8を使用してください。

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

あなたがクロスプラットホームであれば、それは率直であるために混乱です。Windows上でUTF-8をどこでも使用しようとすると、実際には文字列リテラルに注意してコンソールに出力する必要があります。そこにあなたの弦を簡単に破損させることができます。std::wstringLinux上のどこにでも使用している場合は、ワイド・バージョンへのアクセス権std::fstreamがない可能性があるため、変換を行う必要がありますが、破損のリスクはありません。個人的に私はこれがより良い選択だと思う。多くの人が同意しませんが、私は一人ではありません。たとえば、wxWidgetsが使用するパスです。

もう1つの選択肢は、LinuxとWindowsのtypedefでunicodestring、UNI()というマクロがあり、これはWindows上でLの接頭辞を持ち、Linuxでは何もせず、コードstd::stringstd::wstring

#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を使用して手動でエンコードすることを選択した場合は、すべてのユニコードに対してのみWindowsを使用できます。しかし、Windows APIと標準のC ++クラスではstd::string、ロケールコードページを使用してエンコードされることが期待されます。これには、コンピュータが使用するように設定されているコードページに応じて変更される、すべてのASCII + 128文字が含まれます。

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

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


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

わかりやすい結論は非常にシンプルです.C ++(とSTL)の文字エンコーディングのすべてが実質的に壊れて無駄です。 とにかくそれが助けにならない、マイクロソフトかそれとも非難する。

私の解決策は、深い調査の後、多くの不満と結果的な経験は次のとおりです:

  1. 受け入れ、あなたはエンコーディングとコンバートのためにあなた自身の責任を負わなければならないことを承知しています(そして、その大部分はかなり簡単です)

  2. 任意のUTF-8エンコードされた文字列にstd :: stringを使用します(単なるtypedef std::string UTF8String

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

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

  5. 文字単位のアクセスが必要な場合(読み取り、操作など)は常にUCS2Stringインスタンスを使用します。 文字ベースの処理は、NONマルチバイト表現で行う必要があります。 シンプルで、速く、簡単です。

  6. UTF-8とUCS-2の間で前後に変換する2つのユーティリティ関数を追加します。

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

変換は簡単ですが、Googleはここで助けてください...

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

代替案と改善点

  • const wchar_t tt_iso88951[256] = {0,1,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またはその他のユニコードライブラリ?

高度なものについては。


私はWindowsや他の場所でstd::wstringを避けることをお勧めします。ただし、インターフェイスやWindows API呼び出しの近くのどこかで、構文上の砂糖としてそれぞれのエンコーディング変換を必要とする場合を除きます。

私の見解は、私が共著者であるhttp://utf8everywhere.orgに要約されています。

アプリケーションが主にUIアプリケーションなどのAPIコール中心のものでない限り、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つのペアのいずれかをとります。

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

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

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





wstring