web-dev-qa-db-ja.com

Javaで文字列の長さを正しく計算するにはどうすればよいですか?

String#lengthCharacterにはさまざまなメソッドがあり、コード単位/コードポイントで多かれ少なかれ機能することを知っています。

言語/ロケール、正規化、書記素クラスターなどを考慮して、Unicode標準( AX#29 )で指定された結果を実際に返すJavaで推奨される方法は何ですか?

19
soc

Java.text.BreakIterator はテキストを反復処理でき、「文字」、単語、文、行の境界についてレポートできます。

このコードを考えてみましょう:

def length(text: String, locale: Java.util.Locale = Java.util.Locale.ENGLISH) = {
  val charIterator = Java.text.BreakIterator.getCharacterInstance(locale)
  charIterator.setText(text)

  var result = 0
  while(charIterator.next() != BreakIterator.DONE) result += 1
  result
}

それを実行する:

scala> val text = "Thîs lóo̰ks we̐ird!"
text: Java.lang.String = Thîs lóo̰ks we̐ird!

scala> val length = length(text)
length: Int = 17

scala> val codepoints = text.codePointCount(0, text.length)
codepoints: Int = 21 

サロゲートペアの場合:

scala> val parens = "\uDBFF\uDFFCsurpi\u0301se!\uDBFF\uDFFD"
parens: Java.lang.String = ????surpíse!????

scala> val length = length(parens)
length: Int = 10

scala> val codepoints = parens.codePointCount(0, parens.length)
codepoints: Int = 11

scala> val codeunits = parens.length
codeunits: Int = 13

ほとんどの場合、これで十分です。

11
soc

Java文字列の長さの通常のモデル

String.length()は、文字列内のchar値( "コード単位")の数を返すものとしてspecifiedです。これは、a Java Stringの長さの最も一般的に役立つ定義です。以下を参照してください。

あなたの説明1 バッキングアレイ/アレイスライスのサイズに基づくlengthのセマンティクスの誤り。 length()によって返される値がまたであるという事実は、バッキング配列または配列スライスのサイズが単に典型的な実装の詳細Javaクラスライブラリ。Stringはそのように実装する必要はありません。実際、 Java文字列の実装は、その方法で実装されていないWASを見たことがあります。


ストリングの長さの代替モデル。

文字列内のUnicodeコードポイントの数を取得するには、str.codePointCount(0, str.length())を使用します- javadoc を参照してください。

他のエンコーディングで文字列のサイズ(バイト単位)を取得するには、str.getBytes(charset).lengthを使用します。

ロケール固有の問題に対処するには、 Normalizer を使用して、ストリングをユースケースに最も適した形式に正規化し、codePointCountを次のように使用できます上記。

しかし、場合によってはこれでも機能しないことがあります。例えばUnicode規格が明らかに対応していないハンガリーの文字カウント規則。


String.length()の使用は、通常は問題ありません。

ほとんどのアプリケーションがString.length()を使用する理由は、ほとんどのアプリケーションが人間中心の方法で単語、テキストなどの文字数を数えることに関係していないためです。たとえば、これを行うと:

_String s = "hi mum how are you";
int pos = s.indexOf("mum");
String textAfterMum = s.substring(pos + "mum".length());
_

"mum".length()がコードポイントを返さないことや、言語的に正しい文字数でないことは、実際には関係ありません。手元の作業に適したモデルで弦の長さを計測しています。そしてそれは機能します。

明らかに、多言語のテキスト分析を行う場合、状況は少し複雑になります。例えば単語を検索します。しかし、それでも、開始する前にテキストとパラメーターを正規化すると、ほとんどの場合、「コードポイント」ではなく「コード単位」で安全にコーディングできます。つまり、length()は引き続き機能します。


1-この説明は質問の一部のバージョンに関するものでした。十分な担当者がいる場合は、編集履歴を参照してください。

23
Stephen C

「文字列の長さ」が何を意味するかによって異なります。

  • String.length()chars 内の String の数を返します。これは通常、バッファの割り当てなどの関連タスクのプログラミングにのみ役立ちます。マルチバイトエンコーディングは問題を引き起こす可能性があるため、1つの char が1つの nicodeコードポイント を意味するわけではありません=。
  • String.codePointCount(int, int) および Character.codePointCount(CharSequence,int,int) はどちらも、String内のUnicodeコードポイントの数を返します。これは通常、マルチバイトエンコーディングの干渉を心配する必要なく、Stringを一連のUnicodeコードポイントとして見る必要がある関連タスクのプログラミングにのみ役立ちます。
  • BreakIterator.getCharacterInstance(Locale) を使用して、次の grapheme を特定のStringで取得 Locale 。これを複数回使用すると、String内の書記素の数を数えることができます。書記素は基本的に文字であるため(ほとんどの場合)、このメソッドはStringに含まれる書き込み可能な文字の数を取得するのに役立ちます。基本的に、このメソッドは、Stringの文字数を手動で数えた場合に得られるのとほぼ同じ数を返します。これにより、データを破損することなく、ユーザーインターフェイスのサイズ変更やStringsの分割などに役立ちます。

さまざまなメソッドのそれぞれがまったく同じデータに対して異なる長さを返す方法を理解するために、 this class を作成して、 this pageに含まれるUnicodeテキストの長さをすばやく生成します 、これは英語以外の文字を使用して多くの異なる言語の包括的なテストを提供するように設計されています。これは、3つの異なる方法で入力ファイルを正規化した後にそのコードを実行した結果です(正規化なし、 [〜#〜] nfc [〜#〜][〜#〜] nfd [〜#〜] ):

_Input UTF-8 String
>>  String.length() = 3431
>>  String.codePointCount(int,int) = 3431
>>  BreakIterator.getCharacterInstance(Locale) = 3386
NFC Normalized UTF-8 String
>>  String.length() = 3431
>>  String.codePointCount(int,int) = 3431
>>  BreakIterator.getCharacterInstance(Locale) = 3386
NFD Normalized UTF-8 String
>>  String.length() = 3554
>>  String.codePointCount(int,int) = 3554
>>  BreakIterator.getCharacterInstance(Locale) = 3386
_

ご覧のように、String.length()またはString.codePointCount(int,int)を使用すると、「同じように見える」Stringでも長さの結果が異なる可能性があります。

このトピックおよび他の同様のトピックの詳細については、 このブログ投稿 をお読みください。Unicodeを適切に処理するためのJavaの使用に関するさまざまな基本事項について説明しています。

5
Emily Mabrey

String.length()は、文字列を支える配列のサイズを返しませんが、「文字列内のUnicodeコード単位の数」として定義される文字列の実際の長さを返します。 ( APIドキュメント を参照)。

(コメントでスティーブンCが指摘したように、Unicodeコード単位== Java chars)

これがあなたが探しているものではない場合は、質問をもう少し詳しく説明する必要があります。

0
Grodriguez

つまり、言語の文法規則に従って文字列の長さを数えると、答えはノーになります。Javaにはそのようなアルゴリズムはなく、他のどこにもありません。

アルゴリズムがテキストの完全な意味分析も行わない限り、そうではありません。

たとえば、ハンガリー語では、szzsは、出現する単語の構成に応じて、1文字または2文字として数えることができます(例:országは5文字ですが、torzságは7です。)

Uodate:必要なのがUnicode標準の文字数だけである場合(これは、指摘したように正確ではありません)、文字列をNFKCに変換します Java.text.Normalizer が解決策になる可能性があります。

0
biziclop