【问题标题】:Why doesn't Đ get flattened to D when Removing Accents/Diacritics为什么在删除重音/变音符号时 Đ 不会变平为 D
【发布时间】:2011-01-22 16:45:39
【问题描述】:

我正在使用此方法从字符串中删除重音:

static string RemoveAccents(string input)
{
    string normalized = input.Normalize(NormalizationForm.FormKD);
    StringBuilder builder = new StringBuilder();
    foreach (char c in normalized)
    {
        if (char.GetUnicodeCategory(c) !=
        UnicodeCategory.NonSpacingMark)
        {
            builder.Append(c);
        }
    }
    return builder.ToString();
}

但是这种方法将 đ 保留为 đ 并且不会将其更改为 d,即使 d 是它的基本字符。 你可以试试这个输入字符串“æøåáââăäĺćççéęëěíîďđńňóôőöřůúűüýţ”

字母đ有什么特别之处?

【问题讨论】:

  • 你能以“\uxxxx”等形式显示重写字符串文字吗?这将使复制更容易,而不必担心组合字符等。
  • 这是土耳其语(或其他东欧字符)吗?
  • @jon 那我该怎么做呢?
  • 它在我看来就像一个 Eth,它本身就是一个字母。

标签: c# .net string diacritics


【解决方案1】:

为什么它不起作用的答案是“d 是它的基本字符”的说法是错误的。 U+0111(LATIN SMALL LETTER D WITH STROKE)具有 Unicode 类别“字母,小写”并且没有分解映射(即,它不分解为“d”后跟一个组合标记)。

"đ".Normalize(NormalizationForm.FormD) 只是简单地返回"đ",它不会被循环剥离,因为它不是非间距标记。

“ø”和其他 Unicode 不提供分解映射的字母也会存在类似问题。 (如果您试图找到“最好的”ASCII 字符来表示 Unicode 字母,那么这种方法对于西里尔文、希腊文、中文或其他非拉丁字母完全不起作用;如果出现以下情况,您也会遇到问题例如,您想将“ß”音译为“ss”。使用像 UnidecodeSharp 这样的库可能会有所帮助。)

【讨论】:

    【解决方案2】:

    我不得不承认,我不确定为什么会这样,但似乎确实如此

    var str = "æøåáâăäĺćçčéęëěíîďđńňóôőöřůúűüýţ";
    var noApostrophes = Encoding.ASCII.GetString(Encoding.GetEncoding("Cyrillic").GetBytes(str)); 
    

    => "aoaaaaalccceeeeiiddnnooouuuuyt"

    【讨论】:

    • “西里尔文”编码似乎有一个备用字符表,当输入字符未出现在代码页 1251 中时,它将使用该表;这感觉像是对这种无证行为的滥用。它还将“ß”(和任何其他无法识别的字符)转换为“?”,这可能是不合适的(就像将“æ”转换为“a”一样)。对于(几乎完整的)Unicode 音译,请查看unidecode.codeplex.com
    • 是的,这肯定是一个 hack。 Unidecode 与 Iconv //TRANSLIT 相比如何?
    • 我还没有针对 iconv 测试 Unidecode //TRANSLIT;我在这种情况下建议使用 Unidecode,因为它是一个 .NET 库(因此不需要 P/Invoke 来使用它)。
    【解决方案3】:

    D with stroke”(维基百科)在多种语言中使用,并且似乎在所有语言中都被视为不同的字母 - 这就是它保持不变的原因。

    【讨论】:

    • 另外,古英语中的 eth 在英语中变成了“th”,而在挪威语中则变成了“d”。除了与大写 d 表面上的相似性之外,它完全不同。
    • 是的,但同样适用于 č 或 ć,这也是一个不同的字母。
    • 具体来说,Unicode 没有为 đ 定义分解映射(而它为 č 和 å 定义了分解映射,其他一些字母认为它们是不同的字母)。
    【解决方案4】:

    string.Normalize(NormalizationForm) 是删除“真实”变音符号 (Wiki) 的简单方法,但您可能想要转换的许多字母不受此影响。

    我在 Ð 和 ð(字母 Eth)、đ、Æ 和 æ 方面遇到了类似的问题。要将它们转换为 ANSI(拉丁文),请改用 Unicode 转换!

        private static char[] ConvertUnicodeStringToSpecificEncoding(string input, int resultEncodingCode)
        {
            System.Text.Encoding unicodeEncoding = System.Text.Encoding.Unicode;
            System.Text.Encoding specificEncoding = System.Text.Encoding.GetEncoding(resultEncodingCode);
    
            byte[] convertedBytes = System.Text.Encoding.Convert(unicodeEncoding, specificEncoding, unicodeEncoding.GetBytes(input));
            char[] convertedChars = new char[specificEncoding.GetCharCount(convertedBytes, 0, convertedBytes.Length)];
            specificEncoding.GetChars(convertedBytes, 0, convertedBytes.Length, convertedChars, 0);
            return convertedChars;
        }
    

    在同一字符串上使用多种编码调用此方法,以在您想要留下的字母上创建一个交集。

    编码列表: https://docs.microsoft.com/en-us/dotnet/api/system.text.encoding?view=netframework-4.8

    我的解决方案是这样的

        // Encoding Types (int Codes) https://docs.microsoft.com/en-us/dotnet/api/system.text.encoding?view=netframework-4.8
        private static readonly char[] charactersToSkip = new char[] { 'ä', 'ö', 'ü', 'Ä', 'Ö', 'Ü' };
        private static readonly char[] specialCharsToSkip = new char[] { '^', '´', '`', '°', '!', '\'', '§', '$', '%', '&', '/', '(', ')', '=', '{', '[', ']', '}', '\\', '+', '-' };
        private static readonly char[] ambiguousCharsToSkip = new char[] { '?' };   // Chars which might be a result of encoding-conversion and have to be skipped beforehand.
        private static readonly int[] encodingsToRemoveDiacritics = new int[]
        {
            852,    // 852  ibm852  Central European (DOS)
            850,    // 850  ibm850  Western European (DOS)
            860,    // 860  IBM860  Portuguese (DOS)    
    
            /* Warning:
             * Only append encodings.
             * Changing sort order of encodings may result in malfunctioning.
             */ 
        };
    
        public static string RemoveDiacritics(this string inputString)
        {
            if (string.IsNullOrEmpty(inputString))
            {
                return inputString;
            }
    
            var resultStringBuilder = new StringBuilder();
    
            foreach (char currentChar in inputString)
            {
                if (charactersToSkip.Contains(currentChar) || specialCharsToSkip.Contains(currentChar) || ambiguousCharsToSkip.Contains(currentChar))
                {
                    resultStringBuilder.Append(currentChar);
                    continue;
                }
    
                string normalizedString = currentChar.ToString().Normalize(NormalizationForm.FormD);
                foreach (char normalizedChar in normalizedString)
                {
                    if (System.Globalization.CharUnicodeInfo.GetUnicodeCategory(normalizedChar) != System.Globalization.UnicodeCategory.NonSpacingMark)
                    {
                        string convertedString = normalizedChar.ToString();
                        char[] convertedChars = null;
    
                        foreach (int encodingCode in encodingsToRemoveDiacritics)
                        {
                            convertedChars = ConvertUnicodeStringToSpecificEncoding(convertedString, encodingCode);
    
                            if (convertedChars.Contains('?') == false)
                            {
                                convertedString = new string(convertedChars);
                            }
                        }
    
                        resultStringBuilder.Append(convertedString);
                    }
                }
            }
    
            return resultStringBuilder.ToString();
        }
    

    创建以下输出

    "abcdefghijklmnopqrstuvwxzy" -> "abcdefghijklmnopqrstuvwxzy"
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ" -> "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    "1234567890" -> "1234567890"
    "ß" -> "ß"
    "ÄÖÜ" -> "ÄÖÜ"
    "äöü" -> "äöü"
    "!\"§$%&/()=?" -> "!\"§$%&/()=?"
    "+-_~'*#" -> "+-_~'*#"
    ",.;:" -> ",.;:"
    "µ" -> "u" // My -> u
    "<>|" -> "<>|"
    "´`^°" -> "´`^°"
    "²" -> "2" // ² -> 2
    "³" -> "3" // ³ -> 3
    "{}" -> "{}"
    "[]" -> "[]"
    "\\" -> "\\"
    "áàâã" -> "aaaa"
    "ÁÀÂÅ" -> "AAAA"
    "éèêę" -> "eeee"
    "ÉÈÊĚ" -> "EEEE"
    "íìîï" -> "iiii"
    "ÍÌÎ" -> "III"
    "óòôõ" -> "oooo"
    "ÓÒÔŌ" -> "OOOO"
    "úùû" -> "uuu"
    "ÚÙÛ" -> "UUU"
    "ÇĆĈČĊ" -> "CCCCC"
    "çćĉčċ" -> "ccccc"
    "Ñ" -> "N"
    "Æ" -> "A"
    "æ" -> "a"
    "ýÿ" -> "yy"
    "ĹĻĽ" -> "LLL"
    "Ð" -> "D"
    "đ" -> "d"
    "ð" -> "d"
    

    【讨论】:

      【解决方案5】:

      这应该可以工作

          private static String RemoveDiacritics(string text)
          {
              String normalized = text.Normalize(NormalizationForm.FormD);
              StringBuilder sb = new StringBuilder();
      
              for (int i = 0; i < normalized.Length; i++)
              {
                  Char c = normalized[i];
                  if (CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
                      sb.Append(c);
              }
      
              return sb.ToString();
          }
      

      【讨论】:

      • 这看起来就像原始海报的代码,将 FormKD 更改为 FormD(以及小的风格变化)。由于其他答案中给出的原因,这不起作用。
      • 到目前为止,我一直在使用 FormD,但我没有意识到这个问题,但是正如我所见(我刚刚测试过),你是对的。它不起作用。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-03
      • 2016-01-21
      • 1970-01-01
      • 1970-01-01
      • 2015-07-19
      相关资源
      最近更新 更多