21

UTF-16leUTF-8など、さまざまな Unicode エンコーディングでは、1 文字が 2 バイトまたは 3 バイトを占める場合があります。多くの Unicode アプリケーションは、Unicode 文字がすべてラテン文字であるように、Unicode 文字の表示幅を考慮しません。たとえば、80列のテキストでは、1 行に40 個の漢字または80 個のラテン文字を含める必要がありますが、ほとんどのアプリケーション (Eclipse、Notepad++、およびすべてのよく知られているテキスト エディターなど、良い例外があれば敢えて) をカウントするだけです。各漢字をラテン文字として 1 幅として。これは確かに結果のフォーマットを醜く、整列させません。

たとえば、タブ幅が 8 の場合、次のように醜い結果が得られます (すべての Unicode を 1 表示幅としてカウントします)。

apple   10
banana  7
苹果      6
猕猴桃     31
pear    16

ただし、予想される形式は次のとおりです (各漢字を 2 幅としてカウントします)。

apple   10
banana  7
苹果    6
猕猴桃  31
pear    16

文字の表示幅の計算が不適切なため、これらのエディターは、タブの配置、行の折り返し、段落の再フォーマットを行うときにまったく役に立たなくなります。

ただし、文字の幅はフォントによって異なる場合がありますが、固定サイズの端末フォントのすべての場合、漢字は常に倍幅です。つまり、フォントに関係なく、各漢字は 2 幅で表示することが望ましいということです。

解決策の 1 つは、エンコーディングをGB2312に変換することで正しい幅を取得できることです。GB2312エンコーディングでは、各漢字が 2 バイトかかります。ただし、一部の Unicode 文字は GB2312 文字セット (またはGBK文字セット)には存在しません。また、一般に、エンコードされたサイズ (バイト単位) から表示幅を計算することはお勧めできません。

Unicode の ( \u0080.. \uFFFF) の範囲内のすべての文字を単純に 2 幅として計算することも正しくありません。これは、範囲内に 1 幅の文字が多数散在しているためです。

また、アラビア文字や韓国語の文字は、任意の数の Unicode コード ポイントで単語/文字を構成するため、表示幅を計算するのも困難です。

そのため、Unicode コード ポイントの表示幅は整数ではない可能性がありますが、それで問題ないと思います。実際には、整数に固定することができます。少なくとも、何もないよりはましです。

では、Unicode 標準の char の優先表示幅に関連する属性はありますか? または、表示幅を計算する Java ライブラリ関数はありますか?

4

5 に答える 5

25

IEEE Std 1003.1-2001で定義されているが、ISOCから削除されたwcwidthandのようなものを探しているようです。wcswidth

この関数は、ワイド文字wcwcwidth()に必要な列位置の数を決定します。この関数は、0(wcがヌルのワイド文字コードの場合)を返すか、ワイド文字コードwcが占める列位置の数を返すか、 -1(wcが印刷可能なワイドに対応しない場合)を返します。 -文字コード)。wcwidth()

Markus Kuhnは、 Unicode5.0に基づくオープンソースバージョンwcwidth.cを作成しました。これには、問題の説明と、この分野の基準の欠如の認識が含まれています。

固定幅出力デバイスでは、ラテン文字はすべて同じ幅の単一の「セル」位置を占めますが、表意文字のCJK文字はそのような2つのセルを占めます。UTF-8エンコーディングを使用した端末回線アプリケーションと(テレタイプスタイルの)文字端末間の相互運用性には、どの文字がセル位置の数だけカーソルを進めるかについての合意が必要です。現在、Unicode文字が文字端末のセル位置を占める正式な標準は確立されていません。これらのルーチンは、ユニコードコンソーシアムによって提供されるデータに適用される単純なルールに基づいてそのような動作を定義する最初の試みです。[...]

次のルールを実装します。

  • ヌル文字(U + 0000)の列​​幅は0です。
  • 他のC0/C1制御文字とDELは、-1の戻り値になります。
  • 間隔のない結合文字(Unicodeデータベースの一般的なカテゴリコードMnまたはMe)の列幅は0です。
  • ソフトハイフン(U + 00AD)の列幅は1です。
  • その他のフォーマット文字(Unicodeデータベースの一般カテゴリコードCf)およびゼロ幅スペース(U + 200B)の列幅は0です。
  • ハングルジャモの内側母音と最後の子音(U + 1160-U + 11FF)の列幅は0です。
  • Unicodeテクニカルレポート#11で定義されている東アジアワイド(W)または東アジア全幅(F)カテゴリの間隔文字の列幅は2です。
  • 残りのすべての文字(印刷可能なすべてのISO 8859-1およびWGL4文字、Unicode制御文字などを含む)の列幅は1です。
于 2012-02-04T23:54:51.543 に答える
5

この概念を反映する Unicode プロパティはEast_Asian_Widthです。一般的な Unicode レンダリングのコンテキストでの視覚的な幅としては、実際には信頼できません。アジア以外の文字、文字の組み合わせなどは等幅フォントでも整列できません。(あなたの例は確かに私のために並べられていません。)

Java には、文字のこのプロパティを読み取る機能が組み込まれていません (ただし、Android の拡張機能にはあります)。本当に必要な場合は、 ICU4Jから入手できます。

于 2010-09-04T06:57:58.343 に答える
5

コードポイント、書記素、エンコーディングを混同しています。

エンコーディングは、格納、送信、または処理のためにコード ポイントをオクテット ストリームに変換する方法です。UTF-8 と UTF-16 はどちらも可変幅エンコーディングであり、異なるコード ポイントには異なる数のオクテットが必要です (UTF-8 の場合は 1 から IIRC、6 まで、UTF-16 は 2 または 4 のいずれか)。

書記素は「私たちが文字として見るもの」であり、表示されるものです。1 つの書記素に対して 1 つのコード ポイント (LATIN LOWER CASE A など) が必要ですが、それ以外の場合は複数のコード ポイントが必要になることがあります (たとえば、LATIN LOWER CASE A、COMBINING ACUTE および COMBINING UNDERSCORE を使用して、Kwakwala で使用される急性およびアンダースコアを含む小文字を取得します)。 . 場合によっては、同じ書記素を作成するコード ポイントの組み合わせが複数ある場合があります (例: LATIN LOWER CASE A WITH ACUTE と COMBINING UNDERSCORE)。これが「正規化」です。

つまり、単一の書記素のエンコーディングの長さは、エンコーディングと正規化に依存します。

書記素の表示幅は、エンコーディングの長さとは関係なく、書体、スタイル、およびサイズによって異なります。

詳細については、Wikipedia on UnicodeおよびUnicode のホームを参照してください。いくつかの優れた本もあり、おそらく最も注目すべきは、O'Reilly の Yannis Haralambous による「 Fonts & Encodings 」です。

于 2010-09-03T10:08:51.530 に答える
3

これを正しく行うには、Unicode Standard Annex #14、Unicode Line Breaking Algorithmとして知られる公開された Unicode 標準のコンポーネントを考慮する必要があると思います。

Perl でプログラミングしている場合、 UAX#14 を実装するPerl のUnicode::LineBreakcolumnsモジュールには、文字列引数の正しい答えを示す単純なメソッドを持つクラスが含まれているため、知りたいことは非常に簡単です。これらのことは、絶対に他に何もできないアジア言語で特にうまく機能します。このモジュールには 6,000 を超える単体テストが含まれており、積極的にメンテナンスされており、作者自身もアジア人であるため、これらのトリッキーな部分を正確に理解することが彼にとって重要です。

モジュールの根幹のほとんどは C で書かれたライブラリです。そのコンポーネントの C ライブラリを Perl 以外の他の言語から呼び出す方法については調べていませんが、これが可能かどうかを調べてみてください。

于 2012-02-09T11:41:45.300 に答える
1

「または、表示幅を計算するためのJavaライブラリ関数はありますか?」について:もしあれば、私はそれを見つけたことがありません。

文字/文字列の幅を計算する最も簡単な方法は、GNU Unicode フォント ( http://unifoundry.com/unifont.html ) で書き、文字幅を測定することです。きれいではありませんが、これまでのところ、考えられるすべてのエンコーディングで機能しています。

FWIWここに私がしていることがあります:

java.awt.font.Font MONOSPACEFONT = Font.createFont(Font.TRUETYPE_FONT, 
    new File("unifont-5.1.20080907.ttf"));

java.awt.font.FontRenderContext FRC = new FontRenderContext(null, true, true);

int charWidth =  (int) (2.0*((java.awt.geom.Rectangle2D.Float) 
    MONOSPACEFONT.getStringBounds(stringToMeasure, FRC)).width);

... これは、JVM をデプロイするほとんどの場所で機能するはずです (ヘッドレス環境では問題なく動作します)。

于 2012-07-25T18:26:30.923 に答える