web-dev-qa-db-fra.com

Y a-t-il un moyen de se débarrasser des accents et de convertir une chaîne entière en lettres normales?

Existe-t-il un meilleur moyen de supprimer les accents et de rendre ces lettres régulières en dehors de la méthode String.replaceAll() et du remplacement des lettres une par une? Exemple:

Entrée: orčpžsíáýd

Sortie: orcpzsiayd

Il n'est pas nécessaire d'inclure toutes les lettres avec des accents comme l'alphabet russe ou le chinois.

227
Martin

Utilisez Java.text.Normalizer pour gérer cela pour vous.

_string = Normalizer.normalize(string, Normalizer.Form.NFD);
// or Normalizer.Form.NFKD for a more "compatable" deconstruction 
_

Cela séparera toutes les marques d'accentuation des caractères. Ensuite, il vous suffit de comparer chaque caractère avec le statut de lettre et de jeter ceux qui ne le sont pas.

_string = string.replaceAll("[^\\p{ASCII}]", "");
_

Si votre texte est en unicode, vous devriez plutôt utiliser ceci:

_string = string.replaceAll("\\p{M}", "");
_

Pour unicode, _\\P{M}_ correspond au glyphe de base et _\\p{M}_ (minuscule) correspond à chaque accent.

Merci à GarretWilson pour le pointeur et regular-expressions.info pour le super guide Unicode.

352
Erick Robertson

À partir de 2011, vous pouvez utiliser Apache Commons StringUtils.stripAccents (entrée) (depuis la version 3.0):

    String input = StringUtils.stripAccents("Tĥïŝ ĩš â fůňķŷ Šťŕĭńġ");
    System.out.println(input);
    // Prints "This is a funky String"

Remarque:

La réponse acceptée (Erick Robertson) ne fonctionne pas pour Ø ou Ł. Apache Commons 3.5 ne fonctionne pas pour Ø non plus, mais cela fonctionne pour £. Après avoir lu le article de Wikipedia pour Ø , je ne suis pas sûr qu'il faille le remplacer par "O": il s'agit d'une lettre séparée en norvégien et en danois, alphabétisée après "z". C'est un bon exemple des limites de l'approche "strip accents".

114
DavidS

La solution de @ virgo47 est très rapide, mais approximative. La réponse acceptée utilise Normalizer et une expression régulière. Je me suis demandé quelle partie du temps a été prise par Normalizer par rapport à l'expression régulière, puisque la suppression de tous les caractères non-ASCII peut être effectuée sans regex:

import Java.text.Normalizer;

public class Strip {
    public static String flattenToAscii(String string) {
        StringBuilder sb = new StringBuilder(string.length());
        string = Normalizer.normalize(string, Normalizer.Form.NFD);
        for (char c : string.toCharArray()) {
            if (c <= '\u007F') sb.append(c);
        }
        return sb.toString();
    }
}

De petites accélérations supplémentaires peuvent être obtenues en écrivant dans un caractère [] et en n'appelant pas toCharArray (), bien que je ne sois pas sûr que la diminution de la clarté du code le mérite:

public static String flattenToAscii(String string) {
    char[] out = new char[string.length()];
    string = Normalizer.normalize(string, Normalizer.Form.NFD);
    int j = 0;
    for (int i = 0, n = string.length(); i < n; ++i) {
        char c = string.charAt(i);
        if (c <= '\u007F') out[j++] = c;
    }
    return new String(out);
}

Cette variation a l'avantage de l'exactitude de celui qui utilise le normalisateur et de la vitesse de celui qui utilise un tableau. Sur ma machine, celle-ci est environ 4 fois plus rapide que la réponse acceptée et de 6,6 à 7 fois plus lente que celle de @ virgo47 (la réponse acceptée est environ 26 fois plus lente que celle de @ virgo47 sur ma machine).

50
David Conrad

EDIT: Si vous n'êtes pas coincé avec Java <6 et que la vitesse n'est pas critique et/ou que la table de traduction est trop contraignante, utilisez answer by David. Le but est d'utiliser Normalizer (introduit dans Java 6) au lieu de la table de traduction à l'intérieur de la boucle.

Bien que cette solution ne soit pas "parfaite", elle fonctionne bien lorsque vous connaissez la plage (dans notre cas, Latin1,2), qui a fonctionné avant Java 6 (ce n’est pas un problème réel cependant) et qui est beaucoup plus rapide que la plupart version suggérée (peut être ou ne pas être un problème):

    /**
 * Mirror of the unicode table from 00c0 to 017f without diacritics.
 */
private static final String tab00c0 = "AAAAAAACEEEEIIII" +
    "DNOOOOO\u00d7\u00d8UUUUYI\u00df" +
    "aaaaaaaceeeeiiii" +
    "\u00f0nooooo\u00f7\u00f8uuuuy\u00fey" +
    "AaAaAaCcCcCcCcDd" +
    "DdEeEeEeEeEeGgGg" +
    "GgGgHhHhIiIiIiIi" +
    "IiJjJjKkkLlLlLlL" +
    "lLlNnNnNnnNnOoOo" +
    "OoOoRrRrRrSsSsSs" +
    "SsTtTtTtUuUuUuUu" +
    "UuUuWwYyYZzZzZzF";

/**
 * Returns string without diacritics - 7 bit approximation.
 *
 * @param source string to convert
 * @return corresponding string without diacritics
 */
public static String removeDiacritic(String source) {
    char[] vysl = new char[source.length()];
    char one;
    for (int i = 0; i < source.length(); i++) {
        one = source.charAt(i);
        if (one >= '\u00c0' && one <= '\u017f') {
            one = tab00c0.charAt((int) one - '\u00c0');
        }
        vysl[i] = one;
    }
    return new String(vysl);
}

Les tests effectués sur mon disque dur avec JDK 32 bits montrent que cette opération effectue la conversion de àèéľšťč89FDČ en aeelstc89FDC 1 million de fois en ~ 100 ms, tandis que la méthode Normalizer la fait en 3.7s (37x plus lente). Si vos besoins portent sur les performances et que vous connaissez la plage d'entrée, celle-ci peut être pour vous.

Prendre plaisir :-)

28
virgo47
System.out.println(Normalizer.normalize("àèé", Normalizer.Form.NFD).replaceAll("\\p{InCombiningDiacriticalMarks}+", ""));

a travaillé pour moi. La sortie de l'extrait ci-dessus donne "aee", ce que je voulais, mais

System.out.println(Normalizer.normalize("àèé", Normalizer.Form.NFD).replaceAll("[^\\p{ASCII}]", ""));

n'a pas fait de substitution.

20
Nico

Selon la langue, ceux-ci peuvent ne pas être considérés comme des accents (qui changent le son de la lettre), mais des signes diacritiques

https://en.wikipedia.org/wiki/Diacritic#Languages_with_letters_containing_diacritics

"Le bosniaque et le croate ont les symboles č, ć, đ, š et ž, qui sont considérés comme des lettres distinctes et sont énumérés comme tels dans les dictionnaires et autres contextes dans lesquels les mots sont énumérés par ordre alphabétique."

Les supprimer peut être une modification intrinsèque du sens de la Parole ou la transformation des lettres en lettres complètement différentes.

6
NinjaCat

Je suggère Junidecode . Il gérera non seulement 'Ł' et 'Ø', mais il fonctionnera également bien pour la transcription d'alphabets, tels que le chinois, en alphabet latin.

3
OlgaMaciaszek

J'ai rencontré le même problème lié au contrôle d'égalité des chaînes, l'une des chaînes de comparaison a code de caractère ASCII 128-255 .

c'est-à-dire Espace insécable - [Hex - A0] Espace [Hex - 20]. Pour afficher un espace insécable sur HTML. J'ai utilisé ce qui suit spacing entities . Leur caractère et ses octets ressemblent à &emsp is very wide space[ ]{-30, -128, -125}, &ensp is somewhat wide space[ ]{-30, -128, -126}, &thinsp is narrow space[ ]{32} , Non HTML Space {}

_String s1 = "My Sample Space Data", s2 = "My Sample Space Data";
System.out.format("S1: %s\n", Java.util.Arrays.toString(s1.getBytes()));
System.out.format("S2: %s\n", Java.util.Arrays.toString(s2.getBytes()));
_

Sortie en octets:

S1: [77, 121, _32_, 83, 97, 109, 112, 108, 101, _32_, 83, 112, 97, 99, 101, _32_, 68, 97, 116, 97] S2: [77, 121, _-30, -128, -125_, 83, 97, 109, 112, 108, 101, _-30, -128, -125_, 83, 112, 97, 99, 101, _-30, -128, -125_, 68, 97, 116, 97]

Utilisez le code ci-dessous pour différents espaces et leurs octets-codes: wiki for List_of_Unicode_characters

_String spacing_entities = "very wide space,narrow space,regular space,invisible separator";
System.out.println("Space String :"+ spacing_entities);
byte[] byteArray = 
    // spacing_entities.getBytes( Charset.forName("UTF-8") );
    // Charset.forName("UTF-8").encode( s2 ).array();
    {-30, -128, -125, 44, -30, -128, -126, 44, 32, 44, -62, -96};
System.out.println("Bytes:"+ Arrays.toString( byteArray ) );
try {
    System.out.format("Bytes to String[%S] \n ", new String(byteArray, "UTF-8"));
} catch (UnsupportedEncodingException e) {
    e.printStackTrace();
}
_
  • Transl ASCII translittérations de la chaîne Unicode pour Java. unidecode

    _String initials = Unidecode.decode( s2 );
    _
  • ➩ en utilisant Guava : Google Core Libraries for Java .

    _String replaceFrom = CharMatcher.WHITESPACE.replaceFrom( s2, " " );
    _

    Pour le codage d'URL pour l'espace , utilisez la bibliothèque virtuelle de Guava.

    _String encodedString = UrlEscapers.urlFragmentEscaper().escape(inputString);
    _
  • Pour résoudre ce problème, utilisez String.replaceAll() avec un RegularExpression .

    _// \p{Z} or \p{Separator}: any kind of whitespace or invisible separator.
    s2 = s2.replaceAll("\\p{Zs}", " ");
    
    
    s2 = s2.replaceAll("[^\\p{ASCII}]", " ");
    s2 = s2.replaceAll(" ", " ");
    _
  • ➩ Utilisation de Java.text.Normalizer.Form . Cette énumération fournit les constantes des quatre formulaires de normalisation Unicode décrits dans la section Formulaire de normalisation Unicode, annexe 15 - Formulaires de normalisation Unicode, ainsi que deux méthodes pour y accéder.

    enter image description here

    _s2 = Normalizer.normalize(s2, Normalizer.Form.NFKC);
    _

Test de chaîne et de sorties sur différentes approches comme ➩ Unidecode, Normalizer, StringUtils .

_String strUni = "Tĥïŝ ĩš â fůňķŷ Šťŕĭńġ Æ,Ø,Ð,ß";

// This is a funky String AE,O,D,ss
String initials = Unidecode.decode( strUni );

// Following Produce this o/p: Tĥïŝ ĩš â fůňķŷ Šťŕĭńġ Æ,Ø,Ð,ß
String temp = Normalizer.normalize(strUni, Normalizer.Form.NFD);
Pattern pattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
temp = pattern.matcher(temp).replaceAll("");

String input = org.Apache.commons.lang3.StringUtils.stripAccents( strUni );
_

Utiliser Unidecode est le best choice , Mon code final est présenté ci-dessous.

_public static void main(String[] args) {
    String s1 = "My Sample Space Data", s2 = "My Sample Space Data";
    String initials = Unidecode.decode( s2 );
    if( s1.equals(s2)) { //[ , ] %A0 - %2C - %20 « http://www.ascii-code.com/
        System.out.println("Equal Unicode Strings");
    } else if( s1.equals( initials ) ) {
        System.out.println("Equal Non Unicode Strings");
    } else {
        System.out.println("Not Equal");
    }

}
_
2
Yash

La solution de @David Conrad est la plus rapide que j'ai essayée avec le normalisateur, mais il y a un bogue. Il supprime les caractères qui ne sont pas des accents, par exemple les caractères chinois et d'autres lettres comme æ, sont tous supprimés. Les caractères que nous voulons effacer sont des marques non-espacées, des caractères qui ne prennent pas de largeur supplémentaire dans la chaîne finale. Ces caractères de largeur zéro finissent par être combinés dans un autre caractère. Si vous pouvez les voir isolés en tant que caractère, par exemple comme ceci `, je suppose que cela est combiné au caractère espace.

public static String flattenToAscii(String string) {
    char[] out = new char[string.length()];
    String norm = Normalizer.normalize(string, Normalizer.Form.NFD);

    int j = 0;
    for (int i = 0, n = norm.length(); i < n; ++i) {
        char c = norm.charAt(i);
        int type = Character.getType(c);

        //Log.d(TAG,""+c);
        //by Ricardo, modified the character check for accents, ref: http://stackoverflow.com/a/5697575/689223
        if (type != Character.NON_SPACING_MARK){
            out[j] = c;
            j++;
        }
    }
    //Log.d(TAG,"normalized string:"+norm+"/"+new String(out));
    return new String(out);
}
2
Ricardo Freitas

L’un des meilleurs moyens en utilisant regex et Normalizer si vous n’avez pas de bibliothèque est:

    public String flattenToAscii(String s) {
                if(s == null || s.trim().length() == 0)
                        return "";
                return Normalizer.normalize(s, Normalizer.Form.NFD).replaceAll("[\u0300-\u036F]", "");
}

Ceci est plus efficace que replaceAll ("[^\p {ASCII}]", "")) et si vous n'avez pas besoin de signes diacritiques (comme dans votre exemple).

Sinon, vous devez utiliser le modèle p {ASCII}.

Cordialement.

1
Zhar

Au cas où quelqu'un voudrait faire cela en kotlin, ce code fonctionne à merveille. Pour éviter les incohérences, j'utilise aussi .toUpperCase et Trim (). alors je lance cette fonction:

   fun stripAccents(s: String):String{

   if (s == null) {
      return "";
   }

val chars: CharArray = s.toCharArray()

var sb = StringBuilder(s)
var cont: Int = 0

while (chars.size > cont) {
    var c: kotlin.Char
    c = chars[cont]
    var c2:String = c.toString()
   //these are my needs, in case you need to convert other accents just Add new entries aqui
    c2 = c2.replace("Ã", "A")
    c2 = c2.replace("Õ", "O")
    c2 = c2.replace("Ç", "C")
    c2 = c2.replace("Á", "A")
    c2 = c2.replace("Ó", "O")
    c2 = c2.replace("Ê", "E")
    c2 = c2.replace("É", "E")
    c2 = c2.replace("Ú", "U")

    c = c2.single()
    sb.setCharAt(cont, c)
    cont++

}

return sb.toString()

}

pour utiliser ces amusements, utilisez le code comme ceci:

     var str: String
     str = editText.text.toString() //get the text from EditText
     str = str.toUpperCase().trim()

     str = stripAccents(str) //call the function
0
Thiago Silva