web-dev-qa-db-de.com

Wie berechne ich die Länge eines Strings in Java richtig?

Ich weiß, dass es String#length und die verschiedenen Methoden in Character gibt, die mehr oder weniger mit Codeeinheiten/Codepunkten arbeiten.

Welche Methode wird in Java vorgeschlagen, um das Ergebnis gemäß den Unicode-Standards ( UAX # 29 ) zurückzugeben, wobei Dinge wie Sprache/Gebietsschema, Normalisierung und Graphem-Cluster berücksichtigt werden?

16
soc

Java.text.BreakIterator kann Text überlaufen und kann über "Zeichen", Wort, Satz und Zeilengrenzen berichten.

Betrachten Sie diesen Code:

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
}

Ausführen es:

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 

Mit Ersatzpaaren:

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

Dies sollte in den meisten Fällen die Aufgabe erfüllen.

11
soc

Das normale Modell der Java-Stringlänge

String.length() ist angegeben as als Anzahl der char-Werte ("Code-Units") im String. Dies ist die am allgemeinsten nützliche Definition der Länge eines Java-Strings; siehe unten.

Deine Beschreibung1 der Semantik von length basierend auf der Größe des unterstützenden Array-/Array-Slice ist falsch. Die Tatsache, dass der von length() zurückgegebene Wert auch ist, die Größe des Backing Arrays oder des Array Slice ist nur ein Implementierungsdetail typischer Java-Klassenbibliotheken. String muss nicht auf diese Weise implementiert werden. Ich glaube, ich habe Java String-Implementierungen gesehen, bei denen es NICHT so implementiert wurde.


Alternative Modelle der Stringlänge.

Um die Anzahl der Unicode-Codepunkte in einem String zu erhalten, verwenden Sie str.codePointCount(0, str.length()) - siehe den Javadoc .

Um die Größe (in Bytes) eines Strings in einer anderen Codierung zu ermitteln, verwenden Sie str.getBytes(charset).length.

Um länderspezifische Probleme zu beheben, können Sie den String mithilfe von Normalizer auf das für Ihren Anwendungsfall am besten geeignete Formular normalisieren. Anschließend können Sie codePointCount wie oben verwenden.

In einigen Fällen funktioniert dies jedoch nicht. z.B. die ungarischen Buchstabenzählregeln, die der Unicode-Standard offenbar nicht berücksichtigt.


Die Verwendung von String.length () ist im Allgemeinen in Ordnung

Der Grund, dass die meisten Anwendungen String.length() verwenden, besteht darin, dass sich die meisten Anwendungen nicht mit dem Zählen der Anzahl von Zeichen in Wörtern, Texten usw. auf eine menschenzentrierte Weise beschäftigen. Wenn ich das zum Beispiel mache:

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

es ist wirklich egal, dass "mum".length() keine Codepunkte zurückgibt oder dass es sich nicht um eine sprachlich korrekte Zeichenanzahl handelt. Es misst die Länge des Strings anhand des Modells, das für die vorliegende Aufgabe geeignet ist. Und es funktioniert. 

Offensichtlich werden die Dinge bei der mehrsprachigen Textanalyse etwas komplizierter. z.B. nach Wörtern suchen Aber selbst wenn Sie Ihren Text und Ihre Parameter normalisieren, bevor Sie beginnen, können Sie sicher in Form von "Code-Einheiten" anstelle von "Codepunkten" codieren. d.h. length() funktioniert immer noch.


1 - Diese Beschreibung bezieht sich auf einige Versionen der Frage. Sehen Sie sich den Bearbeitungsverlauf an ... wenn Sie genügend Wiederholungspunkte haben.

21
Stephen C

Es hängt davon ab, was genau Sie unter "Länge von [der] Zeichenfolge" verstehen:

  • String.length() gibt die Nummer von chars in der String zurück. Dies ist normalerweise nur für das Programmieren verwandter Aufgaben wie das Zuweisen von Puffern nützlich, da die Multibyte-Codierung Probleme verursachen kann. Ein char bedeutet nicht einen Unicode-Codepunkt .
  • String.codePointCount(int, int) und Character.codePointCount(CharSequence,int,int) geben beide die Anzahl der Unicode-Codepunkte in der String zurück. Dies ist normalerweise nur für das Programmieren von verwandten Aufgaben nützlich, bei denen eine String als eine Reihe von Unicode-Codepunkten betrachtet werden muss, ohne sich um Interferenzen mit der Multibyte-Codierung sorgen zu müssen.
  • BreakIterator.getCharacterInstance(Locale) kann verwendet werden, um das nächste grapheme in einer String für die angegebene Locale abzurufen. Wenn Sie dies mehrfach verwenden, können Sie die Anzahl der Grapheme in einer String zählen. Da Grapheme im Grunde Buchstaben sind (in den meisten Fällen), ist diese Methode hilfreich, um die Anzahl der schreibbaren Zeichen zu ermitteln, die die Variable String enthält. Im Wesentlichen gibt diese Methode ungefähr die gleiche Anzahl zurück, die Sie erhalten würden, wenn Sie die Anzahl der Buchstaben in der Variablen String manuell gezählt hätten. Dies macht sie beispielsweise für die Größenanpassung von Benutzeroberflächen und das Teilen von Strings hilfreich, ohne die Daten zu beschädigen.

Um Ihnen eine Vorstellung davon zu geben, wie die verschiedenen Methoden unterschiedliche Längen für genau dieselben Daten zurückgeben können, habe ich diese Klasse erstellt, um schnell die Längen des in dieser Seite enthaltenen Unicode-Texts zu generieren entworfen, um einen umfassenden Test für viele verschiedene Sprachen mit nichtenglischen Zeichen zu bieten. Hier sind die Ergebnisse der Ausführung dieses Codes nach der Normalisierung der Eingabedatei auf drei verschiedene Arten (keine Normalisierung, 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

Wie Sie sehen, könnte selbst die "gleich aussehende" String unterschiedliche Ergebnisse für die Länge ergeben, wenn Sie entweder String.length() oder String.codePointCount(int,int) verwenden. 

Weitere Informationen zu diesem Thema und zu ähnlichen Themen finden Sie in diesem Blogbeitrag , der eine Vielzahl von Grundlagen zur Verwendung von Java für die korrekte Handhabung von Unicode enthält.

4
Emily Mabrey

String.length() gibt nicht die Größe des Arrays zurück, das den String unterstützt, sondern die tatsächliche Länge des Strings, definiert als "Anzahl der Unicode-Codeeinheiten im String". (Siehe API-Dokumente ).

(Wie Stephen C in den Kommentaren ausgeführt hat, sind Unicode-Codeeinheiten == Java-Zeichen)

Wenn dies nicht das ist, wonach Sie suchen, dann sollten Sie die Frage vielleicht etwas ausführlicher behandeln.

0
Grodriguez

Wenn Sie die Länge eines Strings gemäß den grammatikalischen Regeln einer Sprache zählen, lautet die Antwort nein. Es gibt keinen solchen Algorithmus in Java oder sonstwo.

Es sei denn, der Algorithmus führt auch eine vollständige semantische Analyse des Textes durch.

In Ungarisch können sz und zs als ein oder zwei Buchstaben gezählt werden, was von der Zusammensetzung des Wortes abhängt, in dem sie erscheinen. (Beispiel: ország ist 5 Buchstaben, wohingegen torzság 7 ist.)

Uodate : Wenn Sie nur die Anzahl der Unicode-Standardzeichen (die, wie bereits erwähnt, nicht genau ist) benötigen, kann die Umwandlung Ihrer Zeichenfolge in das Formular NFKC mit Java.text.Normalizer eine Lösung sein.

0
biziclop