如何在.NET中从字符串中删除变音符号(重音符号)?

547
我尝试转换一些法裔加拿大的字符串,基本上,我想能够去掉字母上的法语重音标记并保留字母本身。(例如将é 转换为 e,因此 crème brûlée 将变为 creme brulee)最好的方法是什么?

21
警告:这种方法在某些特定情况下可能有效,但通常情况下不能仅仅去掉变音符号。在某些情况和某些语言中,这可能会改变文本的含义。你没有说明想要这么做的原因;如果是为了比较字符串或搜索,最好使用支持Unicode的库来完成。 - JacquesB
2
由于大多数实现此目的的技术都依赖于Unicode规范化,因此阅读描述该标准的文档可能会很有用:http://www.unicode.org/reports/tr15/ - LuddyPants
我认为Azure团队已经解决了这个问题,我尝试上传一个名为“Mémo de la réunion.pdf”的文件,操作成功了。 - Rady
1
在我们的情况下,限制来自于Postgres数据库中的ltree数据类型。其中ltree仅允许使用[a-zA-Z0-9_]。而对于我们的情况,确实需要进行快速搜索。 - Mike de Klerk
22个回答

652

我没有使用过这种方法,但是迈克尔·卡普兰在他的博客文章中描述了一种方法来实现这一点(标题有些令人困惑),该文章讨论了去除变音符号:去除是一项有趣的工作(又名关于无意义的意义,或者所有Mn字符都是非间距的,但有些比其他的更非间距)

static string RemoveDiacritics(string text) 
{
    var normalizedString = text.Normalize(NormalizationForm.FormD);
    var stringBuilder = new StringBuilder(capacity: normalizedString.Length);

    for (int i = 0; i < normalizedString.Length; i++)
    {
        char c = normalizedString[i];
        var unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c);
        if (unicodeCategory != UnicodeCategory.NonSpacingMark)
        {
            stringBuilder.Append(c);
        }
    }

    return stringBuilder
        .ToString()
        .Normalize(NormalizationForm.FormC);
}

请注意,这是他早期帖子的后续内容:Stripping diacritics....

该方法使用String.Normalize将输入字符串拆分为组成字符(从根本上分离“基”字符与附加符号),然后扫描结果并仅保留基字符。 原理有点复杂,但确实是一个复杂的问题。

当然,如果您只限于法语,则可以使用简单的基于表格的方法,如@David Dibben建议的How to remove accents and tilde in a C++ std::string


47
这是错误的。德语字母ä、ö和ü应该被拉丁化为ae、ue和oe,而不是a、o和u... - Stefan Steiger
36
同时,波兰字母“ł”会被忽略。 - Chris W
13
也就是说,忽略了北欧语中的字母 ø。 - Richard de Wit
42
@StefanSteiger,你知道,在捷克语中有像áčěů这样的字母,我们通常将其“拉丁化”为aceu,尽管它的发音不同,在诸如“hrábě” /hra:bje/,“hrabě” /hrabje/和“hrabe” /hrabe/等单词中可能会引起混淆。对我来说,删除变音符号似乎是一个纯粹的图形问题,与字母的音韵或历史无关。像ä ö ü这样的字母是通过在基本字母上添加上标“e”而创建的,因此“ae”分解在历史上是有意义的。这取决于目标-是去除图形标记还是将字母分解为ASCII字符。 - IS4
25
这个功能与语言无关,它不知道字符串是德语还是其他语言。如果我们考虑在德文文本中用 oe 替换 ö 是可以接受的,但在土耳其语中这样做没有意义,那么我们会发现,如果不检测语言,则无法真正解决此问题。 - thorn0
显示剩余19条评论

240

这对我起了作用...

string accentedStr;
byte[] tempBytes;
tempBytes = System.Text.Encoding.GetEncoding("ISO-8859-8").GetBytes(accentedStr);
string asciiStr = System.Text.Encoding.UTF8.GetString(tempBytes);

快速&简短!


15
这是我见过的最好方法。 - Cleiton
2
我喜欢这个解决方案,它适用于Windows Store应用程序,并且运行良好。然而,对于Windows Phone应用程序来说,ISO-8859-8编码似乎不可用。是否有其他可用的编码可以代替使用? - Philip Colmer
2
这个方法适用于大多数常见字符,但是许多特殊字符,如 « »(作为单个字符),在此过程中将被改变,而这不是被接受的解决方案所能解决的。 - The_Black_Smurf
20
请注意,这在 Linux 上的 .NET Core 中不起作用:System.ArgumentException: 'ISO-8859-8' 不是受支持的编码名称。 - EM0
32
如果你使用的是.NET Core,可以从Nuget安装System.Text.Encoding.CodePages, 然后调用以下代码注册编码提供程序:Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); 。一旦完成这些步骤,就可以使用ISO-8859-8编码。请注意,不要改变原文的意思。 - SpaceBison
显示剩余7条评论

48

我需要一个能转换所有主要Unicode字符的东西,但是所选答案有一些遗漏,因此我创建了一个易于定制的CodeIgniter的convert_accented_characters($str)版本,它可以用于C#:

using System;
using System.Text;
using System.Collections.Generic;

public static class Strings
{
    static Dictionary<string, string> foreign_characters = new Dictionary<string, string>
    {
        { "äæǽ", "ae" },
        { "öœ", "oe" },
        { "ü", "ue" },
        { "Ä", "Ae" },
        { "Ü", "Ue" },
        { "Ö", "Oe" },
        { "ÀÁÂÃÄÅǺĀĂĄǍΑΆẢẠẦẪẨẬẰẮẴẲẶА", "A" },
        { "àáâãåǻāăąǎªαάảạầấẫẩậằắẵẳặа", "a" },
        { "Б", "B" },
        { "б", "b" },
        { "ÇĆĈĊČ", "C" },
        { "çćĉċč", "c" },
        { "Д", "D" },
        { "д", "d" },
        { "ÐĎĐΔ", "Dj" },
        { "ðďđδ", "dj" },
        { "ÈÉÊËĒĔĖĘĚΕΈẼẺẸỀẾỄỂỆЕЭ", "E" },
        { "èéêëēĕėęěέεẽẻẹềếễểệеэ", "e" },
        { "Ф", "F" },
        { "ф", "f" },
        { "ĜĞĠĢΓГҐ", "G" },
        { "ĝğġģγгґ", "g" },
        { "ĤĦ", "H" },
        { "ĥħ", "h" },
        { "ÌÍÎÏĨĪĬǏĮİΗΉΊΙΪỈỊИЫ", "I" },
        { "ìíîïĩīĭǐįıηήίιϊỉịиыї", "i" },
        { "Ĵ", "J" },
        { "ĵ", "j" },
        { "ĶΚК", "K" },
        { "ķκк", "k" },
        { "ĹĻĽĿŁΛЛ", "L" },
        { "ĺļľŀłλл", "l" },
        { "М", "M" },
        { "м", "m" },
        { "ÑŃŅŇΝН", "N" },
        { "ñńņňʼnνн", "n" },
        { "ÒÓÔÕŌŎǑŐƠØǾΟΌΩΏỎỌỒỐỖỔỘỜỚỠỞỢО", "O" },
        { "òóôõōŏǒőơøǿºοόωώỏọồốỗổộờớỡởợо", "o" },
        { "П", "P" },
        { "п", "p" },
        { "ŔŖŘΡР", "R" },
        { "ŕŗřρр", "r" },
        { "ŚŜŞȘŠΣС", "S" },
        { "śŝşșšſσςс", "s" },
        { "ȚŢŤŦτТ", "T" },
        { "țţťŧт", "t" },
        { "ÙÚÛŨŪŬŮŰŲƯǓǕǗǙǛŨỦỤỪỨỮỬỰУ", "U" },
        { "ùúûũūŭůűųưǔǖǘǚǜυύϋủụừứữửựу", "u" },
        { "ÝŸŶΥΎΫỲỸỶỴЙ", "Y" },
        { "ýÿŷỳỹỷỵй", "y" },
        { "В", "V" },
        { "в", "v" },
        { "Ŵ", "W" },
        { "ŵ", "w" },
        { "ŹŻŽΖЗ", "Z" },
        { "źżžζз", "z" },
        { "ÆǼ", "AE" },
        { "ß", "ss" },
        { "IJ", "IJ" },
        { "ij", "ij" },
        { "Œ", "OE" },
        { "ƒ", "f" },
        { "ξ", "ks" },
        { "π", "p" },
        { "β", "v" },
        { "μ", "m" },
        { "ψ", "ps" },
        { "Ё", "Yo" },
        { "ё", "yo" },
        { "Є", "Ye" },
        { "є", "ye" },
        { "Ї", "Yi" },
        { "Ж", "Zh" },
        { "ж", "zh" },
        { "Х", "Kh" },
        { "х", "kh" },
        { "Ц", "Ts" },
        { "ц", "ts" },
        { "Ч", "Ch" },
        { "ч", "ch" },
        { "Ш", "Sh" },
        { "ш", "sh" },
        { "Щ", "Shch" },
        { "щ", "shch" },
        { "ЪъЬь", "" },
        { "Ю", "Yu" },
        { "ю", "yu" },
        { "Я", "Ya" },
        { "я", "ya" },
    };

    public static char RemoveDiacritics(this char c){
        foreach(KeyValuePair<string, string> entry in foreign_characters)
        {
            if(entry.Key.IndexOf (c) != -1)
            {
                return entry.Value[0];
            }
        }
        return c;
    }

    public static string RemoveDiacritics(this string s) 
    {
        //StringBuilder sb = new StringBuilder ();
        string text = "";


        foreach (char c in s)
        {
            int len = text.Length;

            foreach(KeyValuePair<string, string> entry in foreign_characters)
            {
                if(entry.Key.IndexOf (c) != -1)
                {
                    text += entry.Value;
                    break;
                }
            }

            if (len == text.Length) {
                text += c;  
            }
        }
        return text;
    }
}

使用方法

// for strings
"crème brûlée".RemoveDiacritics (); // creme brulee

// for chars
"Ã"[0].RemoveDiacritics (); // A

7
你的实现可以完成工作,但在用于生产代码之前需要进行改进。 - Pierre Arnaud
23
这是一个ASCIIFoldingFilter.cs文件的链接,它位于Lucene.Net.Analysis.Common/Analysis/Miscellaneous目录下。该文件实现了一个分析器过滤器,用于将包含非ASCII字符的文本中的这些字符转换为ASCII等效项。 - Alexander
1
我使用了@Alexander的链接来制作下面的答案:https://dev59.com/zXVC5IYBdhLWcg3wliGe#56797567 - Andy Raddatz
6
我不明白为什么要使用{"äæǽ","ae"}而不是{"ä","ae"}{"æ","ae"}{"ǽ","ae"},然后调用if (foreign_characters.TryGetValue(...)) ...。你已经完全打败了字典已经具有的索引的目的。 - Bacon Bits
对我来说最好的解决方案。谢谢。 - V. Z
显示剩余8条评论

37
接受的答案完全正确,但是现在应该更新为使用Rune类,而不是CharUnicodeInfo,因为C#&.NET在最新版本中更新了分析字符串的方式(Rune类已添加到.NET Core 3.0)。
以下是针对.NET 5+的建议代码,它可以更好地处理非拉丁字符:
static string RemoveDiacritics(string text) 
{
    var normalizedString = text.Normalize(NormalizationForm.FormD);
    var stringBuilder = new StringBuilder();

    foreach (var c in normalizedString.EnumerateRunes())
    {
        var unicodeCategory = Rune.GetUnicodeCategory(c);
        if (unicodeCategory != UnicodeCategory.NonSpacingMark)
        {
            stringBuilder.Append(c);
        }
    }

    return stringBuilder.ToString().Normalize(NormalizationForm.FormC);
}

1
这个工作效果出奇的好。谢谢。 - JJ_Coder4Hire
这个可以完美地处理重音和~。 - 8oris
1
请注意,这在不同的环境中可能会有所不同。在我的Windows开发机上,å被正确地更改为a。但是一旦部署到docker上,此函数未更改å,仍然保持为å而不是a。我怀疑相同的问题也适用于被接受的答案,但azrafe7的答案在两个环境中都完美地工作。 - Inrego

37

如果有人感兴趣,我在寻找类似的东西时最终写出了以下内容:

public static string NormalizeStringForUrl(string name)
{
    String normalizedString = name.Normalize(NormalizationForm.FormD);
    StringBuilder stringBuilder = new StringBuilder();

    foreach (char c in normalizedString)
    {
        switch (CharUnicodeInfo.GetUnicodeCategory(c))
        {
            case UnicodeCategory.LowercaseLetter:
            case UnicodeCategory.UppercaseLetter:
            case UnicodeCategory.DecimalDigitNumber:
                stringBuilder.Append(c);
                break;
            case UnicodeCategory.SpaceSeparator:
            case UnicodeCategory.ConnectorPunctuation:
            case UnicodeCategory.DashPunctuation:
                stringBuilder.Append('_');
                break;
        }
    }
    string result = stringBuilder.ToString();
    return String.Join("_", result.Split(new char[] { '_' }
        , StringSplitOptions.RemoveEmptyEntries)); // remove duplicate underscores
}

10
为了最小化内存分配开销,你应该预先分配 StringBuilder 缓冲区的大小为 name.Length。最后那个 Split/Join 调用以移除连续重复 _ 是有意思的。也许我们应该避免在循环中添加它们。设置一个标志表示前一个字符是 _ 并且如果为 true,则不发出一个 _。 - IDisposable
2个非常好的点,如果我有时间回到这部分代码,我会重新编写它 :) - Luk
很好。除了IDisposable的评论之外,我们可能还应该检查 c < 128,以确保我们不会拾取任何UTF字符,在这里看 - Christian Gollhardt
1
或者更有效率的写法是 c < 123参见ASCII - Christian Gollhardt

16

我经常使用一个基于找到的另一个版本的扩展方法(见Replacing characters in C# (ascii)

  • 将字符规范化为形式D,将像è这样的字符拆分为e和一个非间隔的`
  • 从中删除非间隔字符
  • 结果再次规范化为形式C(我不确定是否必要)

代码:

using System.Linq;
using System.Text;
using System.Globalization;

// namespace here
public static class Utility
{
    public static string RemoveDiacritics(this string str)
    {
        if (null == str) return null;
        var chars =
            from c in str.Normalize(NormalizationForm.FormD).ToCharArray()
            let uc = CharUnicodeInfo.GetUnicodeCategory(c)
            where uc != UnicodeCategory.NonSpacingMark
            select c;

        var cleanStr = new string(chars.ToArray()).Normalize(NormalizationForm.FormC);

        return cleanStr;
    }

    // or, alternatively
    public static string RemoveDiacritics2(this string str)
    {
        if (null == str) return null;
        var chars = str
            .Normalize(NormalizationForm.FormD)
            .ToCharArray()
            .Where(c=> CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
            .ToArray();

        return new string(chars).Normalize(NormalizationForm.FormC);
    }
}

15

希腊编码(ISO)可以做到这一点

有关此代码页的信息位于System.Text.Encoding.GetEncodings()。请参阅:https://msdn.microsoft.com/pt-br/library/system.text.encodinginfo.getencoding(v=vs.110).aspx

Greek (ISO) 编码为 28597,名称为 iso-8859-7

前往代码... \o/

string text = "Você está numa situação lamentável";

string textEncode = System.Web.HttpUtility.UrlEncode(text, Encoding.GetEncoding("iso-8859-7"));
//result: "Voce+esta+numa+situacao+lamentavel"

string textDecode = System.Web.HttpUtility.UrlDecode(textEncode);
//result: "Voce esta numa situacao lamentavel"

因此,编写这个函数...

public string RemoveAcentuation(string text)
{
    return
        System.Web.HttpUtility.UrlDecode(
            System.Web.HttpUtility.UrlEncode(
                text, Encoding.GetEncoding("iso-8859-7")));
}
请注意,Encoding.GetEncoding("iso-8859-7")等同于 Encoding.GetEncoding(28597) ,因为前者是名称,后者是编码页的表示。

5
太棒了!简洁高效! - krlzlx
2
很棒的东西。我测试过的几乎所有字符都通过了 (äáčďěéíľľňôóřŕšťúůýž ÄÁČĎĚÉÍĽĽŇÔÓŘŔŠŤÚŮÝŽ ÖÜË łŁđĐ ţŢşŞçÇ øı)。只有 ßə 出现了问题,被转换成了 ?,但这样的例外总是可以单独处理的。在投入生产之前,最好对所有包含带变音符号字母的 Unicode 区域进行更好的测试。 - miroxlav

13

TL;DR - C#字符串扩展方法

我认为保留字符串的含义的最佳解决方案是转换字符而不是剥离它们,这在例子crème brûléetocrme brlecreme brulee中得到了很好的说明。

我查看了上面亚历山大的评论,发现 Lucene.Net 代码是 Apache 2.0 授权的,因此我将该类修改为一个简单的字符串扩展方法。你可以像这样使用它:

var originalString = "crème brûlée";
var maxLength = originalString.Length; // limit output length as necessary
var foldedString = originalString.FoldToASCII(maxLength); 
// "creme brulee"

这个函数太长了,无法在StackOverflow的回答中发布(30k允许的字符数只有 ~139k),所以我制作了一个代码片段,并注明了作者

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/// <summary>
/// This class converts alphabetic, numeric, and symbolic Unicode characters
/// which are not in the first 127 ASCII characters (the "Basic Latin" Unicode
/// block) into their ASCII equivalents, if one exists.
/// <para/>
/// Characters from the following Unicode blocks are converted; however, only
/// those characters with reasonable ASCII alternatives are converted:
/// 
/// <ul>
///   <item><description>C1 Controls and Latin-1 Supplement: <a href="http://www.unicode.org/charts/PDF/U0080.pdf">http://www.unicode.org/charts/PDF/U0080.pdf</a></description></item>
///   <item><description>Latin Extended-A: <a href="http://www.unicode.org/charts/PDF/U0100.pdf">http://www.unicode.org/charts/PDF/U0100.pdf</a></description></item>
///   <item><description>Latin Extended-B: <a href="http://www.unicode.org/charts/PDF/U0180.pdf">http://www.unicode.org/charts/PDF/U0180.pdf</a></description></item>
///   <item><description>Latin Extended Additional: <a href="http://www.unicode.org/charts/PDF/U1E00.pdf">http://www.unicode.org/charts/PDF/U1E00.pdf</a></description></item>
///   <item><description>Latin Extended-C: <a href="http://www.unicode.org/charts/PDF/U2C60.pdf">http://www.unicode.org/charts/PDF/U2C60.pdf</a></description></item>
///   <item><description>Latin Extended-D: <a href="http://www.unicode.org/charts/PDF/UA720.pdf">http://www.unicode.org/charts/PDF/UA720.pdf</a></description></item>
///   <item><description>IPA Extensions: <a href="http://www.unicode.org/charts/PDF/U0250.pdf">http://www.unicode.org/charts/PDF/U0250.pdf</a></description></item>
///   <item><description>Phonetic Extensions: <a href="http://www.unicode.org/charts/PDF/U1D00.pdf">http://www.unicode.org/charts/PDF/U1D00.pdf</a></description></item>
///   <item><description>Phonetic Extensions Supplement: <a href="http://www.unicode.org/charts/PDF/U1D80.pdf">http://www.unicode.org/charts/PDF/U1D80.pdf</a></description></item>
///   <item><description>General Punctuation: <a href="http://www.unicode.org/charts/PDF/U2000.pdf">http://www.unicode.org/charts/PDF/U2000.pdf</a></description></item>
///   <item><description>Superscripts and Subscripts: <a href="http://www.unicode.org/charts/PDF/U2070.pdf">http://www.unicode.org/charts/PDF/U2070.pdf</a></description></item>
///   <item><description>Enclosed Alphanumerics: <a href="http://www.unicode.org/charts/PDF/U2460.pdf">http://www.unicode.org/charts/PDF/U2460.pdf</a></description></item>
///   <item><description>Dingbats: <a href="http://www.unicode.org/charts/PDF/U2700.pdf">http://www.unicode.org/charts/PDF/U2700.pdf</a></description></item>
///   <item><description>Supplemental Punctuation: <a href="http://www.unicode.org/charts/PDF/U2E00.pdf">http://www.unicode.org/charts/PDF/U2E00.pdf</a></description></item>
///   <item><description>Alphabetic Presentation Forms: <a href="http://www.unicode.org/charts/PDF/UFB00.pdf">http://www.unicode.org/charts/PDF/UFB00.pdf</a></description></item>
///   <item><description>Halfwidth and Fullwidth Forms: <a href="http://www.unicode.org/charts/PDF/UFF00.pdf">http://www.unicode.org/charts/PDF/UFF00.pdf</a></description></item>
/// </ul>
/// <para/>
/// See: <a href="http://en.wikipedia.org/wiki/Latin_characters_in_Unicode">http://en.wikipedia.org/wiki/Latin_characters_in_Unicode</a>
/// <para/>
/// For example, '&amp;agrave;' will be replaced by 'a'.
/// </summary>
public static partial class StringExtensions
{
    /// <summary>
    /// Converts characters above ASCII to their ASCII equivalents.  For example,
    /// accents are removed from accented characters. 
    /// </summary>
    /// <param name="input">     The string of characters to fold </param>
    /// <param name="length">    The length of the folded return string </param>
    /// <returns> length of output </returns>
    public static string FoldToASCII(this string input, int? length = null)
    {
        // See https://gist.github.com/andyraddatz/e6a396fb91856174d4e3f1bf2e10951c
    }
}

希望这能对其他人有所帮助,这是我找到的最稳定的解决方案!


注意事项:1)该概念依赖于语言环境。例如,“ä”可能是“a”或“aa”。2)名称错误/描述不准确:结果不一定只来自C0控制字符和基本拉丁字母块。它只将拉丁字母和一些符号变体转换为“等效物”。(当然,可以进行另一次处理以替换或删除非C0控制字符和基本拉丁字母块字符。)但这样做会很好地完成它的工作。 - Tom Blodget
1
感谢您将这个发布出来。我相信您在文件末尾有一个多余的 } 括号。 - Superman.Lopez
谢谢这个建议,我建议在string.IsNullOrWhiteSpace(input)处添加早期返回,并将StringBuilder的容量初始化为input.Length。 - agrath

8
与已接受的答案相同,但更快,使用 Span 而不是 StringBuilder。 需要 .NET Core 3.1 或更新版本的.NET。
static string RemoveDiacritics(string text) 
{
    ReadOnlySpan<char> normalizedString = text.Normalize(NormalizationForm.FormD);
    int i = 0;
    Span<char> span = text.Length < 1000
        ? stackalloc char[text.Length]
        : new char[text.Length];

    foreach (char c in normalizedString)
    {
        if (CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
            span[i++] = c;
    }

    return new string(span).Normalize(NormalizationForm.FormC);
}

此外,它还可以扩展以进行其他字符替换,例如波兰字母Ł。

span[i++] = c switch
{
    'Ł' => 'L',
    'ł' => 'l',
    _ => c
};

一个小提示:栈分配stackalloc比堆分配new更快,并且对垃圾回收器的工作量更小。 1000是为避免在堆栈上分配大型结构(可能导致StackOverflowException)而设置的阈值。虽然1000是非常安全的值,在大多数情况下,10000甚至100000也可以使用(100k在堆栈上分配高达200kB,而默认堆栈大小为1 MB)。不过,对我来说,100k看起来有点危险。


8

有趣的是,这样一个问题可以得到如此多的答案,但是没有一个符合我的要求 :) 有那么多的语言存在,完全面向语言不可行,因为其他人提到了FormC或FormD会出现问题。

由于原来的问题与法语有关,最简单的有效答案确实是

    public static string ConvertWesternEuropeanToASCII(this string str)
    {
        return Encoding.ASCII.GetString(Encoding.GetEncoding(1251).GetBytes(str));
    }

1251 应该被替换成输入语言的编码。

但是这样只能逐个字符地进行替换。因为我也在处理德语输入,所以我手动进行了转换。

    public static string LatinizeGermanCharacters(this string str)
    {
        StringBuilder sb = new StringBuilder(str.Length);
        foreach (char c in str)
        {
            switch (c)
            {
                case 'ä':
                    sb.Append("ae");
                    break;
                case 'ö':
                    sb.Append("oe");
                    break;
                case 'ü':
                    sb.Append("ue");
                    break;
                case 'Ä':
                    sb.Append("Ae");
                    break;
                case 'Ö':
                    sb.Append("Oe");
                    break;
                case 'Ü':
                    sb.Append("Ue");
                    break;
                case 'ß':
                    sb.Append("ss");
                    break;
                default:
                    sb.Append(c);
                    break;
            }
        }
        return sb.ToString();
    }

它可能不会提供最佳性能,但至少非常易于阅读和扩展。正则表达式是不可取的,比任何字符/字符串处理都要慢。
我还有一个非常简单的去除空格的方法:
    public static string RemoveSpace(this string str)
    {
        return str.Replace(" ", string.Empty);
    }

最终,我正在使用上述三个扩展的组合:
    public static string LatinizeAndConvertToASCII(this string str, bool keepSpace = false)
    {
        str = str.LatinizeGermanCharacters().ConvertWesternEuropeanToASCII();            
        return keepSpace ? str : str.RemoveSpace();
    }

再加上一个小的单元测试(不详尽),该测试已经成功通过。

    [TestMethod()]
    public void LatinizeAndConvertToASCIITest()
    {
        string europeanStr = "Bonjour ça va? C'est l'été! Ich möchte ä Ä á à â ê é è ë Ë É ï Ï î í ì ó ò ô ö Ö Ü ü ù ú û Û ý Ý ç Ç ñ Ñ";
        string expected = "Bonjourcava?C'estl'ete!IchmoechteaeAeaaaeeeeEEiIiiiooooeOeUeueuuuUyYcCnN";
        string actual = europeanStr.LatinizeAndConvertToASCII();
        Assert.AreEqual(expected, actual);
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接