web-dev-qa-db-ja.com

アクセントを取り除き、文字列全体を通常の文字に変換する方法はありますか?

String.replaceAll()メソッドを使用して文字を1つずつ置き換える以外に、アクセントを取り除き、それらの文字を規則的にするためのより良い方法はありますか?例:

入力:orčpžsíáýd

出力:orcpzsiayd

ロシア語のアルファベットや中国語のアルファベットのようなアクセントのあるすべての文字を含める必要はありません。

222
Martin

Java.text.Normalizer を使用してこれを処理します。

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

これにより、すべてのアクセント記号が文字から分離されます。次に、各文字を文字と比較し、そうでない文字を捨てるだけです。

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

テキストがユニコードの場合、代わりにこれを使用する必要があります。

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

Unicodeの場合、\\P{M}は基本グリフに一致し、\\p{M}(小文字)は各アクセントに一致します。

ポインタを提供してくれたGarretWilsonと、すばらしいUnicodeガイドを提供してくれた regular-expressions.info に感謝します。

348
Erick Robertson

2011年現在、Apache Commonsを使用できます StringUtils.stripAccents(input) (3.0以降):

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

注:

受け入れられた答え(Erick Robertsonの)は、Øまたは)には機能しません。 Apache Commons 3.5はØでも機能しませんが、Łでは機能します。 Øのウィキペディアの記事 を読んだ後、「O」に置き換える必要があるかどうかわかりません。これは、ノルウェー語とデンマーク語の別個の文字で、「z」の後にアルファベット順です。これは、「ストリップアクセント」アプローチの制限の良い例です。

114
DavidS

@ virgo47による解決策は非常に高速ですが、おおよそのものです。受け入れられた答えは、ノーマライザーと正規表現を使用しています。正規表現なしですべての非ASCII文字を削除できるので、正規化と正規表現のどちらの時間がかかったのだろうと思いました。

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();
    }
}

Char []に書き込み、toCharArray()を呼び出さないことで、わずかな追加の高速化を実現できますが、コードの明瞭さが低下することでメリットがあるかどうかはわかりません。

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

このバリエーションには、ノーマライザーを使用するものの正確性と、テーブルを使用するものの速度の一部という利点があります。私のマシンでは、これは受け入れられる回答の約4倍速く、@ virgo47の6.6倍から7倍遅い(受け入れられる答えは、私のマシンの@ virgo47の約26倍遅い)。

49
David Conrad

編集:Java <6にとらわれておらず、速度が重要でない場合、および/または変換テーブルが制限を超えている場合は、Davidの回答を使用してください。ポイントは、ループ内で変換テーブルの代わりにNormalizer(Java 6で導入)を使用することです。

これは「完璧な」解決策ではありませんが、範囲(この場合はLatin1,2)を知っていればうまく機能し、Java 6(実際の問​​題ではありません)の前に機能し、最も高速です推奨バージョン(問題になる場合もあれば、そうでない場合もあります):

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

32ビットJDKを使用したHWでのテストでは、これによりàèéľšťč89FDČからaeelstc89FDCへの変換が100ミリ秒で100万回実行されるのに対し、ノーマライザーの方法では3.7秒(37倍遅い)になります。パフォーマンスに関するニーズがあり、入力範囲がわかっている場合は、これが適している可能性があります。

楽しい :-)

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

私のために働いた。上記のスニペットの出力は、私が望んでいた「aee」を提供しますが、

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

置換を行いませんでした。

20
Nico

言語によっては、アクセント(文字の音を変える)とは見なされない場合がありますが、発音区別符号

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

「ボスニア語とクロアチア語には記号č、ć、đ、š、žがあります。これらは別々の文字と見なされ、辞書や単語がアルファベット順にリストされている他のコンテキストにリストされています。」

それらを削除すると、本質的にWordの意味が変わったり、文字がまったく異なるものに変わったりする可能性があります。

6
NinjaCat

Junidecode をお勧めします。 「Ł」と「Ø」だけでなく、中国語などの他のアルファベットからラテンアルファベットへの転写にも適しています。

3
OlgaMaciaszek

文字列の等価性チェックに関連する同じ問題に直面しました。比較する文字列の1つに ASCII文字コード128-255 があります。

つまり、改行なしスペース-[Hex-A0]スペース[Hex-20]。 HTML上に改行なしスペースを表示します。私は次の spacing entities を使用しました。その文字とそのバイトは&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()));

バイト単位の出力:

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]

さまざまなスペースとそのバイトコードに以下のコードを使用します。 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();
}
  • Java ASCII JavaのUnicode文字列の音訳。 unidecode

    String initials = Unidecode.decode( s2 );
    
  • Guava を使用:Google Core Libraries for Java

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

    URLエンコードの場合 スペース用 Guava laibraryを使用します。

    String encodedString = UrlEscapers.urlFragmentEscaper().escape(inputString);
    
  • thisこの問題を克服するために、いくつかの RegularExpressionString.replaceAll()を使用しました。

    // \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(" ", " ");
    
  • Java.text.Normalizer.Form を使用します。この列挙型は、 nicode Standard Annex#15 — Unicode正規化形式とそれらにアクセスする2つの方法で説明されている4つのUnicode正規化形式の定数を提供します。

    enter image description here

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

Testing 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 );

Unidecodeを使用すると、 best choice 、以下に示す最終コードになります。

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

@David Conradソリューションは、ノーマライザーを使用して試した最速ですが、バグがあります。基本的にアクセントではない文字を削除します。たとえば、中国語の文字やæのような他の文字はすべて削除されます。除去したい文字は非スペーシングマークであり、最終文字列で余分な幅を占有しない文字です。これらのゼロ幅の文字は、基本的に他の文字に結合されます。たとえば、この `のように、文字として分離されているのを見ることができれば、スペース文字と組み合わされていると思います。

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

最良の方法の1つregexとNormalizerを使用ライブラリがない場合:

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

これは、replaceAll( "[^\p {ASCII}]"、 ""))および発音区別記号が不要な場合(例のように)よりも効率的です。

それ以外の場合は、p {ASCII}パターンを使用する必要があります。

よろしく。

1
Zhar

誰かがkotlinでこれを行うのに苦労している場合、このコードは魅力のように機能します。矛盾を避けるために、.toUpperCaseとTrim()も使用します。次に、この関数をキャストします:

   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()

}

これらの楽しみを使用するには、次のようにコードをキャストします。

     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