如何在.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个回答

6

对于仅需要删除加拿大法语口音符号的情况,这里提供了一种使用正则表达式而非硬编码转换和For/Next循环的替代方法。根据您的需求,它可以被压缩为单行代码;但是,我将其添加到扩展类中以便于重复使用。

Visual Basic

Imports System.Text
Imports System.Text.RegularExpressions

Public MustInherit Class StringExtension
    Public Shared Function RemoveDiacritics(Text As String) As String
        Return New Regex("\p{Mn}", RegexOptions.Compiled).Replace(Text.Normalize(NormalizationForm.FormD), String.Empty)
    End Function
End Class

实现

    Private Shared Sub DoStuff()
        MsgBox(StringExtension.RemoveDiacritics(inputString))
    End Sub

c#

using System.Text;
using System.Text.RegularExpressions;

namespace YourApplication
{
    public abstract class StringExtension
    {
        public static string RemoveDiacritics(string Text)
        {
            return new Regex(@"\p{Mn}", RegexOptions.Compiled).Replace(Text.Normalize(NormalizationForm.FormD), string.Empty);
        }
    }
}

实现

        private static void DoStuff()
        {
            MessageBox.Show(StringExtension.RemoveDiacritics(inputString));
        }

输入:    äáčďěéíľľňôóřŕšťúůýž ÄÁČĎĚÉÍĽĽŇÔÓŘŔŠŤÚŮÝŽ ÖÜË łŁđĐ ţŢşŞçÇ øı

输出: aacdeeillnoorrstuuyz AACDEEILLNOORRSTUUYZ OUE łŁđĐ tTsScC øı

我包括了一些不会被转换的字符,以帮助可视化处理意外输入时会发生什么。

如果您需要将其他类型的字符(如波兰语中的ł和Ł)也转换,那么根据您的需求,考虑将使用CodePagesEncodingProvider此答案 (适用于.NET Core)融入到您的解决方案中。


1
我真的很想知道为什么我是第一个投票支持这个。这只需要一行代码就可以解决。我不需要添加那些命名空间,因为它们可能已经默认可用了。 - esims

5

1
好的,这个答案被低估了。如果你需要支持许多不同的脚本,这是最好的选择 - 太棒了! - Wouter Steenbergen

4

以下是我如何在所有的.NET程序中将有变音符号的字符替换为没有变音符号的字符:

C#:

//Transforms the culture of a letter to its equivalent representation in the 0-127 ascii table, such as the letter 'é' is substituted by an 'e'
public string RemoveDiacritics(string s)
{
    string normalizedString = null;
    StringBuilder stringBuilder = new StringBuilder();
    normalizedString = s.Normalize(NormalizationForm.FormD);
    int i = 0;
    char c = '\0';

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

    return stringBuilder.ToString().ToLower();
}

VB .NET:

'Transforms the culture of a letter to its equivalent representation in the 0-127 ascii table, such as the letter "é" is substituted by an "e"'
Public Function RemoveDiacritics(ByVal s As String) As String
    Dim normalizedString As String
    Dim stringBuilder As New StringBuilder
    normalizedString = s.Normalize(NormalizationForm.FormD)
    Dim i As Integer
    Dim c As Char

    For i = 0 To normalizedString.Length - 1
        c = normalizedString(i)
        If CharUnicodeInfo.GetUnicodeCategory(c) <> UnicodeCategory.NonSpacingMark Then
            stringBuilder.Append(c)
        End If
    Next
    Return stringBuilder.ToString().ToLower()
End Function

3

3

这是 VB 版本(适用于希腊语):

导入 System.Text

导入 System.Globalization

Public Function RemoveDiacritics(ByVal s As String)
    Dim normalizedString As String
    Dim stringBuilder As New StringBuilder
    normalizedString = s.Normalize(NormalizationForm.FormD)
    Dim i As Integer
    Dim c As Char
    For i = 0 To normalizedString.Length - 1
        c = normalizedString(i)
        If CharUnicodeInfo.GetUnicodeCategory(c) <> UnicodeCategory.NonSpacingMark Then
            stringBuilder.Append(c)
        End If
    Next
    Return stringBuilder.ToString()
End Function

1
可能是一个老问题,但是为什么你要在变量声明和第一次赋值之间使用不同的行呢? - NiKiZe

1
尝试使用HelperSharp包
有一个名为RemoveAccents的方法:
 public static string RemoveAccents(this string source)
 {
     //8 bit characters 
     byte[] b = Encoding.GetEncoding(1251).GetBytes(source);

     // 7 bit characters
     string t = Encoding.ASCII.GetString(b);
     Regex re = new Regex("[^a-zA-Z0-9]=-_/");
     string c = re.Replace(t, " ");
     return c;
 }

1

没有源代码;Nugget包指向一个无效的URL,托管在CodePlex上。 - João Vieira

1
Imports System.Text
Imports System.Globalization

 Public Function DECODE(ByVal x As String) As String
        Dim sb As New StringBuilder
        For Each c As Char In x.Normalize(NormalizationForm.FormD).Where(Function(a) CharUnicodeInfo.GetUnicodeCategory(a) <> UnicodeCategory.NonSpacingMark)  
            sb.Append(c)
        Next
        Return sb.ToString()
    End Function

1
使用NFD而不是NFC会引起远远超出所请求的更改。 - Jon Hanna

1

这个人说了什么:

Encoding.ASCII.GetString(Encoding.GetEncoding(1251).GetBytes(text));

它实际上将像å这样的字符(其字符代码为00E5,而不是0061加上修饰符030A,尽管它看起来相同)分成a和某种修饰符,然后ASCII转换会删除修饰符,只剩下a。

1

我非常喜欢azrafe7提供的简洁而实用的代码。因此,我稍微改变了一下,将其转换为扩展方法:

public static class StringExtensions
{
    public static string RemoveDiacritics(this string text)
    {
        const string SINGLEBYTE_LATIN_ASCII_ENCODING = "ISO-8859-8";

        if (string.IsNullOrEmpty(text))
        {
            return string.Empty;
        }

        return Encoding.ASCII.GetString(
            Encoding.GetEncoding(SINGLEBYTE_LATIN_ASCII_ENCODING).GetBytes(text));
    }
}

这是唯一适用于所有波兰变音符号的方法。被接受的答案不能处理Ł和ł字符。 - yarecky

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