2017-01-17 12 views
-3

私はスクリプト言語を構築しています。私の目標の1つは便利な文字列操作です。私はC++でいくつかのアイデアを試しました。C++ Unicode:バイト、コードポイント、およびGraphemes

  • コードポイントインデックスを含むベクトルを返すバイトシーケンスとフリー関数としての文字列。
  • インデックスを含む文字列とベクトルを組み合わせたラッパークラス。

どちらのアイデアにも問題があり、その問題は何を返すべきかということでした。それは文字にすることはできませんでした、それが文字列だった場合、無駄なスペースになります。

ちょうど4バイトの文字配列の周りにラッパークラスを作成しました。文字列は、メモリ内に正確に4バイト以上あり、それ以下ではありません。

このクラスを作成した後、それを別のクラスのstd::vectorにラップし、そこからビルドして、コードポイントの文字列型を作成したいと感じました。私はこれが良いアプローチであるかどうかは分かりませんが、はるかに便利になりますが、スペースを無駄にすることになります。

コードを投稿する前に、ここではアイデアのリストを整理しています。

  • 私の文字タイプは、バイトでもなく、コードではなく、コードポイントです。私はそれをGo言語のもののようなルーンと名付けました。
  • 一連の分解されたルーンとしての文字列で、O1のインデックス作成とスライスを行います。
  • ルーンはクラスでありプリミティブではないので、ユニコード空白を検出するメソッドを使用して展開することができます。mysring[0].is_whitespace()
  • まだ文体の扱いがわかりません。

好奇心!私がruneクラスのプロトタイプを構築する方法についての奇妙なことは、常にUTF8で印刷することでした。私の縄はint32ではなく4バイトの文字列なので、これはいくつか興味深いプロパティを持っています。

マイコード:

class rune { 
    char data[4] {}; 
public: 
    rune(char c) { 
     data[0] = c; 
    } 

    // This constructor needs a string, a position and an offset! 
    rune(std::string const & s, size_t p, size_t n) { 
     for (size_t i = 0; i < n; ++i) { 
      data[i] = s[p + i]; 
     } 
    } 

    void swap(rune & other) { 
     rune t = *this; 
     *this = other; 
     other = t; 
    } 

    // Output as UTF8! 
    friend std::ostream & operator <<(std::ostream & output, rune input) { 
     for (size_t i = 0; i < 4; ++i) { 
      if (input.data[i] == '\0') { 
       return output; 
      } 
      output << input.data[i]; 
     } 
     return output; 
    } 
}; 

エラー処理のアイデア:私はC++で例外を使用するために好きではない

。私の考えは、コンストラクタが失敗した場合、runeを4 '\0'として初期化し、実行の最初のバイトが'\0'の場合は、bool演算子を明示的にオーバーロードしてfalseを返します。シンプルで使いやすい。

だから、考えていますか?意見ですか?異なるアプローチ?

私のルーン文字列が多くても、少なくとも私はルーンタイプを持っています。小さくて速くコピーする。 :)

+2

? –

+0

どうすれば使用できますか?私が最後にチェックしたのは、それに関する多くの情報がないからです。 –

+2

[char32_t](http://en.cppreference.com/w/cpp/language/types) "タイプで、UTF-32コード単位(32ビット)を表現するのに十分な大きさが必要です。これは、 'std :: uint_least32_t'と同じサイズ、符号、および整列を持ちますが、別個の型です。 –

答えて

0

あなたは車輪を再発明しようとしているようですね。

テキストについて考える必要があり、もちろん、2つの方法があります。

  • バイトの符号化配列としてコードポイント
  • の配列としては。いくつかのコードベースで

、これらの2つの表現は同じである(そして、すべてのエンコーディングは、基本的char32_t又はunsigned intのアレイです)。 (私は「ほとんど」と言っているが、それについて私には言わない)、バイトの符号化された配列はUTF-8を使用し、コードポイントはデータ構造に配置される前に可変長のバイトに変換される。

そしてもちろん、多くのコードベースは、単純に完全にユニコードを無視して、ASCIIでそのデータを格納します。私はそれをお勧めしません。

データを「ラップする」クラスを書くのは意味がありますが(runeとは呼ぶわけではありませんが、私はたぶんそれをcodepointと呼んでいます)あなたのセマンティクスについて考える。

  • あなたは(そしておそらく必要があります)、UTF-8でエンコードされた文字列として、すべてのstd::string年代を扱うとテキストに対処するためのデフォルトのインタフェースとしてこれを好むことができます。ほとんどの外部インターフェイスにとって安全です(唯一失敗するのはUTF-16入力とインターフェースするときです)、コーナーケースを書くことができます。これは一般的な文字列規則に従いながらメモリを最大限節約します辞書的に匹敵する、これは大きなものです)。以下の便利な機能とコンストラクタ
    • あなたはコードポイント形式のデータで作業する必要がある場合、あなたは構造体(またはクラス)を書きたいと思うでしょうがcodepointと呼ばれ、私は、コードを書かなければならなかったしているがコードポイント形式(特にフォントレンダラー用)のテキストを扱う場合は、おそらくではなく、テキストをどのように格納するかです。コードポイントとしてテキストを保存すると、後でUTF-8またはASCIIでエンコードされた文字列と比較しているときに問題が発生します。

コード: "ルーン" を保存するために `char32_t`を使用しないのはなぜ

struct codepoint { 
    char32_t val; 
    codepoint(char32_t _val = 0) : val(_val) {} 
    codepoint(std::string const& s); 
    codepoint(std::string::const_iterator begin, std::string::const_iterator end); 
    //I don't know the UTF-8→codepoint conversion off-hand. There are lots of places 
    //online that show how to do this 

    std::string to_utf8() const; 
    //Again, look up an algorithm. They're not *too* complicated. 
    void append_to_string_as_utf8(std::string & s) const; 
    //This might be more performant if you're trying to reduce how many dynamic memory 
    //allocations you're making. 

    //codepoint(std::wstring const& s); 
    //std::wstring to_utf16() const; 
    //void append_to_string_as_utf16(std::wstring & s) const; 

    //Anything else you need, equality operator, comparison operator, etc. 
}; 
+0

"すべての' std :: string'をUTF-8でエンコードされた文字列として扱うことができます。そこにはWindowsと呼ばれるほとんど知られていないOSがあります... –

+1

@ n.m。私はまだデフォルトの記憶媒体としてUTF-8文字列を推奨し、UTF-16→UTF-8またはUTF-8→UTF-16変換でWinOSシステムコールをラップします。エラーを起こしやすいソリューションです。 – Xirema

+0

問題は、char32_tが整数へのエイリアスとして実装されていることです。私のクラスは単なる配列であり、配列のように振る舞います。後で比較するために特別な空白文字を保存したい場合は、 '" \ xe3 \ x80 \ x80 "'で初期化してから比較してください。トリックは、 'std :: string'を' std :: vector​​'に変換することです。明らかにバリデーションを行う必要があります。しかし、ほとんどの場合、それは正常に動作するはずです。私がする必要があるのは、コードポイントのインデックスを取得し、その文字列を 'std :: vector 'に分割することだけです。加えて、UTF8で印刷します! –

関連する問題