2016-05-23 22 views
5

私はユニコードをサポートしているターミナルベースのプログラムを開発中です。文字列が印刷されるまでに消費するターミナル列の数を特定する必要がある場合があります。残念ながら、いくつかの文字は2列幅(中国語など)ですが、全角文字を検出する良い方法を示すthis answerは、ICUライブラリからu_getIntPropertyValue()を呼び出すことによって検出されました。端末のユニコード文字列幅を検出する方法は?

私はUTF8文字列の文字を解析して、この関数に渡そうとしています。私が今行っている問題は、u_getIntPropertyValue()がUTF-32コードポイントを期待していることです。

utf8文字列からこれを取得する最も良い方法は何ですか?私は現在、boost :: locale(私のプログラムのどこかで使われています)でこれをやろうとしていますが、きれいな変換を得ることができません。 boost :: localeから来る私のUTF32文字列は、バイト順序を示すためにzero-width characterであらかじめ保留されています。明らかに、私は文字列の最初の4バイトをスキップすることができますが、これを行うためのよりクリーンな方法がありますか? n.m @

inline size_t utf8PrintableSize(const std::string &str, std::locale loc) 
{ 
    namespace ba = boost::locale::boundary; 
    ba::ssegment_index map(ba::character, str.begin(), str.end(), loc); 
    size_t widthCount = 0; 
    for (ba::ssegment_index::iterator it = map.begin(); it != map.end(); ++it) 
    { 
     ++widthCount; 
     std::string utf32Char = boost::locale::conv::from_utf(it->str(), std::string("utf-32")); 

     UChar32 utf32Codepoint = 0; 
     memcpy(&utf32Codepoint, utf32Char.c_str()+4, sizeof(UChar32)); 

     int width = u_getIntPropertyValue(utf32Codepoint, UCHAR_EAST_ASIAN_WIDTH); 
     if ((width == U_EA_FULLWIDTH) || (width == U_EA_WIDE)) 
     { 
      ++widthCount; 
     } 

    } 
    return widthCount; 
} 
+1

? –

+0

私はICUに精通していません。私はboost :: localeを使用して、ほとんどの複雑さから私を保護しようとしていました。このutf32コードポイントをICUから直接取得する簡単な方法はありますか? – KyleL

+0

私はそれにも慣れていませんが、ユニコードライブラリから誰もが望んでいたことをすべて知っています。 Googleで時間を過ごすと、それを見つけることができます。 –

答えて

1

UTF-32は、個々の文字の「コードポイント」を直接表現したものです。だからあなたがする必要があるのは、UTF-8文字からそれらを抽出し、これをu_getIntPropertyValueに送ることだけです。

私はこのために作られているように思われ、あなたのコードを取り、u8_to_u32_iteratorを使用するように修正:なぜ、あまりにもUTF8ツーUTF32変換のためにそれを使用しないで、あなたはすでにICUを使用している場合は

#include <boost/regex/pending/unicode_iterator.hpp> 

inline size_t utf8PrintableSize(const std::string &str, std::locale loc) 
{ 
    size_t widthCount = 0; 
    for(boost::u8_to_u32_iterator<std::string::iterator> it(input.begin()), end(input.end()); it!=end; ++it) 
    { 
     ++widthCount; 

     int width = u_getIntPropertyValue(*it, UCHAR_EAST_ASIAN_WIDTH); 
     if ((width == U_EA_FULLWIDTH) || (width == U_EA_WIDE)) 
     { 
      ++widthCount; 
     } 

    } 
    return widthCount; 
} 
+0

ブーストの実装をありがとうございます。興味深いことに、これは正規表現ライブラリの一部であり、ロケールではありません。 – KyleL

2

は正しかった:直接ICSでこれを行う簡単な方法がある

は、ここに私の現在の醜いソリューションです。更新されたコードは以下の通りです。おそらくUnicodeStringを使用し、このシナリオでは全体的なブーストロケールの使用法をバイパスすることができます。

inline size_t utf8PrintableSize(const std::string &str, std::locale loc) 
{ 
    namespace ba = boost::locale::boundary; 
    ba::ssegment_index map(ba::character, str.begin(), str.end(), loc); 
    size_t widthCount = 0; 
    for (ba::ssegment_index::iterator it = map.begin(); it != map.end(); ++it) 
    { 
     ++widthCount; 

     //Note: Some unicode characters are 'full width' and consume more than one 
     // column on output. We will increment widthCount one extra time for 
     // these characters to ensure that space is properly allocated 
     UnicodeString ucs = UnicodeString::fromUTF8(StringPiece(it->str())); 
     UChar32 codePoint = ucs.char32At(0); 

     int width = u_getIntPropertyValue(codePoint, UCHAR_EAST_ASIAN_WIDTH); 
     if ((width == U_EA_FULLWIDTH) || (width == U_EA_WIDE)) 
     { 
      ++widthCount; 
     } 

    } 
    return widthCount; 
} 
+1

ゼロ幅の文字も処理することを忘れないでください! – o11c

+0

@ o11cあなたはこれを確認する方法を知っていますか?私はおそらく間違ったgoogle検索で空白を表示しています。 – KyleL

+0

'' Mn "、" Me "}やDefault_Ignorable_Code_Pointの' 'General_Category 'のようなものです。後者には書式設定文字やソフトハイフンなどが含まれています。しかし、ハングルの合成にはさらに複雑な作業が必要です。前の文字があった。 – o11c

関連する問題