Wie entferne ich diakritische Zeichen (Akzente) aus einer Zeichenfolge in .NET?

432

Ich versuche, einige Zeichenfolgen in Französisch-Kanadisch umzuwandeln, und im Grunde möchte ich in der Lage sein, die französischen Akzentzeichen in den Buchstaben zu entfernen, während der Buchstabe beibehalten wird. (ZB konvertieren ézu e, crème brûléewürde also werden creme brulee)

Was ist die beste Methode, um dies zu erreichen?

James Hall
quelle
14
Eine Warnung: Dieser Ansatz funktioniert möglicherweise in bestimmten Fällen, aber im Allgemeinen können Sie nicht nur diakritische Zeichen entfernen. In einigen Fällen und in einigen Sprachen kann dies die Bedeutung des Textes ändern. Sie sagen nicht, warum Sie das tun wollen; Wenn Sie Zeichenfolgen vergleichen oder suchen möchten, ist es höchstwahrscheinlich besser, wenn Sie hierfür eine Unicode-fähige Bibliothek verwenden.
JacquesB
1
Da die meisten Techniken, um dies zu erreichen, auf der Unicode-Normalisierung beruhen, kann dieses Dokument, das den Standard beschreibt, nützlich sein: unicode.org/reports/tr15
LuddyPants
Ich denke, dass das Azure-Team dieses Problem behoben hat. Ich habe versucht, eine Datei mit dem Namen "Mémo de la réunion.pdf" hochzuladen, und der Vorgang war erfolgreich.
Rady

Antworten:

530

Ich habe diese Methode nicht verwendet, aber Michael Kaplan beschreibt eine Methode dafür in seinem Blog-Beitrag (mit einem verwirrenden Titel), in der es um das Entfernen von Diakritika geht: Das Entfernen ist ein interessanter Job (auch bekannt als bedeutungslos, auch bekannt als All Mn-Zeichen) sind nicht abstandsreich, aber einige sind abstandsloser als andere)

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

Beachten Sie, dass dies eine Fortsetzung seines früheren Beitrags ist: Diakritika entfernen ....

Der Ansatz verwendet String.Normalize , um die Eingabezeichenfolge in konstituierende Glyphen aufzuteilen (im Grunde genommen werden die "Basis" -Zeichen von den diakritischen Zeichen getrennt). Anschließend wird das Ergebnis gescannt und nur die Basiszeichen beibehalten . Es ist nur ein bisschen kompliziert, aber Sie sehen wirklich ein kompliziertes Problem.

Wenn Sie sich auf Französisch beschränken, könnten Sie wahrscheinlich mit dem einfachen tabellenbasierten Ansatz in Entfernen von Akzenten und Tilde in einem C ++ std :: string durchkommen, wie von @David Dibben empfohlen.

Blair Conrad
quelle
32
Das ist falsch. Deutsche Zeichen ä und ö und ü werden als ae ue und oe latinisiert und nicht als au ...
Stefan Steiger
20
Auch der polnische Buchstabe ł wird ignoriert.
Zbigniew Wiadro
4
Auch Norse ø wird ignoriert
Richard de Wit
28
@StefanSteiger Weißt du, auf Tschechisch gibt es Buchstaben wie áčěů, die wir normalerweise zu aceu "latinisieren", obwohl es anders klingt und Verwirrung in Wörtern wie "hrábě" / hra: bje /, "hrabě" / hrabje /, verursachen kann. und "hrabe" / hrabe /. Mir scheint, dass die Streichung von Diakritika eine rein grafische Angelegenheit ist, die unabhängig von der Phonetik oder der Geschichte des Briefes ist. Buchstaben wie ä ö ü wurden durch Hinzufügen eines hochgestellten "e" zu den Basisbuchstaben erstellt, daher ist die "ae" -Zerlegung historisch sinnvoll. Es kommt auf das Ziel an, die grafischen Markierungen zu entfernen oder den Buchstaben in ASCII-Zeichen zu zerlegen.
IllidanS4 will Monica
10
Diese Funktion ist sprachunabhängig. Es ist nicht bekannt, ob die Zeichenfolge in Deutsch oder in einer anderen Sprache vorliegt. Wenn wir berücksichtigen, dass es in einem deutschen Text in Ordnung ist, ö durch oe zu ersetzen, es aber keinen Sinn macht, dies mit Türkisch zu tun, werden wir feststellen, dass dieses Problem ohne Erkennung der Sprache nicht wirklich lösbar ist.
Dorn
162

das hat den Trick für mich getan ...

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

schnell & kurz!

azrafe7
quelle
9
Dies ist die bisher beste Methode, die ich je gesehen habe.
Cleiton
2
Ich mag diese Lösung und sie funktioniert gut für Windows Store Apps. Für Windows Phone Apps funktioniert dies jedoch nicht, da die Codierung ISO-8859-8 anscheinend nicht verfügbar ist. Gibt es eine andere Codierung, die stattdessen verwendet werden kann?
Philip Colmer
2
Dies funktioniert für die meisten gängigen Zeichen, aber viele Sonderzeichen wie « »und (als einzelnes Zeichen) werden dabei geändert, was bei der akzeptierten Lösung nicht der Fall ist.
The_Black_Smurf
7
Beachten Sie, dass dies unter .NET Core unter Linux nicht funktioniert:System.ArgumentException: 'ISO-8859-8' is not a supported encoding name.
EM0
2
Wenn Sie mit .NET Core arbeiten, installieren Sie es System.Text.Encoding.CodePagesvon Nuget und rufen Sie dies an, um den Anbieter zu registrieren: Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);- Sobald Sie dies getan haben, können Sie ISO-8859-8
SpaceBison
32

Falls jemand interessiert ist, habe ich nach etwas Ähnlichem gesucht und folgendes geschrieben:

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
}
Luk
quelle
9
Sie sollten den StringBuilder-Puffer dem Namen vorab zuordnen. Länge, um den Speicherzuweisungsaufwand zu minimieren. Der letzte Split / Join-Aufruf zum Entfernen von sequentiellen Duplikaten _ ist interessant. Vielleicht sollten wir es einfach vermeiden, sie in die Schleife aufzunehmen. Setzen Sie ein Flag für das vorherige Zeichen als _ und geben Sie kein Flag aus, wenn es wahr ist.
IDisposable
2 wirklich gute Punkte, ich werde es umschreiben, wenn ich jemals die Zeit bekomme, zu diesem Teil des Codes zurückzukehren :)
Luk
Nett. Zusätzlich zum Kommentar zu IDisposables sollten wir wahrscheinlich nachsehen c < 128, um sicherzustellen, dass keine UTF-Zeichen erfasst werden ( siehe hier) .
Christian Gollhardt
1
Oder wahrscheinlich effizienter c < 123. siehe ASCI
Christian Gollhardt
funktioniert immer noch nicht mit Zeichen wie ø
Juanora
27

Ich brauchte etwas, das alle wichtigen Unicode-Zeichen konvertiert, und die abgestimmte Antwort ließ einige aus, sodass ich eine Version von CodeIgniter convert_accented_characters($str)in C # erstellt habe, die leicht anpassbar ist:

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

Verwendungszweck

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

// for chars
"Ã"[0].RemoveDiacritics (); // A
KREIS
quelle
5
Ihre Implementierung erledigt den Job, sollte jedoch verbessert werden, bevor sie im Produktionscode verwendet wird.
Pierre Arnaud
warum nicht einfach ersetzen if (entry.Key.IndexOf(c) != -1)inif (entry.Key.Contains(c))
Pawel Cioch
Warum nicht RemoveDiacritics (char c) in der Schleife wiederverwenden, warum nicht StringBuilder verwenden? Ich stimme für ein komplexes Wörterbuch und eine funktionierende Lösung, aber der Code könnte viel einfacher sein
Pawel Cioch
1
Ich verstehe nicht, warum es so viel Reifenspringen gibt, { "äæǽ", "ae" }anstatt { "ä", "ae" }, { "æ", "ae" }, { "ǽ", "ae" }nur anzurufen if (foreign_characters.TryGetValue(...)) .... Sie haben den Zweck des Index, den das Wörterbuch bereits hat, vollständig besiegt.
Bacon Bits
15

Falls jemand interessiert ist, hier ist das Java-Äquivalent:

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();
    }
}
KenE
quelle
3
Verwenden Sie anstelle von stripped + = nrml.charAt (i) einen StringBuilder. Sie haben O (n²) Laufzeit hier versteckt.
Andreas Petersson
6
Diese und andere Java-Antworten hier sind nur ein Durcheinander dieses Threads. Frage ist über c # (.NET) nicht Java!
Suchoss
15

Ich verwende oft eine Erweiterungsmethode, die auf einer anderen Version basiert, die ich hier gefunden habe (siehe Ersetzen von Zeichen in C # (ascii) ). Eine kurze Erklärung:

  • Das Normalisieren zur Bildung von D teilt Zeichen wie è in ein e und ein nicht räumliches `
  • Daraus werden die Leerzeichen entfernt
  • Das Ergebnis wird wieder auf C normalisiert (ich bin mir nicht sicher, ob dies erforderlich ist).

Code:

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);
    }
}
Realbart
quelle
9

Die CodePage of Greek (ISO) kann das

Die Informationen zu dieser Codepage sind in System.Text.Encoding.GetEncodings(). Weitere Informationen finden Sie unter: https://msdn.microsoft.com/pt-br/library/system.text.encodinginfo.getencoding(v=vs.110).aspx

Griechisch (ISO) hat die Codepage 28597 und den Namen iso-8859-7 .

Gehe zum Code ... \ 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"

Also, schreibe diese Funktion ...

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

Beachten Sie, dass ... Encoding.GetEncoding("iso-8859-7")äquivalent zu ist, Encoding.GetEncoding(28597)da erstens der Name und zweitens die Codepage der Codierung ist.

Sergio Cabral
quelle
3
Das ist hervorragend! Kurz und effizient!
krlzlx
1
Tolles Zeug. Fast alle Charaktere, die ich getestet habe, haben bestanden. ( äáčďěéíľľňôóřŕšťúůýž ÄÁČĎĚÉÍĽĽŇÔÓŘŔŠŤÚŮÝŽ ÖÜË łŁđĐ ţŢşŞçÇ øı). Die Probleme wurden nur mit gefunden ßə, in die konvertiert wird ?, aber solche Ausnahmen können immer getrennt behandelt werden. Bevor dies in Produktion geht, sollte der Test besser gegen alle Unicode-Bereiche durchgeführt werden, die Buchstaben mit diakritischen Zeichen enthalten.
Miroxlav
5

Es ist lustig, dass eine solche Frage so viele Antworten bekommen kann und doch keine meinen Anforderungen entspricht :) Es gibt so viele Sprachen, eine vollständige sprachunabhängige Lösung ist AFAIK nicht wirklich möglich, da andere erwähnt haben, dass FormC oder FormD Probleme geben.

Da sich die ursprüngliche Frage auf Französisch bezog, ist die einfachste Antwort tatsächlich

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

1251 sollte durch den Codierungscode der Eingabesprache ersetzt werden.

Dies ersetzt jedoch nur ein Zeichen durch ein Zeichen. Da ich auch mit Deutsch als Eingabe arbeite, habe ich eine manuelle Konvertierung durchgeführt

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

Es liefert möglicherweise nicht die beste Leistung, ist aber zumindest sehr einfach zu lesen und zu erweitern. Regex ist ein NO GO, viel langsamer als jedes Char / String-Zeug.

Ich habe auch eine sehr einfache Methode, um Leerzeichen zu entfernen:

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

Schließlich verwende ich eine Kombination aller drei oben genannten Erweiterungen:

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

Und ein kleiner Unit-Test zu dem (nicht erschöpfenden), der erfolgreich besteht.

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

Dies funktioniert gut in Java.

Grundsätzlich werden alle Zeichen mit Akzent in ihre Gegenstücke mit Akzent umgewandelt, gefolgt von ihren kombinierten Diakritika. Jetzt können Sie eine Regex verwenden, um die Diakritika zu entfernen.

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("");
}
hashable
quelle
Oder in Java 7,"\\p{Block=CombiningDiacriticalMarks}"
Brent Faust
11
Warum sollten Sie eine Java-Lösung veröffentlichen, wenn die Frage speziell nach .NET fragt?
David
2
@ David Diese Frage ist der Top-Hit bei Google für "Java Drop-Akzente". Ich sage nicht, dass es hierher gehört, aber es ist hier nützlich.
Blubb
3

TL; DR - C # String-Erweiterungsmethode

Ich denke, die beste Lösung, um die Bedeutung der Zeichenfolge beizubehalten, besteht darin, die Zeichen zu konvertieren, anstatt sie zu entfernen, was im Beispiel gut dargestellt ist crème brûlée in crme brlevs.creme brulee .

Ich habe mir Alexanders Kommentar oben angesehen gelesen und festgestellt, dass der Lucene.Net-Code Apache 2.0-lizenziert ist. Daher habe ich die Klasse in eine einfache String-Erweiterungsmethode geändert. Sie können es so verwenden:

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

Die Funktion ist zu lang, um sie in einer StackOverflow-Antwort zu veröffentlichen (~ 139.000 Zeichen von 30.000 erlaubt lol), also habe ich einen Kern gemacht und die Autoren zugeschrieben :

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

Ich hoffe, das hilft jemand anderem. Dies ist die robusteste Lösung, die ich gefunden habe!

Andy Raddatz
quelle
Vorsichtsmaßnahmen: 1) Das Konzept ist vom Gebietsschema abhängig. Zum Beispiel könnte "ä" "a" oder "aa" sein. 2) Falsch benannt / falsch beschrieben: Das Ergebnis stammt nicht unbedingt nur aus dem Block C0 Controls und Basic Latin. Es werden nur lateinische Buchstaben und einige Symbolvarianten in "Äquivalente" konvertiert. (Natürlich könnte man einen weiteren Durchgang machen, um Nicht-C0-Steuerelemente und grundlegende lateinische Blockzeichen danach zu ersetzen oder zu entfernen.) Aber dies wird das tun, was es gut macht.
Tom Blodget
2

DAS IST DIE VB-VERSION (funktioniert mit GRIECHISCH):

Importiert System.Text

Importiert 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
Stefanos Michanetzis
quelle
1
Könnte eine alte Antwort sein, aber warum verwenden Sie separate Zeilen für die Variablendeklaration und die erste Zuweisung?
NiKiZe
2

Probieren Sie das HelperSharp-Paket aus .

Es gibt eine Methode 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;
 }
Giacomelli
quelle
2

So ersetze ich diakritische Zeichen in meinem gesamten .NET-Programm durch nicht diakritische Zeichen

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
Heyjee
quelle
2

Sie können die Zeichenfolgenerweiterung aus dem Nuget-Paket MMLib.Extensions verwenden:

using MMLib.RapidPrototyping.Generators;
public void ExtensionsExample()
{
  string target = "aácčeéií";
  Assert.AreEqual("aacceeii", target.RemoveDiacritics());
} 

Nuget-Seite: https://www.nuget.org/packages/MMLib.Extensions/ Codeplex-Projektseite https://mmlib.codeplex.com/

Mino
quelle
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
Tratak
quelle
Die Verwendung von NFD anstelle von NFC würde zu Änderungen führen, die weit über die angeforderten hinausgehen.
Jon Hanna
1

Was diese Person gesagt hat:

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

Tatsächlich werden solche wie åein Zeichen ( dh der Zeichencode 00E5, nicht 0061 plus der Modifikator 030A, der gleich aussehen würde) in aplus eine Art Modifikator aufgeteilt, und dann entfernt die ASCII-Konvertierung den Modifikator, wobei der einzige übrig bleibt a.

Gemeinschaft
quelle
1

Ich mag den prägnanten und funktionalen Code von azrafe7 sehr . Also habe ich es ein wenig geändert, um es in eine Erweiterungsmethode umzuwandeln:

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));
    }
}
Siavash Mortazavi
quelle
Dies ist die einzige Methode, die mit allen polnischen Diakritika funktioniert. Die akzeptierte Antwort funktioniert nicht mit den Zeichen Ł und ł.
Yarecky
-3

Ich habe nicht genug Ruf, anscheinend kann ich Alexanders exzellenten Link nicht kommentieren. - Lucene scheint die einzige Lösung zu sein, die in einigermaßen generischen Fällen funktioniert.

Für diejenigen, die eine einfache Copy-Paste-Lösung suchen, ist hier die Verwendung von Code in Lucene:

string 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;
    }
}
Adrian
quelle