28 votes

La fonction toLowerCase() de Java préserve-t-elle la longueur originale des chaînes de caractères ?

Supposons deux objets Java String :

String str = "<my string>";
String strLower = str.toLowerCase();

Est-il alors vrai que pour chaque valeur de <my string> l'expression

str.length() == strLower.length()

évalue à true ?

Alors, est-ce que String.toLowerCase() préserver la longueur originale de la chaîne pour toute valeur de String ?

44voto

codaddict Points 154968

Étonnamment, c'est le cas pas ! !

Extrait de la documentation Java de toLowerCase

Convertit tous les caractères de cette chaîne en minuscules en utilisant les règles de la langue donnée. La conversion en minuscules est basée sur la version de la norme Unicode spécifiée par la classe Character. Étant donné que les mappages de cas ne sont pas toujours des mappages de caractères 1:1, la chaîne résultante peut avoir une longueur différente de la chaîne d'origine.

Exemple :

package com.stackoverflow.q2357315;

import java.util.Locale;

public class Test {
    public static void main(String[] args) throws Exception {
        Locale.setDefault(new Locale("lt"));
        String s = "\u00cc";
        System.out.println(s + " (" + s.length() + ")"); // Ì (1)
        s = s.toLowerCase();
        System.out.println(s + " (" + s.length() + ")"); // i̇̀ (3)
    }
}

5 votes

Pouvez-vous citer quelques exemples ? Je connais plusieurs exemples qui feraient que la variante en majuscules aurait une taille différente de celle en minuscules, par ex. ß deviendrait SS mais pas l'inverse.

7 votes

@BalusC : Il y a quelques règles fantaisistes concernant la combinaison des caractères dans les locales AZ, LT et TR, cf. java/lang/ConditionalSpecialCasing.java . Par exemple, "\u00cc".toLowerCase(new Locale("lt")).length() == 3

2 votes

Cool, merci pour le pointeur. Je vais donc pouvoir éditer un SSCCE dans cette réponse.

4voto

Joachim Sauer Points 133411

Tout d'abord, je tiens à souligner que je suis tout à fait d'accord avec la réponse (actuellement la mieux notée) de @codaddict.

Mais je voulais faire une expérience, alors la voici :

Il ne s'agit pas d'une preuve formelle, mais ce code a fonctionné pour moi sans jamais atteindre l'intérieur de l'enveloppe de l'ordinateur. if (en utilisant le JDK 1.6.0 Update 16 sur Ubuntu) :

Edit : Voici un code mis à jour qui gère également les Locales :

import java.util.Locale;

public class ToLowerTester {
    public final Locale locale;

    public ToLowerTester(final Locale locale) {
        this.locale = locale;
    }

    public String findFirstStrangeTwoLetterCombination() {
        char[] b = new char[2];
        for (char c1 = 0; c1 < Character.MAX_VALUE; c1++) {
            b[0] = c1;
            for (char c2 = 0; c2 < Character.MAX_VALUE; c2++) {
                b[1] = c2;
                final String string = new String(b);
                String lower = string.toLowerCase(locale);
                if (string.length() != lower.length()) {
                    return string;
                }
            }
        }
        return null;
    }
    public static void main(final String[] args) {
        Locale[] locales;
        if (args.length != 0) {
            locales = new Locale[args.length];
            for (int i=0; i<args.length; i++) {
                locales[i] = new Locale(args[i]);
            }
        } else {
            locales = Locale.getAvailableLocales();
        }
        for (Locale locale : locales) {
            System.out.println("Testing " + locale + "...");
            String result = new ToLowerTester(locale).findFirstStrangeTwoLetterCombination();
            if (result != null) {
                String lower = result.toLowerCase(locale);
                System.out.println("Found strange two letter combination for locale "
                    + locale + ": <" + result + "> (" + result.length() + ") -> <"
                    + lower + "> (" + lower.length() + ")");
            }
        }
    }
}

L'exécution de ce code avec les noms de locale mentionnés dans la réponse acceptée permettra d'imprimer quelques exemples. L'exécuter sans argument essayera toutes les locales disponibles (et prendra un certain temps !).

Ce n'est pas exhaustif, car en théorie, il pourrait y avoir des chaînes de caractères multiples qui se comportent différemment, mais c'est une bonne première approximation.

Notez également que de nombreuses combinaisons de deux caractères produites de cette manière sont probablement invalides en UTF-16. Le fait que rien n'explose dans ce code ne peut être attribué qu'à une API String très robuste en Java.

Enfin, même si l'hypothèse est vraie pour la mise en œuvre actuelle de Java, cela peut facilement changer lorsque les futures versions de Java mettront en œuvre les futures versions de la norme Unicode, dans lesquelles les règles pour les nouveaux caractères peuvent introduire des situations où cela n'est plus vrai.

Donc dépendre de ça est toujours une assez mauvaise idée.

3 votes

Vous devez savoir que le code que vous avez écrit dépend de la locale par défaut. Pas évident, mais désagréable.

2voto

User Points 43

Rappelez-vous également que toUpperCase() ne préserve pas non plus la longueur. Exemple : "straße" devient "STRASSE" pour la version allemande. Vous êtes donc plus ou moins dans la merde si vous travaillez avec des chaînes de caractères sensibles à la casse et que vous avez besoin de stocker l'index pour quelque chose.

0 votes

Puisque straße et strasse sont des orthographes correctes (en ignorant le fait qu'ils devraient avoir un S majuscule initial parce qu'ils sont des noms), je suppose que cela aura pour effet secondaire intéressant que passer en majuscule et revenir en arrière donnera une chaîne différente ? L'avez-vous essayé ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X