web-dev-qa-db-fra.com

Comment calculer correctement la longueur d'une chaîne en Java?

Je sais qu'il existe String#length et les différentes méthodes de Character qui fonctionnent plus ou moins sur les unités de code/points de code.

Quelle est la méthode suggérée par Java pour renvoyer le résultat tel que spécifié par les normes Unicode ( UAX # 29 ), en prenant en compte des éléments tels que la langue/les paramètres régionaux, la normalisation et les grappes de graphèmes?

16
soc

Java.text.BreakIterator est capable de parcourir le texte et de signaler les limites de "caractère", Word, phrase et ligne.

Considérons ce 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
}

Le lancer:

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 

Avec des paires de substitution:

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

Cela devrait faire le travail dans la plupart des cas.

11
soc

Le modèle normal de longueur de chaîne Java

String.length() est spécifié as renvoyant le nombre de valeurs char ("unités de code") dans la chaîne. C’est la plus généralement utile définition de la longueur d’une chaîne Java; voir ci-dessous.

Votre description1 de la sémantique de length basée sur la taille du tableau de sauvegarde/de la tranche de tableau est incorrecte. Le fait que la valeur renvoyée par length() soit également la taille du tableau de sauvegarde ou de la tranche de tableau correspond à simplement est un détail d'implémentation de bibliothèques de classes Java typiques. String n'a pas besoin d'être implémenté de cette façon. En effet, je pense avoir déjà vu des implémentations de Java String dans lesquelles il ne l'était PAS.


Modèles alternatifs de longueur de corde.

Pour obtenir le nombre de points de code Unicode dans une chaîne, utilisez str.codePointCount(0, str.length()) - voir le fichier javadoc .

Pour obtenir la taille (en octets) d'une chaîne dans un autre encodage, utilisez str.getBytes(charset).length.

Pour traiter les problèmes propres aux paramètres régionaux, vous pouvez utiliser Normalizer pour normaliser la chaîne sous la forme la plus appropriée à votre cas d'utilisation, puis utiliser codePointCount comme ci-dessus.

Mais dans certains cas, même cela ne fonctionnera pas. par exemple. les règles de décompte des lettres en hongrois que la norme Unicode ne semble apparemment pas prendre en compte.


Utiliser String.length () est généralement correct

La raison pour laquelle la plupart des applications utilisent String.length() est que la plupart des applications ne se préoccupent pas de compter le nombre de caractères dans les mots, les textes, etc. de manière centrée sur l'homme. Par exemple, si je fais ceci:

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

le fait que "mum".length() ne renvoie pas de points de code ou qu’il ne s’agisse pas d’un nombre de caractères correct sur le plan linguistique importe peu. Il mesure la longueur de la chaîne en utilisant le modèle approprié à la tâche à accomplir. Et il fonctionne. 

Évidemment, les choses se compliquent un peu lorsque vous effectuez une analyse de texte multilingue. par exemple. rechercher des mots. Mais même dans ce cas, si vous normalisez votre texte et vos paramètres avant de commencer, vous pouvez coder en toute sécurité en termes d ’« unités de code »plutôt que de« points de code »la plupart du temps. length() fonctionne toujours.


1 - Cette description était sur certaines versions de la question. Voir l'historique des modifications ... si vous avez suffisamment de points de vente.

21
Stephen C

Cela dépend de ce que vous entendez par "longueur de la chaîne":

  • String.length() renvoie le nombre de chars dans le String . Cela n’est normalement utile que pour la programmation de tâches telles que l’allocation de tampons, car le codage sur plusieurs octets peut poser des problèmes, ce qui signifie qu’un char ne signifie pas un le point de code Unicode .
  • String.codePointCount(int, int) et Character.codePointCount(CharSequence,int,int) les deux renvoient le nombre de points de code Unicode dans la String. Cela n’est normalement utile que pour la programmation de tâches nécessitant de regarder une String comme une série de points de code Unicode sans s’inquiéter des interférences de codage sur plusieurs octets.
  • BreakIterator.getCharacterInstance(Locale) peut être utilisé pour obtenir le prochain grapheme dans une String pour le Locale donné. L’utilisation de cette fonction plusieurs fois peut vous permettre de compter le nombre de graphèmes dans une String. Comme les graphèmes sont fondamentalement lettres (dans la plupart des cas), cette méthode est utile pour obtenir le nombre de caractères en écriture que contient String. En gros, cette méthode retourne à peu près le même nombre que si vous comptiez manuellement le nombre de lettres dans la variable String, ce qui est utile pour des opérations telles que le dimensionnement des interfaces utilisateur et le fractionnement de Strings sans altérer les données.

Pour vous donner une idée de la façon dont chacune des méthodes peut renvoyer différentes longueurs pour les mêmes données, j'ai créé cette classe afin de générer rapidement les longueurs du texte Unicode contenu dans cette page , qui est conçu pour offrir un test complet de nombreuses langues différentes avec des caractères non anglais. Voici les résultats de l'exécution de ce code après la normalisation du fichier d'entrée de trois manières différentes (pas de normalisation, 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

Comme vous pouvez le constater, même la "même apparence" String peut donner des résultats différents pour la longueur si vous utilisez String.length() ou String.codePointCount(int,int)

Pour plus d'informations sur ce sujet et d'autres sujets similaires, vous devriez lire ce billet de blog qui couvre une variété de bases sur l'utilisation de Java pour gérer correctement Unicode.

4
Emily Mabrey

String.length() ne renvoie pas la taille du tableau sauvegardant la chaîne, mais la longueur réelle de la chaîne, définie comme "le nombre d'unités de code Unicode dans la chaîne". (voir Documents API ).

(Comme l'a souligné Stephen C dans les commentaires, unités de code Unicode == caractères Java)

Si ce n'est pas ce que vous recherchez, vous devriez peut-être élaborer un peu plus la question.

0
Grodriguez

Si vous voulez dire, compter la longueur d'une chaîne selon les règles grammaticales d'un langage, la réponse est non, il n'existe pas d'algorithme de ce type en Java, ni ailleurs.

Sauf si l'algorithme effectue également une analyse sémantique complète du texte.

En hongrois, par exemple, sz et zs peuvent compter pour une lettre ou deux, ce qui dépend de la composition du mot dans lequel ils apparaissent. (Exemple: ország est composé de 5 lettres, alors que torzság est composé de 7.)

Uodate : Si tout ce que vous voulez, c'est le nombre de caractères standard Unicode (qui, comme je l'ai indiqué, n'est pas précis), transformer votre chaîne en forme NFKC avec Java.text.Normalizer pourrait être une solution.

0
biziclop