Làm cách nào để xóa dấu phụ (dấu) khỏi chuỗi trong .NET?


433

Tôi đang cố gắng chuyển đổi một số chuỗi bằng tiếng Canada của Pháp và về cơ bản, tôi muốn có thể lấy ra các dấu giọng Pháp trong các chữ cái trong khi giữ chữ cái. (Ví dụ: chuyển đổi éthành e, vì vậy crème brûléesẽ trở thành creme brulee)

Phương pháp tốt nhất để đạt được điều này là gì?


14
Cảnh báo: Cách tiếp cận này có thể hoạt động trong một số trường hợp cụ thể, nhưng nói chung, bạn không thể chỉ xóa dấu phụ. Trong một số trường hợp và một số ngôn ngữ, điều này có thể thay đổi ý nghĩa của văn bản. Bạn không nói lý do tại sao bạn muốn làm điều này; nếu đó là vì mục đích so sánh các chuỗi hoặc tìm kiếm thì có lẽ bạn nên sử dụng một thư viện nhận biết unicode cho việc này.
JacquesB

1
Vì hầu hết các kỹ thuật để đạt được điều này dựa trên chuẩn hóa Unicode, tài liệu này mô tả tiêu chuẩn có thể hữu ích để đọc: unicode.org/reports/tr15
LuddyPants 16/12/13

Tôi nghĩ rằng nhóm Azure đã khắc phục sự cố này, tôi đã cố tải lên một tệp có tên "Mémo de la réunion.pdf" và hoạt động đã thành công.
Rady

Câu trả lời:


531

Tôi đã không sử dụng phương pháp này, nhưng Michael Kaplan mô tả một phương pháp để làm như vậy trong bài đăng trên blog của anh ấy (với một tiêu đề khó hiểu) nói về tước bỏ dấu phụ: Tước là một công việc thú vị (còn gọi là ý nghĩa của các ký tự Mn vô nghĩa không có khoảng cách, nhưng một số không có khoảng cách hơn những cái khác)

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

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

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

Lưu ý rằng đây là phần tiếp theo của bài viết trước của anh ấy: Tước dấu phụ ....

Cách tiếp cận sử dụng String.N normalize để phân tách chuỗi đầu vào thành các glyph cấu thành (về cơ bản tách các ký tự "cơ sở" khỏi các dấu phụ) và sau đó quét kết quả và chỉ giữ lại các ký tự cơ sở. Nó chỉ là một chút phức tạp, nhưng thực sự bạn đang xem xét một vấn đề phức tạp.

Tất nhiên, nếu bạn giới hạn bản thân với tiếng Pháp, có lẽ bạn có thể thoát khỏi cách tiếp cận dựa trên bảng đơn giản trong Cách xóa dấu và dấu trong chuỗi C ++ std :: như được đề xuất bởi @David Dibben.


32
Cái này sai. Các ký tự tiếng Đức ä và ö và ü được Latin hóa thành ae ue và oe, và không phải là a, ou ...
Stefan Steiger

20
Ngoài ra, chữ Ba Lan ł được bỏ qua.
Zbigniew Wiadro

4
Ngoài ra Norse ø bị bỏ qua
Richard de Wit

28
Tài khoản và "hrabe" / hrabe /. Đối với tôi, dường như việc xóa dấu phụ là một vấn đề đồ họa thuần túy, không phụ thuộc vào ngữ âm hoặc lịch sử của bức thư. Các chữ cái như ä ö ü đã được tạo bằng cách thêm một siêu ký tự "e" vào các chữ cái cơ sở, do đó phân tách "ae" có ý nghĩa về mặt lịch sử. Nó phụ thuộc vào mục tiêu - để xóa các dấu đồ họa hoặc phân tách chữ cái thành các ký tự ASCII.
IllidanS4 muốn Monica trở lại vào

10
Chức năng này là bất khả tri ngôn ngữ. Nó không biết liệu chuỗi đó là tiếng Đức hay trong một số ngôn ngữ khác. Nếu chúng ta tính đến việc thay thế ö bằng oe trong văn bản tiếng Đức, nhưng sẽ không có ý nghĩa gì khi làm điều đó với tiếng Thổ Nhĩ Kỳ, thì chúng ta sẽ thấy rằng nếu không phát hiện ra ngôn ngữ thì vấn đề này không thực sự giải quyết được.
gai

163

điều này đã lừa tôi ...

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

nhanh & ngắn!


9
Đây là phương pháp tốt nhất tôi từng thấy.
Cleiton

2
Tôi thích giải pháp này và nó hoạt động tốt cho Ứng dụng Windows Store. Tuy nhiên, nó không hoạt động cho Ứng dụng Windows Phone vì mã hóa ISO-8859-8 dường như không khả dụng. Có một mã hóa khác có thể được sử dụng thay thế?
Philip Colmer

2
Điều này sẽ làm việc cho hầu hết các nhân vật phổ biến, nhưng nhiều nhân vật đặc biệt thích « »(như một nhân vật) sẽ bị thay đổi trong quá trình không phải là trường hợp với giải pháp được chấp nhận.
The_Black_Smurf

7
Lưu ý rằng điều này không hoạt động trên .NET Core trên Linux:System.ArgumentException: 'ISO-8859-8' is not a supported encoding name.
EM0

2
Nếu bạn đang sử dụng .NET Core, hãy cài đặt System.Text.Encoding.CodePagestừ nuget, sau đó gọi số này để đăng ký nhà cung cấp: Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);- khi bạn đã hoàn thành việc này, bạn có thể sử dụng ISO-8859-8
SpaceBison

32

Trong trường hợp ai đó quan tâm, tôi đã tìm kiếm một cái gì đó tương tự và kết thúc bằng văn bản sau:

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
}

9
Bạn nên phân bổ bộ đệm StringBuilder thành tên. Độ dài để giảm thiểu chi phí cấp phát bộ nhớ. Đó là cuộc gọi Chia / Tham gia cuối cùng để loại bỏ trùng lặp liên tiếp _ thật thú vị. Có lẽ chúng ta chỉ nên tránh thêm chúng vào vòng lặp. Đặt cờ cho ký tự trước là _ và không phát ra nếu đúng.
IDis Dùng

2 điểm thực sự tốt, tôi sẽ viết lại nếu tôi có thời gian quay lại phần mã này :)
Luk

Đẹp. Ngoài nhận xét IDisposeables, có lẽ chúng ta nên kiểm tra c < 128, để đảm bảo rằng chúng tôi không nhận bất kỳ ký tự UTF nào, xem tại đây .
Christian Gollhardt

1
Hoặc có lẽ hiệu quả hơn c < 123. xem ASCI
Christian Gollhardt

vẫn không hoạt động với các ký tự như ø
juanora

27

Tôi cần một cái gì đó chuyển đổi tất cả các ký tự unicode chính và câu trả lời được bình chọn đã bỏ qua một số vì vậy tôi đã tạo một phiên bản CodeIgniter convert_accented_characters($str)thành C # có thể dễ dàng tùy chỉnh:

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;
    }
}

Sử dụng

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

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

5
Việc triển khai của bạn thực hiện công việc, nhưng nên được cải thiện trước khi được sử dụng trong mã sản xuất.
Pierre Arnaud


tại sao không chỉ đơn giản là thay thế nó if (entry.Key.IndexOf(c) != -1)vàoif (entry.Key.Contains(c))
Pawel Cioch

Tại sao không sử dụng lại RemoveDiacritics (char c) trong vòng lặp, tại sao không sử dụng StringBuilder. Tôi upvote cho từ điển phức tạp và giải pháp làm việc, nhưng mã có thể đơn giản hơn nhiều
Pawel Cioch

1
Tôi không hiểu tại sao có quá nhiều cú nhảy để sử dụng { "äæǽ", "ae" }thay vì { "ä", "ae" }, { "æ", "ae" }, { "ǽ", "ae" }và chỉ gọi if (foreign_characters.TryGetValue(...)) .... Bạn đã hoàn toàn đánh bại mục đích của chỉ mục mà từ điển đã có.
Bacon Bits

15

Trong trường hợp bất cứ ai quan tâm, đây là tương đương java:

import java.text.Normalizer;

public class MyClass
{
    public static String removeDiacritics(String input)
    {
        String nrml = Normalizer.normalize(input, Normalizer.Form.NFD);
        StringBuilder stripped = new StringBuilder();
        for (int i=0;i<nrml.length();++i)
        {
            if (Character.getType(nrml.charAt(i)) != Character.NON_SPACING_MARK)
            {
                stripped.append(nrml.charAt(i));
            }
        }
        return stripped.toString();
    }
}

3
thay vì tước + = nrml.charAt (i) sử dụng StringBuilder. bạn có thời gian chạy O (n²) ở đây.
Andreas Petersson

6
Câu trả lời Java này và các câu trả lời khác ở đây chỉ là một chủ đề sai lầm này. Câu hỏi là về c # (.NET) không phải Java!
suchoss

15

Tôi thường sử dụng phương pháp mở rộng dựa trên một phiên bản khác mà tôi tìm thấy ở đây (xem Thay thế các ký tự trong C # (ascii) ) Giải thích nhanh:

  • Bình thường hóa để tạo thành D chia tách các biểu đồ như è thành e và không phân biệt `
  • Từ đó, các ký tự không gian được loại bỏ
  • Kết quả được chuẩn hóa trở lại dạng C (Tôi không chắc đây có phải là cần thiết không)

Mã số:

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);
    }
}

9

CodePage của Hy Lạp (ISO) có thể làm điều đó

Các thông tin về codepage này là vào System.Text.Encoding.GetEncodings(). Tìm hiểu về: https://msdn.microsoft.com/pt-br/l Library / system.text.encodinginfo.getencoding (v = vs.110) .aspx

Tiếng Hy Lạp (ISO) có codepage 28597 và tên iso-8859-7 .

Chuyển đến mã ... \ 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"

Vì vậy, hãy viết chức năng này ...

public string RemoveAcentuation(string text)
{
    return
        System.Web.HttpUtility.UrlDecode(
            System.Web.HttpUtility.UrlEncode(
                text, Encoding.GetEncoding("iso-8859-7")));
}

Lưu ý rằng ... Encoding.GetEncoding("iso-8859-7")tương đương với Encoding.GetEncoding(28597)vì đầu tiên là tên và thứ hai là bảng mã của Mã hóa.


3
Thật tuyệt vời! Ngắn gọn và hiệu quả!
krlzlx

1
Công cụ tuyệt vời. Hầu như bất kỳ nhân vật tôi đã thử nghiệm thông qua. ( äáčďěéíľľňôóřŕšťúůýž ÄÁČĎĚÉÍĽĽŇÔÓŘŔŠŤÚŮÝŽ ÖÜË łŁđĐ ţŢşŞçÇ øı). Các vấn đề chỉ được tìm thấy với ßə, được chuyển đổi thành ?, nhưng các ngoại lệ như vậy luôn có thể được xử lý theo cách riêng biệt. Trước khi đưa sản phẩm này vào sản xuất, thử nghiệm phải được thực hiện tốt hơn đối với tất cả các khu vực Unicode có chứa các chữ cái có dấu phụ.
miroxlav

5

Thật là một câu hỏi buồn cười có thể nhận được rất nhiều câu trả lời, nhưng không có câu hỏi nào phù hợp với yêu cầu của tôi :) Có rất nhiều ngôn ngữ xung quanh, một giải pháp bất khả tri ngôn ngữ đầy đủ là AFAIK không thực sự có thể, vì những người khác đã đề cập rằng FormC hoặc FormD đang đưa ra vấn đề.

Vì câu hỏi ban đầu có liên quan đến tiếng Pháp, nên câu trả lời đơn giản nhất thực sự là

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

1251 nên được thay thế bằng mã hóa của ngôn ngữ đầu vào.

Điều này tuy nhiên chỉ thay thế một nhân vật bởi một nhân vật. Vì tôi cũng đang làm việc với tiếng Đức làm đầu vào, tôi đã thực hiện chuyển đổi thủ công

    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();
    }

Nó có thể không cung cấp hiệu suất tốt nhất, nhưng ít nhất nó rất dễ đọc và mở rộng. Regex là KHÔNG GO, chậm hơn nhiều so với bất kỳ công cụ char / chuỗi nào.

Tôi cũng có một phương pháp rất đơn giản để loại bỏ không gian:

    public static string RemoveSpace(this string str)
    {
        return str.Replace(" ", string.Empty);
    }

Cuối cùng, tôi đang sử dụng kết hợp cả 3 phần mở rộng ở trên:

    public static string LatinizeAndConvertToASCII(this string str, bool keepSpace = false)
    {
        str = str.LatinizeGermanCharacters().ConvertWesternEuropeanToASCII();            
        return keepSpace ? str : str.RemoveSpace();
    }

Và một bài kiểm tra đơn vị nhỏ cho điều đó (không đầy đủ) mà vượt qua thành công.

    [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);
    }

4

Điều này hoạt động tốt trong java.

Về cơ bản, nó chuyển đổi tất cả các ký tự có dấu thành các bản sao gốc của chúng, sau đó là dấu phụ kết hợp của chúng. Bây giờ bạn có thể sử dụng một biểu thức chính quy để loại bỏ dấu phụ.

import java.text.Normalizer;
import java.util.regex.Pattern;

public String deAccent(String str) {
    String nfdNormalizedString = Normalizer.normalize(str, Normalizer.Form.NFD); 
    Pattern pattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
    return pattern.matcher(nfdNormalizedString).replaceAll("");
}

Hoặc trong Java 7,"\\p{Block=CombiningDiacriticalMarks}"
Brent Faust

11
Tại sao bạn lại đăng một giải pháp Java khi câu hỏi đặc biệt yêu cầu .NET?
David

2
@David Câu hỏi này là câu hỏi hay nhất trên Google về "java drop accents". Không nói nó thuộc về nơi này, nhưng nó hữu ích ở đây.
blubb

3

Phương pháp mở rộng chuỗi TL; DR - C #

Tôi nghĩ rằng giải pháp tốt nhất để bảo tồn ý nghĩa của chuỗi là chuyển đổi các ký tự thay vì tước chúng, được minh họa rõ trong ví dụ này crème brûléethành crme brlevs.creme brulee .

Tôi đã kiểm tra nhận xét của Alexandre ở trên và thấy mã Lucene.Net được cấp phép Apache 2.0, vì vậy tôi đã sửa đổi lớp thành một phương thức mở rộng chuỗi đơn giản. Bạn có thể sử dụng nó như thế này:

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

Hàm quá dài để đăng trong câu trả lời StackOverflow (~ 139k ký tự 30k cho phép lol) vì vậy tôi đã tạo ra một ý chính và quy cho các tác giả :

/*
 * 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
    }
}

Hy vọng rằng sẽ giúp được người khác, đây là giải pháp mạnh mẽ nhất mà tôi đã tìm thấy!


Hãy cẩn thận: 1) Khái niệm này phụ thuộc vào địa phương. Ví dụ: "ä" có thể là "a" hoặc "aa". 2) Đặt tên sai / mô tả sai: Kết quả không nhất thiết chỉ từ Khối điều khiển C0 và khối Latin cơ bản. Nó chỉ chuyển đổi các chữ cái Latinh và một số biến thể ký hiệu thành "tương đương". (Tất nhiên, người ta có thể mất một lượt khác để thay thế hoặc loại bỏ các Điều khiển không phải C0 và các ký tự khối Latin cơ bản sau đó.) Nhưng điều này sẽ làm tốt những gì nó làm.
Tom Blodget

2

ĐÂY LÀ PHIÊN BẢN VB (Hoạt động với GREEK):

Nhập khẩu System.Text

Hệ thống nhập khẩu. Cân bằng

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
Có thể là một câu trả lời cũ, nhưng tại sao bạn sử dụng các dòng riêng biệt để khai báo biến và gán đầu tiên?
NiKiZe

2

Hãy thử gói HelperSharp .

Có một phương thức 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;
 }

2

Đây là cách tôi thay thế các ký tự dấu phụ thành các ký tự không dấu phụ trong tất cả chương trình .NET của tôi

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



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

Sử dụng NFD thay vì NFC sẽ gây ra những thay đổi vượt xa những yêu cầu.
Jon Hanna

1

Người này nói gì:

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

Nó thực sự phân tách những thứ giống như åmột ký tự (là mã ký tự 00E5, không phải 0061 là công cụ sửa đổi 030Atrông giống nhau) thành amột loại công cụ sửa đổi, và sau đó chuyển đổi ASCII loại bỏ công cụ sửa đổi, chỉ còn lại a.


1

Tôi thực sự thích mã súc tích và chức năng được cung cấp bởi azrafe7 . Vì vậy, tôi đã thay đổi nó một chút để chuyển đổi nó thành một phương thức mở rộng:

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));
    }
}

Đây là phương pháp duy nhất hoạt động với tất cả các dấu phụ đánh bóng. Câu trả lời được chấp nhận không hoạt động với các ký tự Ł và ł.
yarecky

-3

Không có đủ danh tiếng, rõ ràng tôi không thể nhận xét về liên kết tuyệt vời của Alexandre. - Lucene dường như là giải pháp duy nhất hoạt động trong các trường hợp chung chung.

Đối với những người muốn một giải pháp sao chép-dán đơn giản, đây là, tận dụng mã trong Lucene:

chuỗi testbed = "ÁÂÄÅÇÉÍÎÓÖØÚÜÞàáâãäåæçèéêëìíîïðñóôöøúüāăčĐęğęğŁńŌřŞřŞřŞ

Console.WriteLine (Lucene.latinizeLucene (testbed));

AAAACEIIOOOUUTHaaaaaaaeceeeeiiiidnoooouuaacDegiLlnOorSsszzsteu

//////////

public static class Lucene
{
    // source: https://raw.githubusercontent.com/apache/lucenenet/master/src/Lucene.Net.Analysis.Common/Analysis/Miscellaneous/ASCIIFoldingFilter.cs
    // idea: /programming/249087/how-do-i-remove-diacritics-accents-from-a-string-in-net (scroll down, search for lucene by Alexander)
    public static string latinizeLucene(string arg)
    {
        char[] argChar = arg.ToCharArray();

        // latinizeLuceneImpl can expand one char up to four chars - e.g. Þ to TH, or æ to ae, or in fact ⑽ to (10)
        char[] resultChar = new String(' ', arg.Length * 4).ToCharArray();

        int outputPos = Lucene.latinizeLuceneImpl(argChar, 0, ref resultChar, 0, arg.Length);

        string ret = new string(resultChar);
        ret = ret.Substring(0, outputPos);

        return ret;
    }

    /// <summary>
    /// Converts characters above ASCII to their ASCII equivalents.  For example,
    /// accents are removed from accented characters. 
    /// <para/>
    /// @lucene.internal
    /// </summary>
    /// <param name="input">     The characters to fold </param>
    /// <param name="inputPos">  Index of the first character to fold </param>
    /// <param name="output">    The result of the folding. Should be of size >= <c>length * 4</c>. </param>
    /// <param name="outputPos"> Index of output where to put the result of the folding </param>
    /// <param name="length">    The number of characters to fold </param>
    /// <returns> length of output </returns>
    private static int latinizeLuceneImpl(char[] input, int inputPos, ref char[] output, int outputPos, int length)
    {
        int end = inputPos + length;
        for (int pos = inputPos; pos < end; ++pos)
        {
            char c = input[pos];

            // Quick test: if it's not in range then just keep current character
            if (c < '\u0080')
            {
                output[outputPos++] = c;
            }
            else
            {
                switch (c)
                {
                    case '\u00C0': // À  [LATIN CAPITAL LETTER A WITH GRAVE]
                    case '\u00C1': // Á  [LATIN CAPITAL LETTER A WITH ACUTE]
                    case '\u00C2': // Â  [LATIN CAPITAL LETTER A WITH CIRCUMFLEX]
                    case '\u00C3': // Ã  [LATIN CAPITAL LETTER A WITH TILDE]
                    case '\u00C4': // Ä  [LATIN CAPITAL LETTER A WITH DIAERESIS]
                    case '\u00C5': // Å  [LATIN CAPITAL LETTER A WITH RING ABOVE]
                    case '\u0100': // Ā  [LATIN CAPITAL LETTER A WITH MACRON]
                    case '\u0102': // Ă  [LATIN CAPITAL LETTER A WITH BREVE]
                    case '\u0104': // Ą  [LATIN CAPITAL LETTER A WITH OGONEK]
                    case '\u018F': // Ə  http://en.wikipedia.org/wiki/Schwa  [LATIN CAPITAL LETTER SCHWA]
                    case '\u01CD': // Ǎ  [LATIN CAPITAL LETTER A WITH CARON]
                    case '\u01DE': // Ǟ  [LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON]
                    case '\u01E0': // Ǡ  [LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON]
                    case '\u01FA': // Ǻ  [LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE]
                    case '\u0200': // Ȁ  [LATIN CAPITAL LETTER A WITH DOUBLE GRAVE]
                    case '\u0202': // Ȃ  [LATIN CAPITAL LETTER A WITH INVERTED BREVE]
                    case '\u0226': // Ȧ  [LATIN CAPITAL LETTER A WITH DOT ABOVE]
                    case '\u023A': // Ⱥ  [LATIN CAPITAL LETTER A WITH STROKE]
                    case '\u1D00': // ᴀ  [LATIN LETTER SMALL CAPITAL A]
                    case '\u1E00': // Ḁ  [LATIN CAPITAL LETTER A WITH RING BELOW]
                    case '\u1EA0': // Ạ  [LATIN CAPITAL LETTER A WITH DOT BELOW]
                    case '\u1EA2': // Ả  [LATIN CAPITAL LETTER A WITH HOOK ABOVE]
                    case '\u1EA4': // Ấ  [LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE]
                    case '\u1EA6': // Ầ  [LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE]
                    case '\u1EA8': // Ẩ  [LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE]
                    case '\u1EAA': // Ẫ  [LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE]
                    case '\u1EAC': // Ậ  [LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW]
                    case '\u1EAE': // Ắ  [LATIN CAPITAL LETTER A WITH BREVE AND ACUTE]
                    case '\u1EB0': // Ằ  [LATIN CAPITAL LETTER A WITH BREVE AND GRAVE]
                    case '\u1EB2': // Ẳ  [LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE]
                    case '\u1EB4': // Ẵ  [LATIN CAPITAL LETTER A WITH BREVE AND TILDE]
                    case '\u1EB6': // Ặ  [LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW]
                    case '\u24B6': // Ⓐ  [CIRCLED LATIN CAPITAL LETTER A]
                    case '\uFF21': // A  [FULLWIDTH LATIN CAPITAL LETTER A]
                        output[outputPos++] = 'A';
                        break;
                    case '\u00E0': // à  [LATIN SMALL LETTER A WITH GRAVE]
                    case '\u00E1': // á  [LATIN SMALL LETTER A WITH ACUTE]
                    case '\u00E2': // â  [LATIN SMALL LETTER A WITH CIRCUMFLEX]
                    case '\u00E3': // ã  [LATIN SMALL LETTER A WITH TILDE]
                    case '\u00E4': // ä  [LATIN SMALL LETTER A WITH DIAERESIS]
                    case '\u00E5': // å  [LATIN SMALL LETTER A WITH RING ABOVE]
                    case '\u0101': // ā  [LATIN SMALL LETTER A WITH MACRON]
                    case '\u0103': // ă  [LATIN SMALL LETTER A WITH BREVE]
                    case '\u0105': // ą  [LATIN SMALL LETTER A WITH OGONEK]
                    case '\u01CE': // ǎ  [LATIN SMALL LETTER A WITH CARON]
                    case '\u01DF': // ǟ  [LATIN SMALL LETTER A WITH DIAERESIS AND MACRON]
                    case '\u01E1': // ǡ  [LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON]
                    case '\u01FB': // ǻ  [LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE]
                    case '\u0201': // ȁ  [LATIN SMALL LETTER A WITH DOUBLE GRAVE]
                    case '\u0203': // ȃ  [LATIN SMALL LETTER A WITH INVERTED BREVE]
                    case '\u0227': // ȧ  [LATIN SMALL LETTER A WITH DOT ABOVE]
                    case '\u0250': // ɐ  [LATIN SMALL LETTER TURNED A]
                    case '\u0259': // ə  [LATIN SMALL LETTER SCHWA]
                    case '\u025A': // ɚ  [LATIN SMALL LETTER SCHWA WITH HOOK]
                    case '\u1D8F': // ᶏ  [LATIN SMALL LETTER A WITH RETROFLEX HOOK]
                    case '\u1D95': // ᶕ  [LATIN SMALL LETTER SCHWA WITH RETROFLEX HOOK]
                    case '\u1E01': // ạ  [LATIN SMALL LETTER A WITH RING BELOW]
                    case '\u1E9A': // ả  [LATIN SMALL LETTER A WITH RIGHT HALF RING]
                    case '\u1EA1': // ạ  [LATIN SMALL LETTER A WITH DOT BELOW]
                    case '\u1EA3': // ả  [LATIN SMALL LETTER A WITH HOOK ABOVE]
                    case '\u1EA5': // ấ  [LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE]
                    case '\u1EA7': // ầ  [LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE]
                    case '\u1EA9': // ẩ  [LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE]
                    case '\u1EAB': // ẫ  [LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE]
                    case '\u1EAD': // ậ  [LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW]
                    case '\u1EAF': // ắ  [LATIN SMALL LETTER A WITH BREVE AND ACUTE]
                    case '\u1EB1': // ằ  [LATIN SMALL LETTER A WITH BREVE AND GRAVE]
                    case '\u1EB3': // ẳ  [LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE]
                    case '\u1EB5': // ẵ  [LATIN SMALL LETTER A WITH BREVE AND TILDE]
                    case '\u1EB7': // ặ  [LATIN SMALL LETTER A WITH BREVE AND DOT BELOW]
                    case '\u2090': // ₐ  [LATIN SUBSCRIPT SMALL LETTER A]
                    case '\u2094': // ₔ  [LATIN SUBSCRIPT SMALL LETTER SCHWA]
                    case '\u24D0': // ⓐ  [CIRCLED LATIN SMALL LETTER A]
                    case '\u2C65': // ⱥ  [LATIN SMALL LETTER A WITH STROKE]
                    case '\u2C6F': // Ɐ  [LATIN CAPITAL LETTER TURNED A]
                    case '\uFF41': // a  [FULLWIDTH LATIN SMALL LETTER A]
                        output[outputPos++] = 'a';
                        break;
                    case '\uA732': // Ꜳ  [LATIN CAPITAL LETTER AA]
                        output[outputPos++] = 'A';
                        output[outputPos++] = 'A';
                        break;
                    case '\u00C6': // Æ  [LATIN CAPITAL LETTER AE]
                    case '\u01E2': // Ǣ  [LATIN CAPITAL LETTER AE WITH MACRON]
                    case '\u01FC': // Ǽ  [LATIN CAPITAL LETTER AE WITH ACUTE]
                    case '\u1D01': // ᴁ  [LATIN LETTER SMALL CAPITAL AE]
                        output[outputPos++] = 'A';
                        output[outputPos++] = 'E';
                        break;
                    case '\uA734': // Ꜵ  [LATIN CAPITAL LETTER AO]
                        output[outputPos++] = 'A';
                        output[outputPos++] = 'O';
                        break;
                    case '\uA736': // Ꜷ  [LATIN CAPITAL LETTER AU]
                        output[outputPos++] = 'A';
                        output[outputPos++] = 'U';
                        break;

        // etc. etc. etc.
        // see link above for complete source code
        // 
        // unfortunately, postings are limited, as in
        // "Body is limited to 30000 characters; you entered 136098."

                    [...]

                    case '\u2053': // ⁓  [SWUNG DASH]
                    case '\uFF5E': // ~  [FULLWIDTH TILDE]
                        output[outputPos++] = '~';
                        break;
                    default:
                        output[outputPos++] = c;
                        break;
                }
            }
        }
        return outputPos;
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.