web-dev-qa-db-ja.com

Java Unicode文字列の長さ

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

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

基本的には長さを測り、1文字ずつプリントしたいです。 「கு」、「மா」、「ர்」のように。

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

PS:タミル語です。

56
user1611248

あなたの問題の解決策を見つけました。

this SO answer に基づいて、オプションの修飾子を含む可能性のある文字を検索するために正規表現文字クラスを使用するプログラムを作成しました。これは、文字列を単一に分割します(必要に応じて結合されます) )文字を追加し、それらをリストに入れます。

import Java.util.*;
import Java.lang.*;
import Java.util.regex.*;

class Main
{
    public static void main (String[] args)
    {
        String s="குமார்";
        List<String> characters=new ArrayList<String>();
        Pattern pat = Pattern.compile("\\p{L}\\p{M}*");
        Matcher matcher = pat.matcher(s);
        while (matcher.find()) {
            characters.add(matcher.group());            
        }

        // Test if we have the right characters and length
        System.out.println(characters);
        System.out.println("String length: " + characters.size());

    }
}

どこ \\p{L}はUnicode文字を意味し、\\p{M}はUnicodeマークを意味します。

スニペットの出力は次のとおりです。

கு
மா
ர்
String length: 3

動作するデモについては https://ideone.com/Apkapn を参照してください


[〜#〜]編集[〜#〜]

http://en.wikipedia.org/wiki/Tamil_script の表から取得したすべての有効なタミル語の文字で正規表現を確認しました。現在の正規表現ではすべての文字が正しくキャプチャされないことがわかりました(Grantha複合テーブルの最後の行のすべての文字が2文字に分割されている)ので、正規表現を次の解決策に絞り込みました。

Pattern pat = Pattern.compile("\u0B95\u0BCD\u0BB7\\p{M}?|\\p{L}\\p{M}?");

上記のパターンの代わりにこのパターンを使用すると、文章をすべての有効なタミル語の文字に分割できるはずです(ウィキペディアのテーブルが完成している限り)。

チェックに使用したコードは次のとおりです。

String s = "ஃஅஆஇஈஉஊஎஏஐஒஓஔக்ககாகிகீகுகூகெகேகைகொகோகௌங்ஙஙாஙிஙீஙுஙூஙெஙேஙைஙொஙோஙௌச்சசாசிசீசுசூசெசேசைசொசோசௌஞ்ஞஞாஞிஞீஞுஞூஞெஞேஞைஞொஞோஞௌட்டடாடிடீடுடூடெடேடைடொடோடௌண்ணணாணிணீணுணூணெணேணைணொணோணௌத்ததாதிதீதுதூதெதேதைதொதோதௌந்நநாநிநீநுநூநெநேநைநொநோநௌப்பபாபிபீபுபூபெபேபைபொபோபௌம்மமாமிமீமுமூமெமேமைமொமோமௌய்யயாயியீயுயூயெயேயையொயோயௌர்ரராரிரீருரூரெரேரைரொரோரௌல்லலாலிலீலுலூலெலேலைலொலோலௌவ்வவாவிவீவுவூவெவேவைவொவோவௌழ்ழழாழிழீழுழூழெழேழைழொழோழௌள்ளளாளிளீளுளூளெளேளைளொளோளௌற்றறாறிறீறுறூறெறேறைறொறோறௌன்னனானினீனுனூனெனேனைனொனோனௌஶ்ஶஶாஶிஶீஶுஶூஶெஶேஶைஶொஶோஶௌஜ்ஜஜாஜிஜீஜுஜூஜெஜேஜைஜொஜோஜௌஷ்ஷஷாஷிஷீஷுஷூஷெஷேஷைஷொஷோஷௌஸ்ஸஸாஸிஸீஸுஸூஸெஸேஸைஸொஸோஸௌஹ்ஹஹாஹிஹீஹுஹூஹெஹேஹைஹொஹோஹௌக்ஷ்க்ஷக்ஷாக்ஷிக்ஷீக்ஷுக்ஷூக்ஷெக்ஷேக்ஷைஷொக்ஷோஷௌ";
List<String> characters = new ArrayList<String>();
Pattern pat = Pattern.compile("\u0B95\u0BCD\u0BB7\\p{M}?|\\p{L}\\p{M}?");
Matcher matcher = pat.matcher(s);
while (matcher.find()) {
    characters.add(matcher.group());
}

System.out.println(characters);
System.out.println(characters.size() == 325);
39
halex

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文字カテゴリを試してみてください。

15
Mifeet

これはreallyuglyであることがわかります...私はあなたの文字列をデバッグしました、そしてそれは以下の文字(そしてそれらの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;
  }

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

8
Thorsten S.

すでに述べたように、ストリングには6つの異なるコードポイントが含まれています。それらの半分は文字で、残りの半分は母音記号です。 (合マーク)

ICU4Jライブラリに組み込まれている transformations を使用して、次のルールを使用して、文字ではないすべての母音記号を削除できます。

[:^ Letter:]削除

結果の文字列をカウントします。彼らのデモサイトで試してください:

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

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

2
Charlie

これは、Unicode文字を考慮したJava Stringの長さを計算する新しい方法です。

int unicodeLength = str.codePointCount(0, str.length);
0
jordiburgos