2013-04-11 22 views
55

私はユニコード文字列の数を得るのが苦労していて、さまざまなオプションを試しました。小さな問題のように見えますが、大きな打撃を受けます。JavaのUnicode文字列の長さ

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

基本的には、長さを測定して各文字を印刷したいと考えています。 "கு"、 "மா"、 "ர்"のように。

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

PS:タミール語です。

+18

これは、問題の任意の違いはありませんが、 '新しい文字列を(「...」)'を使用する必要はありません、次のようにしてください: 'String str1 ="குமார் ";' – Jesper

+5

この問題に関する論文についてはhttp://www.venkatarangan.com/blog/content/binary/Counting%20Letters%20in%20an%20Unicode%20String.pdfを参照してください。 – halex

+0

ブログは本当に非常に有益です。しかし、それは文字列を3つの意味のある文字に分割するjavaのオプションを与えません。 – user1611248

答えて

38

問題の解決方法を見つけました。

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

EDIT

を参照してください、私は今から取られたすべての有効なタミール語の文字で私の正規表現をチェックします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); 
+1

いいですね! +1 –

+1

はい、私はそれがタミル語で起こることができるすべてのケースを処理するかどうか分かりませんが、それは間違いなくエレガントです。 – Mifeet

+0

どのような美しい文字セット! –

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

+4

文字列を正規化して長さを測定しようとしました。まだ6として取得しています。ブラウザエディタでカーソルナビゲーションを3文字として識別できる場合は、javaで標準的なメソッドを使用していませんか? – user1611248

+2

この場合は正しくありませんが、他の問題のヒントはあります。 +1 –

+0

編集後:私は他の言語にも使用されているかもしれません。 –

8

これは本当に醜いであることが判明した.... 私はあなたの文字列をデバッグしているし、それが文字(およびその六角位置を)次のものが含まれます。

க0x0b95
ு0x0bc1
ம0x0bae
ா0x0bbe
ர0x0bb0
்0x0bcd

だから、タミル語は明らかに発音区別符号-などを使用残念ながら別のエンティティとしてカウントされるすべての文字を に取得するシーケンス。

これはUTF-8/UTF-16の問題ではありません。間違いなく という別の回答があります。これは、タミール語 の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()); 

結果:、yはwは、-6.0を=
ுBC1 [X = 8.0

க[7.0、H = 6.0 = W、Y、X = 0.0、= -6.0、] B95 = 5.0、h = 6.0]
∨bae[x = 17.0、y = -6.0、w = 6.0、h = 6.0]
ribe bbe [x = 23.0、y = -6.0、w = 5.0、h = 6.0]
ரのBB0のよう

[1.0、H = 2.0 = W、X = 31.0、Y = -9.0]
்BCD [X = 30.0、yは4.0、H = 8.0 = W、-6.0 =] gl yphsが交差している場合は、他の解決策のようなJava文字タイプ の関数を使用する必要があります。

SOLUTION:あなたは組み合わせ文字を除外し、それに応じてそれらをカウントする必要が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; 
    } 

私は、このリンクを使用しています。

2

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

[:^手紙:]あなたはルールを使用して文字ではありません母音記号のすべてを削除するには、ICU4Jライブラリーに組み込まれてtransformationsを使用することができます

(組み合わせマーク)

カウント削除結果の文字列彼らのデモサイト上でそれを試してみてください:

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

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

+5

6文字か3文字かにかかわらず、 "文字"の定義に完全に依存します。残念ながら、その言葉はよく定義されておらず、さまざまな互換性のない方法で使用されています。あなたの声明は、あなたが "文字ポイント"を意味する "文字"を取った場合にのみ正しいです。 – delnan

+0

ああ。編集されました。 – Charlie