言語的および文化的特徴を忘れないでください

遅かれ早かれ、誰もがプログラムを書くときに言語的および文化的な多様性に関連する問題に直面します。 C ++で書いている私の友人の何人かが、バイクでこれらの問題を解決していることを知って非常に驚きました。 まだstd ::ロケールが何であるかを知らない人のために、それをどのように扱うか、そしてそれを忘れたらどうなるかを簡単に示したいと思います...



std ::ロケール (ローカリゼーション)は、ユーザーの文化的および言語的特徴を考慮に入れることができるオブジェクトです。 実際、これは特別なクラスのコンテナです。 ファセットは、自然言語に依存するアクションを実行する必要がある場合にプログラムにアクセスします。 プログラムは、そのようなアクションをローカライズファセットに指示します。 ローカライズには、任意のカスタムファセットを追加できます。 しかし、標準的なものは、ローカライズに実装され、永久的または一時的に置き換えることができるため、最も興味深いものです。



現実には、疑うことなく常にファセットを使用しています。 標準テンプレートライブラリは、入力/出力にローカライズを使用します。 boost ::大文字と小文字の変換などの正規表現 ローカリゼーションはプラットフォームによって定義されます。 * nixシステムのユーザーは、「ru​​_RU:UTF-8」、「en_US.UTF-8」などの行に精通しています。これらはプラットフォームのローカリゼーションの名前です。 プログラムはユーザーのローカライズを使用します。 ローカライズがユーザーによって指定されていない場合、「クラシック」なものが使用されます。



ファセットのローカライズとオーバーライドの使用例



標準のローカライズファセットを置き換える手法を試す例を考えてみましょう。 通常、入力/出力のストリーミングを検討しますが、ローカライズに依存するコードを記述した場合に、それが何であるかわからない場合に何が起こるかに焦点を当てたいと思います。 人気のあるBoost :: xpressiveライブラリでロケールを使用してみましょう(boost :: regexも使用できますが、初めてxpressiveについて聞いた人は、それについて読むと便利です)。



  1. #include <boost/xpressive/xpressive.hpp> #include <iostream> using namespace std ; using namespace boost :: xpressive ; int main ( int argc, char * argv [ ] ) { sregex xpr = sregex :: compile ( "" , regex_constants :: icase ) ; smatch match ; string str ( " !" ) ; if ( regex_search ( str, match, xpr ) ) cout << "icase ok" << endl ; else cout << "icase fail" << endl ; return 0 ; }



  2. #include <boost/xpressive/xpressive.hpp> #include <iostream> using namespace std ; using namespace boost :: xpressive ; int main ( int argc, char * argv [ ] ) { sregex xpr = sregex :: compile ( "" , regex_constants :: icase ) ; smatch match ; string str ( " !" ) ; if ( regex_search ( str, match, xpr ) ) cout << "icase ok" << endl ; else cout << "icase fail" << endl ; return 0 ; }



  3. #include <boost/xpressive/xpressive.hpp> #include <iostream> using namespace std ; using namespace boost :: xpressive ; int main ( int argc, char * argv [ ] ) { sregex xpr = sregex :: compile ( "" , regex_constants :: icase ) ; smatch match ; string str ( " !" ) ; if ( regex_search ( str, match, xpr ) ) cout << "icase ok" << endl ; else cout << "icase fail" << endl ; return 0 ; }



  4. #include <boost/xpressive/xpressive.hpp> #include <iostream> using namespace std ; using namespace boost :: xpressive ; int main ( int argc, char * argv [ ] ) { sregex xpr = sregex :: compile ( "" , regex_constants :: icase ) ; smatch match ; string str ( " !" ) ; if ( regex_search ( str, match, xpr ) ) cout << "icase ok" << endl ; else cout << "icase fail" << endl ; return 0 ; }



  5. #include <boost/xpressive/xpressive.hpp> #include <iostream> using namespace std ; using namespace boost :: xpressive ; int main ( int argc, char * argv [ ] ) { sregex xpr = sregex :: compile ( "" , regex_constants :: icase ) ; smatch match ; string str ( " !" ) ; if ( regex_search ( str, match, xpr ) ) cout << "icase ok" << endl ; else cout << "icase fail" << endl ; return 0 ; }



  6. #include <boost/xpressive/xpressive.hpp> #include <iostream> using namespace std ; using namespace boost :: xpressive ; int main ( int argc, char * argv [ ] ) { sregex xpr = sregex :: compile ( "" , regex_constants :: icase ) ; smatch match ; string str ( " !" ) ; if ( regex_search ( str, match, xpr ) ) cout << "icase ok" << endl ; else cout << "icase fail" << endl ; return 0 ; }



  7. #include <boost/xpressive/xpressive.hpp> #include <iostream> using namespace std ; using namespace boost :: xpressive ; int main ( int argc, char * argv [ ] ) { sregex xpr = sregex :: compile ( "" , regex_constants :: icase ) ; smatch match ; string str ( " !" ) ; if ( regex_search ( str, match, xpr ) ) cout << "icase ok" << endl ; else cout << "icase fail" << endl ; return 0 ; }



  8. #include <boost/xpressive/xpressive.hpp> #include <iostream> using namespace std ; using namespace boost :: xpressive ; int main ( int argc, char * argv [ ] ) { sregex xpr = sregex :: compile ( "" , regex_constants :: icase ) ; smatch match ; string str ( " !" ) ; if ( regex_search ( str, match, xpr ) ) cout << "icase ok" << endl ; else cout << "icase fail" << endl ; return 0 ; }



  9. #include <boost/xpressive/xpressive.hpp> #include <iostream> using namespace std ; using namespace boost :: xpressive ; int main ( int argc, char * argv [ ] ) { sregex xpr = sregex :: compile ( "" , regex_constants :: icase ) ; smatch match ; string str ( " !" ) ; if ( regex_search ( str, match, xpr ) ) cout << "icase ok" << endl ; else cout << "icase fail" << endl ; return 0 ; }



  10. #include <boost/xpressive/xpressive.hpp> #include <iostream> using namespace std ; using namespace boost :: xpressive ; int main ( int argc, char * argv [ ] ) { sregex xpr = sregex :: compile ( "" , regex_constants :: icase ) ; smatch match ; string str ( " !" ) ; if ( regex_search ( str, match, xpr ) ) cout << "icase ok" << endl ; else cout << "icase fail" << endl ; return 0 ; }



  11. #include <boost/xpressive/xpressive.hpp> #include <iostream> using namespace std ; using namespace boost :: xpressive ; int main ( int argc, char * argv [ ] ) { sregex xpr = sregex :: compile ( "" , regex_constants :: icase ) ; smatch match ; string str ( " !" ) ; if ( regex_search ( str, match, xpr ) ) cout << "icase ok" << endl ; else cout << "icase fail" << endl ; return 0 ; }



  12. #include <boost/xpressive/xpressive.hpp> #include <iostream> using namespace std ; using namespace boost :: xpressive ; int main ( int argc, char * argv [ ] ) { sregex xpr = sregex :: compile ( "" , regex_constants :: icase ) ; smatch match ; string str ( " !" ) ; if ( regex_search ( str, match, xpr ) ) cout << "icase ok" << endl ; else cout << "icase fail" << endl ; return 0 ; }



  13. #include <boost/xpressive/xpressive.hpp> #include <iostream> using namespace std ; using namespace boost :: xpressive ; int main ( int argc, char * argv [ ] ) { sregex xpr = sregex :: compile ( "" , regex_constants :: icase ) ; smatch match ; string str ( " !" ) ; if ( regex_search ( str, match, xpr ) ) cout << "icase ok" << endl ; else cout << "icase fail" << endl ; return 0 ; }



  14. #include <boost/xpressive/xpressive.hpp> #include <iostream> using namespace std ; using namespace boost :: xpressive ; int main ( int argc, char * argv [ ] ) { sregex xpr = sregex :: compile ( "" , regex_constants :: icase ) ; smatch match ; string str ( " !" ) ; if ( regex_search ( str, match, xpr ) ) cout << "icase ok" << endl ; else cout << "icase fail" << endl ; return 0 ; }



  15. #include <boost/xpressive/xpressive.hpp> #include <iostream> using namespace std ; using namespace boost :: xpressive ; int main ( int argc, char * argv [ ] ) { sregex xpr = sregex :: compile ( "" , regex_constants :: icase ) ; smatch match ; string str ( " !" ) ; if ( regex_search ( str, match, xpr ) ) cout << "icase ok" << endl ; else cout << "icase fail" << endl ; return 0 ; }







プログラムの配信がプラットフォームに大きく依存しているのは驚くでしょう。 さらに、1つのプラットフォームで、プログラムは異なる結果を生成する可能性があります。 それはすべてロケールに関するものです。 サンプルファイルのエンコーディングがwindows-1251であると仮定すると、「icase fail」の結果は、ユーザーロケールがcp1251以外のエンコーディングを持つプラットフォームで実現できます。 このようなプラットフォームの最も一般的な例は、mingw(sourceforgeバイナリとしてダウンロード)+ Windowsです。 この場合、boost :: xpressiveアルゴリズムは、cp-1251コードテーブルの拡張部分のどの文字が文字であるかを単純に知りません。 そして、古典的なローカライズのctypeファセットは、これを非難することです。 xpressiveが動作するローカライズの正しいctypeファセットを報告することにより、望ましい結果を達成します。 最も単純なケースでは、目的のローカリゼーションがシステムにインストールされている場合、それをグローバルにする必要があります



  1. //グローバルなローカライズを設定します
  2. std :: locale cp1251_locale "ru_RU.CP1251" ;
  3. std :: ロケール :: グローバル cp1251_locale ;


または正規表現コンパイラに報告する

  1. std :: locale cp1251_locale "ru_RU.CP1251" ;
  2. sregex_compilerコンパイラ;
  3. //使用するローカライズを正規表現コンパイラに伝えます
  4. コンパイラ imbue cp1251_locale ;
  5. sregex xpr =コンパイラ。 compile "world" 、regex_constants :: icase ;




すべて問題ありませんが、ローカリゼーションru_RUがサポートされていないプラットフォームでは、CP1251のコードで例外がスローされます。 最良の場合、最悪の場合、名前は誤って示されます-必要なローカリゼーションがシステムにありません。 独自のctypeファセットを実装することでこの問題を解決します(どの文字が文字で、大文字小文字がどのように変化するかをxpressiveに説明するのは彼です)。



CPtypeエンコーディングのctypeファセットの実装と例の最も簡単な例:



  1. #include <boost / xpressive / xpressive.hpp>
  2. #include <iostream>
  3. 名前空間 std を使用し ます
  4. ネームスペースブーストの使用 :: xpressive ;
  5. / ** @ briefで正しく動作するためのctypeファセットの非常に単純化された例
  6. *エンコードされたCp1251 * /
  7. クラス ctype_cp1251 public ctype < char >
  8. {
  9. 公開
  10. / ** ctype_baseの@ breifマスクは、可能なすべての型の列挙です
  11. *文字-アルファ、数字、... * /
  12. typedef typename ctype < char > :: ctype_base :: mask mask ;
  13. //簡潔にするために、定数を再定義します
  14. enum {
  15. alpha = ctype < char > :: alpha
  16. lower = ctype < char > :: lower
  17. punct = ctype < char > :: punct
  18. //他のマスク
  19. } ;
  20. / ** @ briefメインコンストラクター。 r-人生の領域を特徴付ける
  21. *ファセット。 詳細については、Straustrupの本をご覧ください* /
  22. ctype_cp1251 size_t r = 0
  23. {
  24. //マスクテーブルを初期化します。 インデックスは、charの負の部分です。
  25. //つまり、ext_tab [1]はchar(-1)-'i'のマスクです
  26. ext_tab [ 0 ] = 0 ;
  27. for size_t i = 1 ; i <= 32 ; ++ i
  28. ext_tab [ i ] = alpha | 低い;
  29. for size_t i = 33 ; i <= 64 ; ++ i
  30. ext_tab [ i ] = alpha |;
  31. // ...この例の他のキャラクターは面白くない
  32. for size_t i = 65 ; i <= 128 ; ++ i
  33. ext_tab [ i ] = punct ;
  34. }
  35. 〜ctype_cp1251
  36. { }
  37. 保護された
  38. / ** @ brief文字cがマスクmと一致するかどうかの質問に答えます* /
  39. 仮想 ブール mask m、 char c const
  40. {
  41. if 0 <= c && c <= 127
  42. return ctype < char > :: is m、c ;
  43. else if -128 <= c && c < 0
  44. return ext_tab [ static_cast < size_t > c * -1 ] m ;
  45. }
  46. / ** @ brief cを大文字に変換します* /
  47. virtual char do_toupper char c const
  48. {
  49. if 0 <= c && c <= 127
  50. return ctype < char > :: do_toupper c ;
  51. else if is lower、c
  52. return c - 32 ;
  53. return c ;
  54. }
  55. / ** @ brief文字cを小文字に変換します* /
  56. virtual char do_tolower char c const
  57. {
  58. if 0 <= c && c <= 127
  59. return ctype < char > :: do_tolower c ;
  60. else if is upper、c
  61. return c + 32 ;
  62. return c ;
  63. }
  64. //例が複雑にならないように、残りは再定義しません
  65. //仮想関数
  66. プライベート
  67. //コピーの禁止
  68. ctype_cp1251 const ctype_cp1251 ;
  69. const ctype_cp1251 operator = const ctype_cp1251 ;
  70. ext_tab [ 129 ]をマスクします。 // @ <コードテーブルCP1251の拡張部分のマスク
  71. } ;
  72. int main int argc、 char * argv [ ]
  73. {
  74. //ファセットのインスタンスを作成します
  75. ctype < char > * ctype_cp1251_facet = new ctype_cp1251 ;
  76. //を使用して、現在のローカライズに基づいて新しいローカライズを作成します
  77. //上記で定義されたファセット。 グローバルを定義できます
  78. //説明されたファセットを使用してローカライズし、すべてのクラスと
  79. //関数、彼らはそれを使用します。
  80. ロケールcp1251_locale locale "" 、ctype_cp1251_facet ;
  81. //特定のローカライズで正規表現コンパイラを作成します
  82. sregex_compilerコンパイラ;
  83. コンパイラ imbue cp1251_locale ;
  84. sregex xpr =コンパイラ。 compile "world" 、regex_constants :: icase ;
  85. マッチマッチ;
  86. 文字列str 「こんにちは平和!」 ;
  87. if regex_search str、match、xpr
  88. cout << "icase ok" << endl ;
  89. 他に
  90. cout << "icase fail" << endl ;
  91. 0を 返し ます
  92. }




これで、プログラムの結果は特定のプラットフォームに依存しなくなります。 標準ファセットを再定義するか、新しいファセットを追加することにより、ユーザーの文化的および言語的特性に応じてアルゴリズム/プログラムの動作を制御できます。



std ::ローカルクラスとファセットの使用方法の完全な説明は、付録のBjörnStraustrupの著書「C ++ Programming Language」の第3 特別版に記載されています。 任意のSTLマニュアルを使用して、ファセット構造を明確にすることができます。 例えばここに



エンコード変換のタスクは、codecvtファセットを実装することで解決されます。 興味深い場合は、次の記事で説明します。



______________________

テキストは©SoftCoder.ruによってブログエディターで作成されます。



All Articles