56

ユニコード文字列の数を取得しようと懸命に努力しており、さまざまなオプションを試しました。小さな問題のように見えますが、大きな打撃を受けました。

ここでは、文字列 str1 の長さを取得しようとしています。6 と表示されていますが、実際には 3 です。文字列 "குமார்" の上にカーソルを移動すると、3 文字として表示されます。

基本的には長さを測って各文字を印刷したいです。「கு」、「மா」、「ர்」など。

 public class one {
    public static void main(String[] args) {
            String str1 = new String("குமார்");
            System.out.print(str1.length());
    }
}

PS : タミル語です。

4

5 に答える 5

15

Normalizerクラスを見てください。問題の原因について説明があります。Unicode では、次のようないくつかの方法で文字をエンコードできますÁ

  U+00C1    LATIN CAPITAL LETTER A WITH ACUTE

また

  U+0041    LATIN CAPITAL LETTER A
  U+0301    COMBINING ACUTE ACCENT

Normalizer文字列を構成された形式に変換してから、文字を反復処理するために使用することができます。


編集:上記の@halexによって提案された記事に基づいて、Javaでこれを試してください:

    String str = new String("குமார்");

    ArrayList<String> characters = new ArrayList<String>();
    str = Normalizer.normalize(str, Form.NFC);
    StringBuilder charBuffer = new StringBuilder();
    for (int i = 0; i < str.length(); i++) {
        int codePoint = str.codePointAt(i);
        int category = Character.getType(codePoint);
        if (charBuffer.length() > 0
                && category != Character.NON_SPACING_MARK
                && category != Character.COMBINING_SPACING_MARK
                && category != Character.CONTROL
                && category != Character.OTHER_SYMBOL) {
            characters.add(charBuffer.toString());
            charBuffer.delete(0, charBuffer.length());
        }
        charBuffer.appendCodePoint(codePoint);
    }
    if (charBuffer.length() > 0) {
        characters.add(charBuffer.toString());
    }
    System.out.println(characters);

私が得る結果はです[கு, மா, ர்]。すべての文字列でうまくいかない場合は、ifブロック内の他の Unicode 文字カテゴリをいじってみてください。

于 2013-04-11T11:55:07.783 に答える
8

これは本当に醜いことが判明しました....あなたの文字列をデバッグしましたが、次の文字(およびそれらの16進位置)が含まれています:

க 0x0b95
ு 0x0bc1
ம 0x0bae

0x0bbe ர 0x0bb0 ்
0x0bcd

そのため、タミル語は明らかに分音記号のようなシーケンスを使用して、残念ながら個別のエンティティとしてカウントされるすべての文字を取得します。

これは、他の回答で誤って主張されているように、UTF-8 / UTF-16 の問題ではなく、タミル語の Unicode エンコーディングに固有のものです。

提案されたノーマライザーは機能しません。タミル語は Unicode の「専門家」によって、正規化できない組み合わせシーケンスを明示的に使用するように設計されているようです。ああ。

私の次のアイデアは、文字を数えるのではなく、文字の視覚的表現であるグリフを数えることです。

String str1 = new String(Normalizer.normalize("குமார்", Normalizer.Form.NFC ));

Font display = new Font("SansSerif",Font.PLAIN,12);
GlyphVector vec = display.createGlyphVector(new FontRenderContext(new AffineTransform(),false, false),str1);

System.out.println(vec.getNumGlyphs());
for (int i=0; i<str1.length(); i++)
        System.out.printf("%s %s %s %n",str1.charAt(i),Integer.toHexString((int) str1.charAt(i)),vec.getGlyphVisualBounds(i).getBounds2D().toString());

結果:

க b95 [x=0.0,y=-6.0,w=7.0,h=6.0]
ு bc1 [x=8.0,y=-6.0,w=7.0,h=4.0]
ம bae [x=17.0,y=- 6.0,w=6.0,h =6.0] → bbe [x
=23.0,y=-6.0,w=5.0,h=6.0]
→ bb0 [x=30.0,y=-6.0,w=4.0,h=8.0]
் bcd [x=31.0,y=-9.0,w=1.0,h=2.0]

グリフが交差しているため、他のソリューションのように Java 文字型関数を使用する必要があります。

解決:

私はこのリンクを使用しています: http://www.venkatarangan.com/blog/content/binary/Counting%20Letters%20in%20an%20Unicode%20String.pdf

public static int getTamilStringLength(String tamil) {
    int dependentCharacterLength = 0;
    for (int index = 0; index < tamil.length(); index++) {
        char code = tamil.charAt(index);
        if (code == 0xB82)
            dependentCharacterLength++;
        else if (code >= 0x0BBE && code <= 0x0BC8)
            dependentCharacterLength++;
        else if (code >= 0x0BCA && code <= 0x0BD7)
            dependentCharacterLength++;
    }
    return tamil.length() - dependentCharacterLength;
  }

組み合わせ文字を除外し、それに応じて数える必要があります。

于 2013-04-11T12:17:04.293 に答える
2

前述のとおり、文字列には 6 つの異なるコード ポイントが含まれています。それらの半分は文字で、残りの半分は母音記号です。(合印)

ICU4J ライブラリに組み込まれた変換を使用して、規則を使用して文字ではないすべての母音記号を削除できます。

[:^文字:] 削除

結果の文字列を数えます。デモサイトで試してみてください:

http://demo.icu-project.org/icu-bin/translit

結果の文字列をエンドユーザーに表示することはありません。私は専門家ではないため、一般的なケースに到達するにはルールを微調整する必要があるかもしれませんが、それは考えです.

于 2013-04-11T12:51:30.657 に答える