[java] 유니 코드 문자에서 분음 부호 제거 (ń ǹ ň ñ ṅ ņ ṇ ṋ ṉ ̈ ɲ ƞ ᶇ ɳ ȵ)

분음 부호 ( 틸드 , 곡절 , 캐럿 , 움라우트 , 카론 )와 “단순”문자 사이를 매핑 할 수있는 알고리즘을 찾고 있습니다 .

예를 들면 :

ń  ǹ  ň  ñ  ṅ  ņ  ṇ  ṋ  ṉ  ̈  ɲ  ƞ ᶇ ɳ ȵ  --> n
á --> a
ä --> a
ấ --> a
ṏ --> o

기타.

  1. 나는 이것이 유니 코드 형식이어야하고 어떤 언어로도 합리적으로 쉽게 할 수 있어야한다고 생각하지만 자바로 이것을하고 싶다.

  2. 목적 : 분음 부호가있는 단어를 쉽게 검색 할 수 있습니다. 예를 들어, 테니스 선수 데이터베이스가 있고 Björn_Borg가 입력 된 경우 Bjorn_Borg도 유지하므로 Björn이 아닌 누군가가 Bjorn에 입력하면 찾을 수 있습니다.



답변

최근 Java에서이 작업을 수행했습니다.

public static final Pattern DIACRITICS_AND_FRIENDS
    = Pattern.compile("[\\p{InCombiningDiacriticalMarks}\\p{IsLm}\\p{IsSk}]+");

private static String stripDiacritics(String str) {
    str = Normalizer.normalize(str, Normalizer.Form.NFD);
    str = DIACRITICS_AND_FRIENDS.matcher(str).replaceAll("");
    return str;
}

지정한대로 수행됩니다.

stripDiacritics("Björn")  = Bjorn

그러나 ł문자가 분음 부호가 아니기 때문에 예를 들어 Białystok에서는 실패합니다 .

완전한 문자열 단순화를 원하면 분음 부호가 아닌 특수 문자에 대해 두 번째 정리 라운드가 필요합니다. 이 맵은 고객 이름에 나타나는 가장 일반적인 특수 문자를 포함했습니다. 완전한 목록은 아니지만 확장하는 방법에 대한 아이디어를 제공합니다. immutableMap은 google-collections의 단순한 클래스입니다.

public class StringSimplifier {
    public static final char DEFAULT_REPLACE_CHAR = '-';
    public static final String DEFAULT_REPLACE = String.valueOf(DEFAULT_REPLACE_CHAR);
    private static final ImmutableMap<String, String> NONDIACRITICS = ImmutableMap.<String, String>builder()

        //Remove crap strings with no sematics
        .put(".", "")
        .put("\"", "")
        .put("'", "")

        //Keep relevant characters as seperation
        .put(" ", DEFAULT_REPLACE)
        .put("]", DEFAULT_REPLACE)
        .put("[", DEFAULT_REPLACE)
        .put(")", DEFAULT_REPLACE)
        .put("(", DEFAULT_REPLACE)
        .put("=", DEFAULT_REPLACE)
        .put("!", DEFAULT_REPLACE)
        .put("/", DEFAULT_REPLACE)
        .put("\\", DEFAULT_REPLACE)
        .put("&", DEFAULT_REPLACE)
        .put(",", DEFAULT_REPLACE)
        .put("?", DEFAULT_REPLACE)
        .put("°", DEFAULT_REPLACE) //Remove ?? is diacritic?
        .put("|", DEFAULT_REPLACE)
        .put("<", DEFAULT_REPLACE)
        .put(">", DEFAULT_REPLACE)
        .put(";", DEFAULT_REPLACE)
        .put(":", DEFAULT_REPLACE)
        .put("_", DEFAULT_REPLACE)
        .put("#", DEFAULT_REPLACE)
        .put("~", DEFAULT_REPLACE)
        .put("+", DEFAULT_REPLACE)
        .put("*", DEFAULT_REPLACE)

        //Replace non-diacritics as their equivalent characters
        .put("\u0141", "l") // BiaLystock
        .put("\u0142", "l") // Bialystock
        .put("ß", "ss")
        .put("æ", "ae")
        .put("ø", "o")
        .put("©", "c")
        .put("\u00D0", "d") // All Ð ð from http://de.wikipedia.org/wiki/%C3%90
        .put("\u00F0", "d")
        .put("\u0110", "d")
        .put("\u0111", "d")
        .put("\u0189", "d")
        .put("\u0256", "d")
        .put("\u00DE", "th") // thorn Þ
        .put("\u00FE", "th") // thorn þ
        .build();


    public static String simplifiedString(String orig) {
        String str = orig;
        if (str == null) {
            return null;
        }
        str = stripDiacritics(str);
        str = stripNonDiacritics(str);
        if (str.length() == 0) {
            // Ugly special case to work around non-existing empty strings
            // in Oracle. Store original crapstring as simplified.
            // It would return an empty string if Oracle could store it.
            return orig;
        }
        return str.toLowerCase();
    }

    private static String stripNonDiacritics(String orig) {
        StringBuffer ret = new StringBuffer();
        String lastchar = null;
        for (int i = 0; i < orig.length(); i++) {
            String source = orig.substring(i, i + 1);
            String replace = NONDIACRITICS.get(source);
            String toReplace = replace == null ? String.valueOf(source) : replace;
            if (DEFAULT_REPLACE.equals(lastchar) && DEFAULT_REPLACE.equals(toReplace)) {
                toReplace = "";
            } else {
                lastchar = toReplace;
            }
            ret.append(toReplace);
        }
        if (ret.length() > 0 && DEFAULT_REPLACE_CHAR == ret.charAt(ret.length() - 1)) {
            ret.deleteCharAt(ret.length() - 1);
        }
        return ret.toString();
    }

    /*
    Special regular expression character ranges relevant for simplification -> see http://docstore.mik.ua/orelly/perl/prog3/ch05_04.htm
    InCombiningDiacriticalMarks: special marks that are part of "normal" ä, ö, î etc..
        IsSk: Symbol, Modifier see http://www.fileformat.info/info/unicode/category/Sk/list.htm
        IsLm: Letter, Modifier see http://www.fileformat.info/info/unicode/category/Lm/list.htm
     */
    public static final Pattern DIACRITICS_AND_FRIENDS
        = Pattern.compile("[\\p{InCombiningDiacriticalMarks}\\p{IsLm}\\p{IsSk}]+");


    private static String stripDiacritics(String str) {
        str = Normalizer.normalize(str, Normalizer.Form.NFD);
        str = DIACRITICS_AND_FRIENDS.matcher(str).replaceAll("");
        return str;
    }
}


답변

핵심 java.text 패키지는이 사용 사례 (분음 부호, 대소 문자 등을 고려하지 않고 문자열 일치)를 해결하도록 설계되었습니다.

문자 차이 Collator를 정렬하도록 a 를 구성합니다 PRIMARY. 이를 통해 CollationKey각 문자열에 대해 생성하십시오 . 모든 코드가 Java로되어있는 경우 CollationKey직접 사용할 수 있습니다. 데이터베이스 나 다른 종류의 인덱스에 키를 저장해야하는 경우 이를 바이트 배열로 변환 할 수 있습니다 .

이러한 클래스는 유니 코드 표준을 사용합니다. 케이스 폴딩 데이터를 동일한 문자를 결정하고 다양한 분해 전략을 지원 합니다.

Collator c = Collator.getInstance();
c.setStrength(Collator.PRIMARY);
Map<CollationKey, String> dictionary = new TreeMap<CollationKey, String>();
dictionary.put(c.getCollationKey("Björn"), "Björn");
...
CollationKey query = c.getCollationKey("bjorn");
System.out.println(dictionary.get(query)); // --> "Björn"

콜 레이터는 로케일에 따라 다릅니다. 이것은 “알파벳 순서”가 로케일마다 다르기 때문입니다 (스페인어의 경우처럼 시간이 지남에 따라). Collator클래스는이 규칙을 모두 추적하고 최신을 유지하는 데에서 당신을 해소.


답변

이 버전 은 Apache Commons Lang 의 일부입니다 . 3.1.

org.apache.commons.lang3.StringUtils.stripAccents("Añ");

보고 An


답변

다음에서 Normalizer 클래스 를 사용할 수 있습니다 java.text.

System.out.println(new String(Normalizer.normalize("ń ǹ ň ñ ṅ ņ ṇ ṋ", Normalizer.Form.NFKD).getBytes("ascii"), "ascii"));

그러나 Java는 변환 할 수없는 유니 코드 문자로 이상한 일을 만들기 때문에 여전히해야 할 일이 있습니다 (이 문자를 무시하지 않고 예외를 던지지 않습니다). 그러나 나는 그것을 시작점으로 사용할 수 있다고 생각합니다.


답변

많은 관련 자료가있는 유니 코드 웹 사이트에 문자 접기에 대한 초안 보고서 가 있습니다 . 구체적으로 섹션 4.1을 참조하십시오. “폴딩 알고리즘”.

다음은 Perl을 사용한 분음 부호 제거에 대한 논의 및 구현 입니다.

이러한 기존 SO 질문은 관련이 있습니다.


답변

이러한 모든 표시가 의미를 변경하지 않고 제거 할 수있는 일부 “일반”문자의 “표시”는 아닙니다.

스웨덴어에서 å ä와 ö는 다른 문자의 일부 “변형”이 아니라 참되고 적절한 일류 문자입니다. 그들은 다른 모든 문자들과 다르게 들리고, 다르게 분류되며, 단어의 의미를 변경합니다 ( “mätt”와 “matt”는 서로 다른 두 단어입니다).


답변

유니 코드에는 특정 통어 문자 (복합 문자)가 있으며 문자와 통어가 분리되도록 문자열을 변환 할 수 있습니다. 그런 다음 문자열에서 구절을 제거하면 기본적으로 완료됩니다.

정규화, 분해 및 동등성에 대한 자세한 내용은 유니 코드 홈 페이지 에서 유니 코드 표준을 참조하십시오 .

그러나 실제로이를 달성하는 방법은 작업중인 프레임 워크 / OS / …에 따라 다릅니다. .NET을 사용하는 경우 System.Text.NormalizationForm 열거를 허용하는 String.Normalize 메서드를 사용할 수 있습니다 .