エンコーディング



遅かれ早かれ、誰もが異なるエンコーディングで作業する必要があります。 彼のチームのコードでこれらの問題を解決するためのさまざまな、時には奇妙なアプローチに気づいたので、説明的な会話をしなければなりませんでした。 以下では、コード内の非ASCII文字を使用した正しい作業のビジョンを共有します。 私は建設的な批判を喜んでいます。







動作原理



C ++でさまざまなエンコーディングを使用するロジックは、シンプルで透過的です。 一般的に、それはスキームに反映されます。 プログラムは1つで動作します-独自の内部エンコーディング、および正しくローカライズされたストリームは、データエンコーディングを外部コードから内部コードに、またはその逆に変換する責任があります。 プログラムの内部エンコードは、一度だけ修正するのが最適です。 プログラムが非ASCII文字で動作する場合、内部エンコードの最も論理的な選択はUnicodeであり、UTF-8とcharを使用してSTLをパラメーター化することは通常不当です(ただし、これが必要な状況もあります)。 拡張wchar_t文字に切り替えてUCS-2を使用する方が論理的です。 codecvtファセットは、データを外部エンコーディングから内部エンコーディングに変換する役割を果たします。 ローカライズされたストリーム自体は、データを受信するときに、対応するファセット関数を呼び出します(これらのファセットが誰であるかについては以前書い

上記をコメント付きの例で説明します。この例では、cp1251ファイルからデータを読み取り、boost :: xpressiveがUnicodeでどのように機能するかを示し、cp866でエンコードされたcoutでキリル文字を表示します(デフォルトではWindowsコンソール)。











ソースエンコーディング



例の検討を開始する前に、(念のため)プログラムのソースコードのエンコードについていくつかの単語を言う必要があります。 すべてのソースをUTF-8で保持します(非ASCII文字を含むワイド文字列定数が含まれる場合は、ファイルにBOMを追加します)。 最新のコンパイラ自体は、ソースでマークされた「ワイド」文字をUCS-2(またはUCS-4)でL ""として変換します。 正しい変換がソースエンコーディングに依存することは明らかです。 gccはデフォルトでUTF-8テキストで機能すると信じているため、それを納得させるためには-finput-charsetパラメーターの値を具体的に指定する必要があります。 MSコンパイラには少し助けが必要です-BOM( Byte Order Mark )ファイルをUTF-8に追加します。 残念ながら、UTF-8を使用したBorland C ++ Compilerバージョン5.5には問題があります。

私に石を投げる人のために、2つのポイントを説明します:1つ目-「unicode escape」タイプのコードを読むのは不便です:

std::wstring wstr(L"\u0410\u0411\u0412\u0413\u0413");





2つ目は、ユーザーインターフェイスだけではないため、すべてのワイド文字列定数を個別のモジュールに配置し、何らかの方法で(gettextなど)それらを操作することは選択肢ではありません。

そのため、UTF-8のソースとBOMが決定されました。 誰も知らない場合、vimで「set bomb」コマンドを使用してBOMをファイルに追加できます。 ファイル内のBOMに既にvimが含まれている場合、どこにも移動しません。



さまざまなエンコーディングの使用例



さて、私たちは最も興味深いことに到達しました。 私が言ったように、コードはシンプルで簡単です。 標準スレッドに関する小さな注意-デフォルトでは、パフォーマンスのためにstdioと同期されるため、ファセットは使用されません。 sync_with_stdio(false)を指定する必要があります。



 #include <boost/xpressive/xpressive.hpp> #include <locale> #include <fstream> #include <iostream> #include <iterator> #include "codecvt_cp866.hpp" #include "codecvt_cp1251.hpp" #include "unicyr_ctype.hpp" using namespace std; using namespace boost::xpressive; int main() { //    input.txt   cp1251,   // ", !" ofstream ofile("input.txt", std::ios::binary); ostreambuf_iterator<char> writer(ofile); writer = 0xCF; //  ++writer = 0xF0; //  ++writer = 0xE8; //  ++writer = 0xE2; //  ++writer = 0xE5; //  ++writer = 0xF2; //  ++writer = 0x2C; // , ++writer = 0x20; // ++writer = 0xEC; //  ++writer = 0xE8; //  ++writer = 0xF0; //  ++writer = 0x21; // ! ofile.close(); //   wifstream ifile("input.txt"); //    locale cp1251(locale(""), new codecvt_cp1251<wchar_t, char, mbstate_t>); ifile.imbue(cp1251); wchar_t wstr[14]; ifile.getline(wstr, 13); //  C++   cout, cin, cerr  // clog,       stdio,   //        (    //  gcc, msvc 7    ).  //  ios,       stdio. ios_base::sync_with_stdio(false); //    locale cp866(locale(""), new codecvt_cp866<wchar_t, char, mbstate_t>); //  ,      //  wcout.imbue(cp866); //    ctype locale cyrr(locale(""), new unicyr_ctype); wsregex_compiler xpr_compiler; xpr_compiler.imbue(cyrr); wsregex xpr = xpr_compiler.compile(L"", regex_constants::icase); wsmatch match; if(regex_search(wstring(wstr), match, xpr)) wcout << L"icase " << endl; else wcout << L"icase  " << endl; return 0; }
      
      







キリル文字をcp1251からucs-2に、またはその逆に変換するためのファセットcodecvt



 #include <locale> #include <map> /**@brief  codecvt      cp1251 *  UCS-2   * *       (3-   - * ).        - * .   ,   codecvt     *  .       *  .  ,      , *  ,     .   *        . State *      ,     *       ,    *       . */ template<class I, class E, class State> class codecvt_cp1251 : public std::codecvt<I, E, State> { public: //     typedef typename std::codecvt_base::result result; const result ok, //  partial, //   (  State) error, //   noconv; //   explicit codecvt_cp1251(size_t r=0) : std::codecvt<I, E, State>(r), ok(std::codecvt_base::ok), partial(std::codecvt_base::partial), error(std::codecvt_base::error), noconv(std::codecvt_base::noconv) { //    -   in_tab[0xA8] = 0x401; out_tab[0x401] = 0xA8; in_tab[0xB8] = 0x451; out_tab[0x451] = 0xB8; // ...   } ~codecvt_cp1251() { } protected: /**@brief        *  from-from_end,     in-in_end.*/ virtual result do_in(State&, const E* from, const E* from_end, const E* &from_next, I* to, I* to_end, I* &to_next) const { while(from != from_end) { if(to == to_end) { from_next = ++from; to_next = to; return partial; } // ASCII if(0 <= *from && *from <= 0x7F) { *to = static_cast<I>(*from); } else if(0xC0 <= static_cast<unsigned char>(*from) && static_cast<unsigned char>(*from) <=0xFF) { *to = static_cast<I>(static_cast<unsigned char>(*from) + 0x350); } else { typename std::map<E, I>::const_iterator s; s = in_tab.lower_bound(*from); if(s == in_tab.end()) { //   ,  from  next   from_next = ++from; to_next = ++to; return error; } *to = s->second; } ++to; ++from; } from_next = from_end; to_next = to; return ok; } /**@brief    ,  true*/ virtual int do_encoding() const throw() { return 1; } /**@brief    ,  true*/ virtual bool do_always_noconv() const throw() { return false; } /*     virtual result do_out(State&, const I* from, const I* from_end, const I* &from_next, E* to, E* to_end, E* &to_next); virtual int do_length(State& s, const E* from, const E* from_end, size_t max) const; virtual int do_max_length() const throw(); */ private: //       std::map<E, I> in_tab; std::map<I, E> out_tab; };
      
      







注釈



不要なコードで記事を散らかしませんでした-cp866のcodecvtファセットは同様に実装されていますが、先ほどctypeについて話しましたが、誰かが実際の例を必要とする場合、これらのファセットはgithub-git://github.com/hoxnox/cyrillic- facets.git。

そして、行番号がないことをおizeびします。UFOがこのトピックを「飲み込む」ために、ハイライトコードを減らす必要がありました。



UNICODEの詳細はこちらをご覧ください

ファセットの詳細については、Stroustrupの本「C ++ Programming Language」、第3版、付録

ブーストの詳細:: ここで xpressive



UPD 20150813:

Cyapa habrublerは調査を実施し、codecvtファセットはbisic_filebufで機能するストリームでのみ機能することを発見しました。 標準(27.9.1.1項に加えて、これはStraustrupによって指摘されています)によると、実装は他のバッファ(特にbasic_stringbuf)のcodecvtファセットメソッドを呼び出す必要はありません。 したがって、codecvtファセットを使用してロケールを作成し、このロケールが(imbueを使用して)割り当てられるstd :: stringstreamがこのファセットをプルすることを期待している場合、あなたは間違っています。



All Articles