Can.NET преобразование Unicode в ASCII для удаления "умных кавычек" и т. д.?

некоторые наши пользователи используют почтовые клиенты, которые не могут справиться с Unicode, даже если кодировка и т. д. правильно установлены в заголовках почты.

Я хотел бы "нормализовать" контент, который они получают. Самая большая проблема, которую мы имеем, - это пользователи копируют контент из Microsoft Word в наше веб-приложение, которое затем пересылает этот контент по электронной почте, включая фракции, смарт-кавычки и все другие расширенные символы Юникода, которые Word услужливо вставляет для вы.

Я предполагаю, что нет определенного решения для этого, но прежде чем я сяду и начну писать большие таблицы поиска, есть ли какой-то встроенный метод, который заставит меня начать?

в основном задействованы три фазы.

во-первых, зачистка акцентов от иначе-нормальных букв-решение этого здесь

This paragraph contains “smart quotes” and áccénts and ½ of the problem is fractions

идет

This paragraph contains “smart quotes” and accents and ½ of the problem is fractions

во-вторых, замена отдельных символов Юникода их ASCII эквивалент, чтобы дать:

This paragraph contains "smart quotes" and accents and ½ of the problem is fractions

это та часть, где я надеюсь, что есть решение, прежде чем я реализую свой собственный. Наконец, замена определенных символов подходящей последовательностью ASCII-от ½ до 1/2 и т. д., которая, я уверен, изначально не поддерживается какой - либо магией Unicode, но кто-то мог написать подходящую таблицу поиска, которую я могу повторно использовать.

какие идеи?

4 ответов


спасибо всем за очень полезные ответы. Я понимаю, что фактический вопрос не "как я могу преобразовать любой символ Юникода в его резервный код ASCII" - вопрос в том, " как я могу преобразовать символы Юникода мои клиенты жалуются в их ASCII fallbacks"?

другими словами-нам не нужно универсальное решение; нам нужно решение, которое будет работать 99% времени, для англоговорящих клиентов, вставляющих англоязычный контент из Word и другие сайты в нашем приложении. С этой целью я проанализировал восемь лет сообщений, отправленных через нашу систему в поисках символов, которые не представимы в кодировке ASCII, используя этот тест:

///<summary>Determine whether the supplied character is 
///using ASCII encoding.</summary>
bool IsAscii(char inputChar) {
    var ascii = new ASCIIEncoding();
    var asciiChar = (char)(ascii.GetBytes(inputChar.ToString())[0]);
    return(asciiChar == inputChar);
}

затем я прошел через результирующий набор непредставимых символов и вручную назначил соответствующую строку замены. Весь лот упакован в метод расширения, поэтому вы можете вызвать myString.Asciify (), чтобы преобразовать вашу строку в разумную ASCII-кодирование приближения.

public static class StringExtensions {
    private static readonly Dictionary<char, string> Replacements = new Dictionary<char, string>();
    /// <summary>Returns the specified string with characters not representable in ASCII codepage 437 converted to a suitable representative equivalent.  Yes, this is lossy.</summary>
    /// <param name="s">A string.</param>
    /// <returns>The supplied string, with smart quotes, fractions, accents and punctuation marks 'normalized' to ASCII equivalents.</returns>
    /// <remarks>This method is lossy. It's a bit of a hack that we use to get clean ASCII text for sending to downlevel e-mail clients.</remarks>
    public static string Asciify(this string s) {
        return (String.Join(String.Empty, s.Select(c => Asciify(c)).ToArray()));
    }

    private static string Asciify(char x) {
        return Replacements.ContainsKey(x) ? (Replacements[x]) : (x.ToString());
    }

    static StringExtensions() {
        Replacements['’'] = "'"; // 75151 occurrences
        Replacements['–'] = "-"; // 23018 occurrences
        Replacements['‘'] = "'"; // 9783 occurrences
        Replacements['”'] = "\""; // 6938 occurrences
        Replacements['“'] = "\""; // 6165 occurrences
        Replacements['…'] = "..."; // 5547 occurrences
        Replacements['£'] = "GBP"; // 3993 occurrences
        Replacements['•'] = "*"; // 2371 occurrences
        Replacements[' '] = " "; // 1529 occurrences
        Replacements['é'] = "e"; // 878 occurrences
        Replacements['ï'] = "i"; // 328 occurrences
        Replacements['´'] = "'"; // 226 occurrences
        Replacements['—'] = "-"; // 133 occurrences
        Replacements['·'] = "*"; // 132 occurrences
        Replacements['„'] = "\""; // 102 occurrences
        Replacements['€'] = "EUR"; // 95 occurrences
        Replacements['®'] = "(R)"; // 91 occurrences
        Replacements['¹'] = "(1)"; // 80 occurrences
        Replacements['«'] = "\""; // 79 occurrences
        Replacements['è'] = "e"; // 79 occurrences
        Replacements['á'] = "a"; // 55 occurrences
        Replacements['™'] = "TM"; // 54 occurrences
        Replacements['»'] = "\""; // 52 occurrences
        Replacements['ç'] = "c"; // 52 occurrences
        Replacements['½'] = "1/2"; // 48 occurrences
        Replacements['­'] = "-"; // 39 occurrences
        Replacements['°'] = " degrees "; // 33 occurrences
        Replacements['ä'] = "a"; // 33 occurrences
        Replacements['É'] = "E"; // 31 occurrences
        Replacements['‚'] = ","; // 31 occurrences
        Replacements['ü'] = "u"; // 30 occurrences
        Replacements['í'] = "i"; // 28 occurrences
        Replacements['ë'] = "e"; // 26 occurrences
        Replacements['ö'] = "o"; // 19 occurrences
        Replacements['à'] = "a"; // 19 occurrences
        Replacements['¬'] = " "; // 17 occurrences
        Replacements['ó'] = "o"; // 15 occurrences
        Replacements['â'] = "a"; // 13 occurrences
        Replacements['ñ'] = "n"; // 13 occurrences
        Replacements['ô'] = "o"; // 10 occurrences
        Replacements['¨'] = ""; // 10 occurrences
        Replacements['å'] = "a"; // 8 occurrences
        Replacements['ã'] = "a"; // 8 occurrences
        Replacements['ˆ'] = ""; // 8 occurrences
        Replacements['©'] = "(c)"; // 6 occurrences
        Replacements['Ä'] = "A"; // 6 occurrences
        Replacements['Ï'] = "I"; // 5 occurrences
        Replacements['ò'] = "o"; // 5 occurrences
        Replacements['ê'] = "e"; // 5 occurrences
        Replacements['î'] = "i"; // 5 occurrences
        Replacements['Ü'] = "U"; // 5 occurrences
        Replacements['Á'] = "A"; // 5 occurrences
        Replacements['ß'] = "ss"; // 4 occurrences
        Replacements['¾'] = "3/4"; // 4 occurrences
        Replacements['È'] = "E"; // 4 occurrences
        Replacements['¼'] = "1/4"; // 3 occurrences
        Replacements['†'] = "+"; // 3 occurrences
        Replacements['³'] = "'"; // 3 occurrences
        Replacements['²'] = "'"; // 3 occurrences
        Replacements['Ø'] = "O"; // 2 occurrences
        Replacements['¸'] = ","; // 2 occurrences
        Replacements['Ë'] = "E"; // 2 occurrences
        Replacements['ú'] = "u"; // 2 occurrences
        Replacements['Ö'] = "O"; // 2 occurrences
        Replacements['û'] = "u"; // 2 occurrences
        Replacements['Ú'] = "U"; // 2 occurrences
        Replacements['Œ'] = "Oe"; // 2 occurrences
        Replacements['º'] = "?"; // 1 occurrences
        Replacements['‰'] = "0/00"; // 1 occurrences
        Replacements['Å'] = "A"; // 1 occurrences
        Replacements['ø'] = "o"; // 1 occurrences
        Replacements['˜'] = "~"; // 1 occurrences
        Replacements['æ'] = "ae"; // 1 occurrences
        Replacements['ù'] = "u"; // 1 occurrences
        Replacements['‹'] = "<"; // 1 occurrences
        Replacements['±'] = "+/-"; // 1 occurrences
    }
}

обратите внимание, что там есть некоторые довольно странные отступления-как этот:

Replacements['³'] = "'"; // 3 occurrences
Replacements['²'] = "'"; // 3 occurrences

это потому, что у одного из наших пользователей есть программа, которая преобразует открытые/закрытые смарт-кавычки в 2 и 3 (например : он сказал 2hello3), и никто никогда не использовал их для представления экспоненции, поэтому это, вероятно, будет работать довольно хорошо для нас, но YMMV.


у меня были некоторые проблемы с этим, используя список строк, изначально построенный в Word. Я обнаружил, что с помощью простого "String".replace(current char/string, new char/string) команда отлично работает. Точный код, который я использовал, был для смарт-кавычек или, если быть точным: left", right ", left 'и right' выглядит следующим образом:

StringName = StringName.Replace(ChrW(8216), "'")     ' Replaces any left ' with a normal '
StringName = StringName.Replace(ChrW(8217), "'")     ' Replaces any right ' with a normal '
StringName = StringName.Replace(ChrW(8220), """")    ' Replace any left " with a normal "
StringName = StringName.Replace(ChrW(8221), """")    ' Replace any right " with a normal "

Я надеюсь, что это поможет любому, кто все еще имеет эту проблему!


есть какой-то встроенный метод, который начнем?

первое, что я бы попытался, это преобразовать текст в NFKD форма нормализации С нормализуют метод строк. Это предложение упоминается в ответе на вопрос, который вы связали, но я рекомендую использовать NFKD вместо NFD, потому что NFKD удалит нежелательные типографские различия (например, NBSP → space или ℂ → C).

вы также можете сделайте общие замены категория Юникода. Например, Pd можно заменить на -, Nd могут быть заменены на соответствующие 0 -9 цифра и Mn могут быть заменены пустой строкой (для удаления акцентов).

но кто-то мог написать подходящая таблица поиска, которую я могу повторно использовать.

вы можете попробовать использовать данные из программы Unidecode, или CLDR.

редактировать: Есть огромная диаграмма замещения здесь.


вы никогда не должны пытаться конвертировать Unicode в ASCII, потому что в конечном итоге у вас будет больше проблем, чем решение.

Это похоже на попытку поместить 1,114,112 кодовые точки (Unicode 6.0) всего в 128 символов.

вы думаете у вас получится?

кстати, есть много кавычек в Unicode, а не только те, которые упомянуты вами, а также если вы хотите сделать преобразование в любом случае помните, что преобразования будут зависеть от место действия.

Регистрация ОИТ - содержит наиболее полные процедуры преобразования Unicode.