Jak usunąć znaki diakrytyczne (akcenty) z łańcucha w .NET?


433

Próbuję przekonwertować niektóre ciągi znaków, które są w języku francuskim kanadyjskim i, w zasadzie, chciałbym być w stanie usunąć francuskie znaki akcentujące litery przy jednoczesnym zachowaniu litery. (Np. Przekonwertuj éna e, więc crème brûléesię stanie creme brulee)

Jaka jest najlepsza metoda na osiągnięcie tego?


14
Ostrzeżenie: To podejście może działać w niektórych szczególnych przypadkach, ale ogólnie nie można po prostu usunąć znaków diakrytycznych. W niektórych przypadkach i niektórych językach może to zmienić znaczenie tekstu. Nie mówisz, dlaczego chcesz to zrobić; jeśli chodzi o porównywanie ciągów lub wyszukiwanie, to prawdopodobnie lepiej jest użyć biblioteki obsługującej Unicode.
JacquesB

1
Ponieważ większość technik osiągnięcia tego celu opiera się na normalizacji Unicode, ten dokument opisujący standard może być przydatny do czytania: unicode.org/reports/tr15
LuddyPants

Myślę, że zespół Azure rozwiązał ten problem, próbowałem przesłać plik o nazwie „Mémo de la réunion.pdf” i operacja się powiodła.
Rady

Odpowiedzi:


531

Nie użyłem tej metody, ale Michael Kaplan opisuje metodę jej wykonania w swoim blogu (z mylącym tytułem), która mówi o usuwaniu znaków diakrytycznych: Stripping to interesująca praca (aka W znaczeniu nieistotnych, czyli wszystkich znaków Mn są nierozstawne, ale niektóre są bardziej nierozstawne niż inne)

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

Zauważ, że jest to kontynuacja jego wcześniejszego postu: Usuwanie diakrytów ....

Podejście wykorzystuje String.Normalize, aby podzielić ciąg wejściowy na glify składowe (w zasadzie oddzielając znaki „bazowe” od znaków diakrytycznych), a następnie skanuje wynik i zachowuje tylko znaki podstawowe. To tylko trochę skomplikowane, ale tak naprawdę patrzysz na skomplikowany problem.

Oczywiście, jeśli ograniczasz się do francuskiego, prawdopodobnie możesz uciec od prostego podejścia opartego na tabeli w Jak usunąć akcenty i tyldę w std :: string C ++ , zgodnie z zaleceniem @David Dibben.


32
To jest źle. Niemieckie znaki ä i ö i ü są latynizowane jako ae ue i oe, a nie jako ou ...
Stefan Steiger

20
Również polska litera ł jest ignorowana.
Zbigniew Wiadro,

4
Również Norse ø jest ignorowane
Richard de Wit

28
@StefanSteiger Wiesz, w języku czeskim są litery takie jak áčěů, które zwykle „latynizujemy” do aceu, nawet jeśli brzmi to inaczej i może powodować zamieszanie w słowach takich jak „hrábě” / hra: bje /, „hrabě” / hrabje /, i „hrabe” / hrabe /. Wydaje mi się, że usunięcie znaków diakrytycznych jest kwestią czysto graficzną, niezależną od fonetyki lub historii listu. Listy takie jak ä ö ü zostały utworzone przez dodanie indeksu górnego „e” do liter podstawowych, a zatem rozkład „ae” ma sens historyczny. Zależy to od celu - usunięcia znaków graficznych lub rozłożenia litery na znaki ASCII.
IllidanS4 chce, aby Monica wróciła

10
Ta funkcja jest niezależna od języka. Nie wie, czy napis jest w języku niemieckim czy w innym języku. Jeśli weźmiemy pod uwagę, że w tekście niemieckim można zastąpić ö przez oe, ale nie ma sensu robić tego z tureckim, to przekonamy się, że bez wykrycia języka ten problem nie jest tak naprawdę do rozwiązania.
thorn̈

163

to załatwiło sprawę dla mnie ...

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

szybkie i krótkie!


9
To jak dotąd najlepsza metoda, jaką widziałem.
Cleiton

2
Podoba mi się to rozwiązanie i działa dobrze w przypadku aplikacji ze Sklepu Windows. Jednak nie działa w przypadku aplikacji Windows Phone, ponieważ kodowanie ISO-8859-8 nie wydaje się być dostępne. Czy zamiast tego można zastosować inne kodowanie?
Philip Colmer,

2
Będzie to działać dla większości popularnych znaków, ale wiele znaków specjalnych, takich jak « »i (jako jeden znak), zostanie zmienionych w procesie, co nie jest zgodne z przyjętym rozwiązaniem.
The_Black_Smurf

7
Pamiętaj, że to nie działa w systemie .NET Core w systemie Linux:System.ArgumentException: 'ISO-8859-8' is not a supported encoding name.
EM0

2
Jeśli korzystasz z .NET Core, zainstaluj System.Text.Encoding.CodePagesz nuget, a następnie zadzwoń, aby zarejestrować dostawcę: Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);- gdy to zrobisz, możesz skorzystać z ISO-8859-8
SpaceBison

32

Gdyby ktoś był zainteresowany, szukałem czegoś podobnego i skończyłem pisać:

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
Powinieneś wstępnie przydzielić bufor StringBuilder do nazwy, aby zminimalizować obciążenie alokacji pamięci. Ostatnie połączenie Split / Join w celu usunięcia sekwencyjnego duplikatu _ jest interesujące. Być może powinniśmy po prostu unikać dodawania ich w pętli. Ustaw flagę dla poprzedniego znaku będącego _ i nie emituj go, jeśli jest prawdziwy.
IDisposable

2 naprawdę dobre punkty, przepiszę je, jeśli kiedykolwiek zdążę wrócić do tej części kodu :)
Luk

Miły. Oprócz komentarza IDisposables, powinniśmy prawdopodobnie sprawdzić c < 128, aby upewnić się, że nie pobieramy żadnych znaków UTF, zobacz tutaj .
Christian Gollhardt,

1
Lub prawdopodobnie bardziej wydajnie c < 123. patrz ASCI
Christian Gollhardt,

wciąż nie działa z
znakami

27

Potrzebowałem czegoś, co konwertuje wszystkie główne znaki Unicode, a głosowana odpowiedź pozostawiła kilka, więc stworzyłem wersję CodeIgniter's convert_accented_characters($str)w języku C #, który można łatwo dostosować:

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

Stosowanie

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

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

5
Wdrożenie wykonuje zadanie, ale należy je poprawić przed użyciem w kodzie produkcyjnym.
Pierre Arnaud,


dlaczego nie po prostu zamienić to if (entry.Key.IndexOf(c) != -1)naif (entry.Key.Contains(c))
Paweł Cioch

Dlaczego nie użyć ponownie RemoveDiacritics (char c) w pętli, dlaczego nie użyć StringBuilder. Głosuję za złożonym słownikiem i działającym rozwiązaniem, ale kod może być znacznie prostszy
Paweł Cioch

1
Nie rozumiem, dlaczego tyle skakania do obręczy można użyć { "äæǽ", "ae" }zamiast po { "ä", "ae" }, { "æ", "ae" }, { "ǽ", "ae" }prostu dzwonić if (foreign_characters.TryGetValue(...)) .... Całkowicie pokonałeś cel indeksu, który już ma słownik.
Bacon Bits

15

Jeśli ktoś jest zainteresowany, oto odpowiednik 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
zamiast pozbawionego + = nrml.charAt (i) użyj StringBuilder. masz tutaj ukryte środowisko uruchomieniowe O (n²).
Andreas Petersson,

6
Ta i inne odpowiedzi Javy tutaj są po prostu bałaganem w tym wątku. Pytanie dotyczy c # (.NET) nie Java!
suchoss

15

Często używam metody rozszerzenia opartej na innej wersji, którą znalazłem tutaj (zobacz Zastępowanie znaków w C # (ascii) ) Szybkie wyjaśnienie:

  • Normalizacja do postaci D dzieli znaki takie jak è na e i na spację `
  • Z tego usuwane są postacie nospace
  • Wynik jest znormalizowany z powrotem do postaci C (nie jestem pewien, czy jest to konieczne)

Kod:

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 Greek (ISO) może to zrobić

Informacje o tej stronie kodowej znajdują się w System.Text.Encoding.GetEncodings(). Dowiedz się więcej na: https://msdn.microsoft.com/pt-br/library/system.text.encodinginfo.getencoding(v=vs.110).aspx

Grecki (ISO) ma stronę kodową 28597 i nazwę iso-8859-7 .

Idź do kodu ... \ 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"

Napisz więc tę funkcję ...

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

Zauważ, że ... Encoding.GetEncoding("iso-8859-7")jest równoważne, Encoding.GetEncoding(28597)ponieważ pierwszy to nazwa, a drugi strona kodowa Kodowania.


3
To wspaniale! Krótki i wydajny!
krlzlx

1
Świetne rzeczy. Prawie wszystkie postacie, które testowałem, zdały. ( äáčďěéíľľňôóřŕšťúůýž ÄÁČĎĚÉÍĽĽŇÔÓŘŔŠŤÚŮÝŽ ÖÜË łŁđĐ ţŢşŞçÇ øı). Problemy znaleziono tylko w przypadku ßə, na które są konwertowane ?, ale takie wyjątki zawsze można rozwiązać osobno. Przed wprowadzeniem go do produkcji należy lepiej wykonać test na wszystkich obszarach Unicode zawierających litery ze znakami diakrytycznymi.
miroxlav,

5

To zabawne, że takie pytanie może uzyskać tak wiele odpowiedzi, a jednak żadne nie spełnia moich wymagań :) Jest tak wiele języków wokół, rozwiązanie agnostyczne w pełnym języku jest AFAIK, nie jest tak naprawdę możliwe, jak inni wspominali, że FormC lub FormD powodują problemy.

Ponieważ pierwotne pytanie dotyczyło języka francuskiego, odpowiedź jest najprostsza

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

1251 należy zastąpić kodowaniem języka wejściowego.

Zastępuje to jednak tylko jeden znak jednym znakiem. Ponieważ pracuję również z niemieckim jako danymi wejściowymi, dokonałem ręcznej konwersji

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

Może nie zapewniać najlepszej wydajności, ale przynajmniej jest bardzo łatwy do odczytania i rozszerzenia. Regex jest NO GO, znacznie wolniejszy niż jakikolwiek ciąg znaków / znaków.

Mam również bardzo prostą metodę usuwania miejsca:

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

W końcu używam kombinacji wszystkich 3 powyższych rozszerzeń:

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

I mały test jednostkowy do tego (nie wyczerpujący), który pomyślnie przeszedł.

    [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

Działa to dobrze w Javie.

Zasadniczo konwertuje wszystkie znaki akcentowane na ich odpowiedniki deAccented, a następnie łączą znaki diakrytyczne. Teraz możesz użyć wyrażenia regularnego, aby usunąć znaki diakrytyczne.

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

Lub w Javie 7,"\\p{Block=CombiningDiacriticalMarks}"
Brent Faust

11
Dlaczego warto opublikować rozwiązanie Java, gdy pytanie dotyczy konkretnie platformy .NET?
David

2
@David To pytanie jest największym hitem w Google pod kątem „akcentów upuszczenia Java”. Nie mówię, że należy tutaj, ale jest przydatny tutaj.
blubb

3

TL; DR - C # metoda przedłużenia łańcucha

Myślę, że najlepszym rozwiązaniem, aby zachować sens napisu jest konwersja znaków zamiast ich usuwania, co dobrze ilustruje przykład crème brûléedo crme brlewersetach creme brulee.

Sprawdziłem powyższy komentarz Aleksandra i zobaczyłem, że kod Lucene.Net ma licencję Apache 2.0, więc zmodyfikowałem klasę do prostej metody rozszerzenia łańcucha. Możesz użyć tego w następujący sposób:

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

Ta funkcja jest zbyt długa, aby opublikować ją w odpowiedzi StackOverflow (~ 139 tys. Znaków z 30 tys. Dozwolonych lol), więc sporządziłem listę i przypisałem autorom :

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

Mam nadzieję, że pomoże to komuś innemu. To najbardziej niezawodne rozwiązanie, jakie znalazłem!


Zastrzeżenia: 1) Koncepcja zależy od lokalizacji. Na przykład „ä” może być „a” lub „aa”. 2) Błędnie nazwany / źle opisany: Wynik niekoniecznie pochodzi tylko z C0 Controls i podstawowego bloku Latin. Konwertuje tylko litery łacińskie i niektóre warianty symboli na „odpowiedniki”. (Oczywiście, można by potem przejść inną operację, aby zastąpić lub usunąć znaki kontrolne inne niż C0 i podstawowe znaki bloku Latin). Ale to zrobi to, co robi dobrze.
Tom Blodget

2

TO JEST WERSJA VB (współpracuje z GRECKIM):

Importuje System.Text

Importuje system. Globalizacja

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
Może to być stara odpowiedź, ale dlaczego używasz osobnych wierszy do deklaracji zmiennych i pierwszego przypisania?
NiKiZe

2

Wypróbuj pakiet HelperSharp .

Istnieje metoda 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

W ten sposób zamieniam znaki diakrytyczne na znaki niediakrytyczne w całym moim programie .NET

DO#:

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

Używanie NFD zamiast NFC spowodowałoby zmiany znacznie wykraczające poza te, o które proszono.
Jon Hanna

1

Co ta osoba powiedziała:

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

W rzeczywistości dzieli takie, w åktórych jeden znak (który jest kodem znaków 00E5, a nie 0061 modyfikator, 030Aktóry wyglądałby tak samo) na aplus jakiś modyfikator, a następnie konwersja ASCII usuwa modyfikator, pozostawiając jedyny a.


1

Bardzo podoba mi się zwięzły i funkcjonalny kod dostarczony przez azrafe7 . Więc zmieniłem to trochę, aby przekonwertować na metodę rozszerzenia:

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

To jedyna metoda, która działa ze wszystkimi polskimi znakami diakrytycznymi. Zaakceptowana odpowiedź nie działa ze znakami Ł i Ł.
yarecky

-3

Nie mając wystarczającej reputacji, najwyraźniej nie mogę skomentować doskonałego linku Aleksandra. - Lucene wydaje się być jedynym rozwiązaniem działającym w uzasadnionych przypadkach ogólnych.

Dla tych, którzy chcą prostego rozwiązania kopiuj-wklej, oto kod wykorzystujący kod w Lucene:

testbed string = "ÁÂĘÅÇÇÍÍÎÓÖØÚÜÞàáâãäåæçèéêëìíîïðñóôöøúüāăčĐęğıŁłńŌōřŞşšźžșțệủ";

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;
    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.