إزالة العلامات التشوئية (ń ǹ ǹ ṅ ņ ṇ ṋ ṋ ṉ ɲ ɲ ᶇ ᶇ ᶇ ɳ ɳ) من Unicode chars

StackOverflow https://stackoverflow.com/questions/1453171

سؤال

أنا أبحث في خوارزمية يمكنها تعيين الخريطة بين الشخصيات مع التشكيل (تيلد, الحيلية, قائلة, umlaut, كارون) شخصية "البسيطة" الخاصة بهم.

علي سبيل المثال:

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

إلخ.

  1. أريد أن أفعل ذلك في جافا، على الرغم من أنني أظن أنه يجب أن يكون شيئا يونيك-y ويجب أن تكون قابلة للقيام بشكل معقول بسهولة بأي لغة.

  2. الغرض: للسماح للبحث بسهولة عن الكلمات ذات العلامات التشكيلية. على سبيل المثال، إذا كان لدي قاعدة بيانات لاعبين تنس، يتم إدخال Björn_borg، سأبقي أيضا Bjorn_borg حتى أتمكن من العثور عليه إذا دخل شخص ما Björn وليس Björn.

هل كانت مفيدة؟

المحلول

لقد فعلت هذا مؤخرا في جاوة:

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.

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 للفرز على ذلك PRIMARY الاختلافات في الشخصيات. مع ذلك، قم بإنشاء CollationKey لكل سلسلة. إذا كان كل الكود الخاص بك في Java، يمكنك استخدام CollationKey مباشرة. إذا كنت بحاجة إلى تخزين المفاتيح في قاعدة بيانات أو أي نوع آخر من الفهرس، يمكنك تحويله إلى صفيف بايت.

هذه الفصول تستخدم Unicode Standard. حالة قابلة للطي لتحديد الأحرف التي تعادلها، ودعم مختلف تقسيم استراتيجيات.

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 تخفف من الاضطرار إلى تتبع كل هذه القواعد واحتفظ بها حتى الآن.

انها جزء من اباتشي المشاع Lang. اعتبارا من الاصدار. 3.1.

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

عائدات An

يمكنك استخدام الطبقة الطبيعية من java.text:

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

ولكن لا يزال هناك بعض العمل الذي يجب القيام به، لأن Java يجعل أشياء غريبة من أحرف Unicode غير قابلة للرقابة (لا تتجاهلها، ولا رمي استثناء). لكنني أعتقد أنك يمكن أن تستخدم ذلك كنقطة انطلاق.

هناك مشروع تقرير على الطابع القابل للطي على موقع Unicode الذي يحتوي على الكثير من المواد ذات الصلة. انظر على وجه التحديد القسم 4.1. "خوارزمية قابلة للطي".

هنا المناقشة والتنفيذ إزالة علامة التشويم باستخدام بيرل.

هذه الأسئلة الحالية ترتبط كذلك:

يرجى ملاحظة أنه ليس كل هذه العلامات فقط "علامات" على بعض الأحرف "العادية"، والتي يمكنك إزالتها دون تغيير المعنى.

في السويدية، å ä و Ö هي أحرف من الدرجة الأولى الحقيقية والمناسبة، وليس بعض "البديل" من بعض الطابع الآخر. أنها تبدو مختلفة عن جميع الأحرف الأخرى، أنها فرز مختلفة، وأنها تجعل الكلمات تتغير معنى ("Mätt" و "مات" هي كلمات مختلفة).

Unicode لديه أحرف غير محددة غير رسمية (وهي أحرف مركبة) ويمكن تحويل السلسلة بحيث يتم فصل الشخصية والشاحنات. بعد ذلك، يمكنك فقط إزالة Diattricts من السلسلة وأنت تم القيام بها أساسا.

لمزيد من المعلومات حول التطبيع، التحلل والمعادلة، راجع معيار يونيكود في Unicode الصفحة الرئيسية.

ومع ذلك، كيف يمكنك تحقيق ذلك بالفعل هذا يعتمد على الإطار / OS / ... أنت تعمل على. إذا كنت تستخدم .NET، يمكنك استخدام string.normalize. طريقة قبول system.text.normalizationform. تعداد.

ستكون أسهل طريقة (بالنسبة لي) هي ببساطة الحفاظ على صفيف تعيين متفرق يغير ببساطة نقاط رمز Unicode الخاص بك في سلاسل عرضة.

مثل:

start    = 0x00C0
size     = 23
mappings = {
    "A","A","A","A","A","A","AE","C",
    "E","E","E","E","I","I","I", "I",
    "D","N","O","O","O","O","O"
}
start    = 0x00D8
size     = 6
mappings = {
    "O","U","U","U","U","Y"
}
start    = 0x00E0
size     = 23
mappings = {
    "a","a","a","a","a","a","ae","c",
    "e","e","e","e","i","i","i", "i",
    "d","n","o","o","o","o","o"
}
start    = 0x00F8
size     = 6
mappings = {
    "o","u","u","u","u","y"
}
: : :

استخدام أ متناثر سيسمح لك Array بالتمثيل بكفاءة بدائل حتى عندما تكون في أقسام متباعدة على نطاق واسع من جدول Unicode. ستتيح بدائل السلسلة تسلسل تعسفيا لاستبدال درعك (مثل æ Grapheme تصبح ae).

هذه هي إجابة غير مرجعية للغة، إذا كان لديك لغة محددة في الاعتبار، فستكون هناك طرق أفضل (على الرغم من أنها ستسقط كلها من المرجح أن تؤدي إلى هذا في أدنى مستويات على أي حال).

شيء يجب مراعاته: إذا ذهبت في محاولة محاولة الحصول على "ترجمة" واحدة من كل كلمة، فقد تفوتك بعض البائعات الممكنة.

على سبيل المثال، باللغة الألمانية، عند استبدال "مجموعة S"، قد يستخدم بعض الأشخاص "B"، بينما قد يستخدم الآخرون "SS". أو، استبدال Umlauted O مع "O" أو "OE". أي حل الذي توصلت إليه، من الناحية المثالية، أعتقد أنه ينبغي أن يشمل كليهما.

في Windows و .NET، أنا فقط تحويل استخدام ترميز السلسلة. بهذه الطريقة أتجنب التعيين اليدوي والترميز.

حاول أن تلعب مع ترميز السلسلة.

في حالة اللغة الألمانية، لم يكن الأمر مطلوبا لإزالة دروعيا من Umlauts (Ä، Ö، ü). بدلا من ذلك، يتم استبدالها بمجموعة من حرفين (AE، OE، UE) على سبيل المثال، يجب كتابة Björn كجزء إن البورنر (وليس Bjorn) للحصول على نطق صحيح.

لذلك، سيكون لدي رسم خرائط صغار، حيث يمكنك تحديد قاعدة البديل بشكل فردي لكل مجموعة أحرف خاصة.

للمرجع المستقبلي، هنا طريقة تمديد C # التي تزيل اللجه.

public static class StringExtensions
{
    public static string RemoveDiacritics(this string str)
    {
        return new string(
            str.Normalize(NormalizationForm.FormD)
                .Where(c => CharUnicodeInfo.GetUnicodeCategory(c) != 
                            UnicodeCategory.NonSpacingMark)
                .ToArray());
    }
}
static void Main()
{
    var input = "ŃŅŇ ÀÁÂÃÄÅ ŢŤţť Ĥĥ àáâãäå ńņň";
    var output = input.RemoveDiacritics();
    Debug.Assert(output == "NNN AAAAAA TTtt Hh aaaaaa nnn");
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top