Est-il un moyen de se débarrasser d'accents et de convertir une chaîne entière pour les lettres ordinaires?

y a-t-il une meilleure façon de se débarrasser des accents et de rendre ces lettres régulières en dehors de l'utilisation de la méthode String.replaceAll() et de remplacer les lettres une par une? Exemple:

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

sortie: orcpzsiayd

il n'a pas besoin d'inclure toutes les lettres avec des accents comme l'alphabet russe ou le Chinois.

197
demandé sur Erick Robertson 2010-07-24 00:33:27

9 réponses

utilisez java.text.Normalizer pour gérer cela pour vous.

string = Normalizer.normalize(string, Normalizer.Form.NFD);

cela séparera toutes les marques d'accent des caractères. Ensuite, vous avez juste besoin de comparer chaque caractère par rapport à être une lettre et jeter ceux qui ne sont pas.

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

si votre texte est en unicode, vous devez utiliser ceci à la place:

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

pour unicode, \P{M} correspond à la base glyphe et \p{M} (minuscule) correspond à chaque accent.

merci à GarretWilson pour le pointeur et regular-expressions.info pour le grand guide unicode.

316
répondu Erick Robertson 2014-10-12 15:44:57

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

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

Note:

Les accepté de répondre (Erick Robertson) ne fonctionne pas pour Ø ou Ł. Apache Commons 3.5 ne fonctionne pas pour Ø non plus, mais il fonctionne pour Ł. Après avoir lu L'article de Wikipedia pour Ø , Je ne suis pas sûr qu'il devrait être remplacé par "O": c'est une lettre séparée en norvégien et en Danois, alphabétisés après "z". C'est un bon exemple des limites de l'approche "strip accents".

102
répondu DavidS 2017-03-15 17:54:53

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 supprimer tous les caractères non-ASCII peut être fait sans un 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 vitesses supplémentaires peuvent être obtenues en écrivant dans un char[] et ne pas appeler tocharray (), bien que je ne suis pas sûr que la diminution de la clarté du code mérite it:

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 normalisateur et une partie de la vitesse de celui qui utilise une table. Sur ma machine, celle-ci est environ 4x plus rapide que la réponse acceptée, et 6,6 x à 7x plus lente que celle de @virgo47 (la réponse acceptée est environ 26x plus lente que celle de @virgo47 sur ma machine).

46
répondu David Conrad 2013-03-03 22:09:43

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 restrictive, utilisez réponse de David. Le point est d'utiliser Normalizer (introduit en Java 6) au lieu de la table de traduction à l'intérieur de la boucle.

bien que ce ne soit pas une solution "parfaite", cela fonctionne bien quand vous connaissez la gamme (dans notre cas Latin1, 2), travaillé avant Java 6 (pas un vrai problème cependant) et est beaucoup plus rapide que la version la plus suggérée (Peut être ou peut ne pas être une question):

    /**
 * 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);
}

Tests sur mon HW avec 32bit JDK montrent que cela effectue la conversion de àèéľšťč89FDČ à aeelstc89FDC 1 million de fois en ~100ms tandis que Normalizer way le rend en 3.7 s (37x plus lent). Dans le cas où vos besoins sont autour de la performance et vous connaissez la gamme d'entrées, cela peut être pour vous.

Profiter :-)

25
répondu virgo47 2015-05-28 07:22:08
System.out.println(Normalizer.normalize("àèé", Normalizer.Form.NFD).replaceAll("\p{InCombiningDiacriticalMarks}+", ""));

travaillait pour moi. La sortie de l'extrait ci-dessus donne "aee" qui est ce que je voulais, mais

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

n'a pas fait de substitution.

19
répondu Nico 2010-11-19 14:02:47

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

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

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

les enlever pourrait changer le sens du mot ou changer les lettres en des lettres complètement différentes.

6
répondu NinjaCat 2016-12-03 02:15:39

@David Conrad solution est le plus rapide que j'ai essayé d'utiliser le normalisateur, mais il a un bug. Il s'agit essentiellement de bandes de caractères qui ne sont pas des accents, par exemple caractères chinois et d'autres lettres comme æ, sont tous dépouillés. Les caractères que nous voulons bande sont non espacement des marques, des personnages qui ne prennent largeur supplémentaire dans la chaîne finale. Ces caractères de largeur zéro finissent essentiellement par être combinés dans un autre caractère. Si vous pouvez les voir isolés comme un caractère, par exemple comme ça, je pense que c'est combiné avec le personnage de l'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
répondu Ricardo Freitas 2015-07-09 04:31:41

j'ai fait face à la même question liée aux chaînes de vérification de l'égalité, l'un de la chaîne de comparaison a code de caractère ASCII 128 à 255 .

c'est à dire, de Non-rupture de l'espace - [Hex - A0] Espace [Hex - 20]. Pour afficher L'espace sans rupture sur HTML. J'ai utilisé ce qui suit: spacing entities . Leur caractère et ses octets sont comme &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]

utiliser le code ci-dessous pour différents espaces et leurs Bytes-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();
}
  • ➩ translittérations ASCII D'Unicode string pour Java. unidecode

    String initials = Unidecode.decode( s2 );
    
  • ➩ à l'aide de Guava : Google Base Libraries for Java .

    String replaceFrom = CharMatcher.WHITESPACE.replaceFrom( s2, " " );
    

    pour URL Encoder pour l'espace utiliser Guava laibrary.

    String encodedString = UrlEscapers.urlFragmentEscaper().escape(inputString);
    
  • pour surmonter ce problème utilisé String.replaceAll() avec certains 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.texte.Normalisateur.Formulaire . Cet enum fournit les constantes des quatre formes de normalisation Unicode qui sont décrites dans Unicode Standard Annex #15 - formulaires de normalisation Unicode et deux méthodes pour y accéder.

    enter image description here

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

les Tests de Chaîne et de sorties sur les différentes approches comme ➩ Unidecode, Normalisateur, 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 );

à l'Aide de Unidecode est le best choice , mon Code final 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
répondu Yash 2017-09-11 14:58:06

je suggère Junidecode . Il ne traitera pas seulement "Ł" et "Ø", mais il fonctionne aussi bien pour la transcription d'autres alphabets, tels que le chinois, en alphabet Latin.

1
répondu OlgaMaciaszek 2017-11-13 15:11:36