[java] 악센트를 제거하고 전체 문자열을 일반 문자로 변환하는 방법이 있습니까?

방법을 사용 String.replaceAll()하고 문자를 하나씩 바꾸는 것 외에 악센트를 없애고 문자를 규칙적으로 만드는 더 좋은 방법이 있습니까? 예:

입력: orčpžsíáýd

산출: orcpzsiayd

러시아어 알파벳이나 중국어와 같은 악센트가있는 모든 문자를 포함 할 필요는 없습니다.



답변

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

유니 코드의 \\P{M}경우 기본 글리프와 \\p{M}일치 하고 (소문자)는 각 액센트와 일치합니다.

포인터와 대한 GarretWilson 덕분에 regular-expressions.info 큰 유니 코드 가이드.


답변

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 ‘s)은 ​​Ø 또는 Ł에서 작동하지 않습니다. Apache Commons 3.5는 Ø에서도 작동하지 않지만 Ł에서는 작동합니다. Ø대한 Wikipedia 기사를 읽은 후에 는 “O”로 대체해야할지 모르겠습니다. “z”뒤에 알파벳순으로 노르웨이어와 덴마크어로 별도의 문자가 있습니다. “스트립 악센트”접근 방식의 한계에 대한 좋은 예입니다.


답변

@ 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 배 느립니다).


답변

편집 : 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에서 테스트 한 결과 이것이 ~ 100ms에서 àèéľšťč89FDČ에서 aeelstc89FDC로 백만 번 변환을 수행하는 반면 Normalizer 방식은 3.7 초 (37x 느리게) 만듭니다. 요구 사항이 성능에 근접하고 입력 범위를 알고있는 경우이 기능이 적합 할 수 있습니다.

즐겨 🙂


답변

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

나를 위해 일했다. 위의 스 니펫 출력은 “aee”를 제공합니다.

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

대체하지 않았다.


답변

언어에 따라 악센트 (문자의 소리를 변경하는)로 간주되지 않지만 발음 구별 부호로 간주 될 수 있습니다.

https://ko.wikipedia.org/wiki/Diacritic#Languages_with_letters_include_diacritics

“보스니아 어와 크로아티아어는 č, ć, đ, š 및 ž의 기호를가집니다.이 기호는 별도의 문자로 간주되며 사전과 기타 문맥에서 단어가 알파벳 순서로 나열됩니다.”

그것들을 제거하는 것은 본질적으로 단어의 의미를 바꾸거나 글자를 완전히 다른 단어로 바꾸는 것일 수 있습니다.


답변

Strings equality check와 관련하여 동일한 문제에 직면했습니다. 비교 문자열 중 하나에는
ASCII 문자 코드 128-255가 있습니다.

즉, 비 분리 공간-[Hex-A0] Space [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,, 3283, 97, 109, 112, 108, 101,, 3283, 112, 97, 99, 101 32,, 68, 97, 116, 97]
S2 : [77, 121 -30, -128, -125,, 83, 97, 109, 112, 108, 101,, -30, -128, -12583, 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();
}
  • for Java 용 유니 코드 문자열의 ASCII 음역. 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);
  • String.replaceAll()some 일부와 함께 사용되는이 문제를 극복하기 위해 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(" ", " ");
  • java java.text.Normalizer.Form 사용 . 이 열거 형은 유니 코드 표준 부록 # 15- 유니 코드 정규화 양식에 설명 된 네 가지 유니 코드 정규화 형식과 이에 액세스하는 두 가지 방법의 상수를 제공 합니다.

    여기에 이미지 설명을 입력하십시오

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

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

}