Как удалить диакритические знаки (диакритические знаки) из строки в .NET?

Я пытаюсь преобразовать некоторые строки на французском канадском языке, и в основном я хотел бы иметь возможность убрать французские знаки ударения в буквах, сохранив букву. (Например, преобразовать é в e, чтобы crème brûlée стал creme brulee)

Как лучше всего этого добиться?

Предупреждение: этот подход может работать в некоторых конкретных случаях, но в целом вы не можете просто удалить диакритические знаки. В некоторых случаях и на некоторых языках это может изменить смысл текста. Вы не говорите, почему хотите это сделать; если это делается для сравнения строк или поиска, вам, скорее всего, будет лучше использовать для этого библиотеку с поддержкой Unicode.

JacquesB 30.10.2008 13:48

Поскольку большинство методов для достижения этого основаны на нормализации Unicode, этот документ, описывающий стандарт, может быть полезно прочитать: unicode.org/reports/tr15

LuddyPants 17.12.2013 03:50

Я думаю, что команда Azure устранила эту проблему, я попытался загрузить файл с таким именем «Mémo de la réunion.pdf», и операция прошла успешно.

Rady 27.12.2016 22:32
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
459
3
224 778
20
Перейти к ответу Данный вопрос помечен как решенный

Ответы 20

Ответ принят как подходящий

Я не использовал этот метод, но Майкл Каплан описывает метод для этого в своем сообщении в блоге (с запутанным заголовком), в котором говорится об удалении диакритических знаков: Стриптиз - интересная работа (она же О значении бессмысленного, ака Все Mn символов не имеют пробелов, но некоторые из них больше без пробелов, чем другие)

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

Обратите внимание, что это продолжение его более ранней публикации: Удаление диакритических знаков ....

Подход использует String.Normalize для разделения входной строки на составляющие глифы (в основном отделяя «базовые» символы от диакритических знаков), а затем сканирует результат и сохраняет только базовые символы. Это немного сложно, но на самом деле вы сталкиваетесь с сложной проблемой.

Конечно, если вы ограничиваетесь французским, вам, вероятно, удастся обойтись простым табличным подходом в Как убрать акценты и тильду в C++ std :: string, как рекомендовано @David Dibben.

Спасибо, помечено как ответ. В основном моему приложению нужно было взять заголовок раздела для веб-сайта и изменить его на «имя просмотра», в котором наше флэш-приложение могло связать его с нашей панелью навигации. это именно то, что мне нужно.

James Hall 30.10.2008 16:52

Идеальное решение ... намного лучше, чем очистка на основе регулярных выражений, подобная этой myownpercept.com/2009/06/replacing-special-chars-string-csha‌ rp

Jalal El-Shaer 08.07.2010 02:37

Почему необходимо выполнять каноническую композицию на результате: sb.ToString (). Normalize (NormalizationForm.FormC)? Отбросив метки без пробелов, разве нельзя просто вернуть sb.ToString ()? Или мне что-то здесь не хватает?

Simon Tewsi 16.07.2012 03:30

@SimonTewsi Сразу же возникает одна вещь: это будет означать, что у вас есть слоги хангыль, а не соединение Jamo, которое, помимо того, что требуется для NFC, также имеет лучшую поддержку шрифтов на практике.

Jon Hanna 03.09.2012 23:23

Это не правильно. Немецкие иероглифы ä, ö и ü латинизируются как ae ue и oe, а не как a, o u ...

Stefan Steiger 20.11.2012 17:41

Также игнорируется польская буква ł.

Chris W 12.12.2012 13:05

Обе ссылки блога michkap мертвы.

Matthieu 06.12.2013 00:45

Также игнорируется норвежский ø

Richard de Wit 25.03.2014 12:09

Это зависит от настроек персонализации ПК? На моем английском рабочем столе этот метод работает. Для ввода ä, ö, ü, ø я получаю вывод a, o, u, o.

a.farkas2508 15.07.2015 15:42

@ a.farkas2508: Да, но это неверно. ä должно быть ae, ö должно быть oe и ü должно быть ue, но на самом деле это a, o, u ...

Stefan Steiger 04.08.2015 12:21

@StefanSteiger Опишите, почему ä должно быть ae вместо a. Не должно ли разлагаться до ae?

IS4 03.11.2015 15:09

@ IllidanS4: Это распространенное очень древнее соглашение, согласно которому, когда вы «латинируете» немецкие символы ä, ö или ü, вы используете символ без ¨ и добавляете e; ä становится ae, ö становится oe, а ü становится ue. Точно так же, когда вы используете эти «комбинированные гласные» в немецком тексте, они произносятся как ä ö ü. æ наверное тоже надо разложить до ae. С другой стороны, какой бы ни был NormalizationForm.FormC;) Возможно, изменение формы поможет.

Stefan Steiger 11.11.2015 22:39

@StefanSteiger Знаете, в чешском есть такие буквы, как áčěů, которые мы обычно латинизируем до aceu, хотя это звучит по-другому и может вызывать путаницу в таких словах, как «hrábě» / hra: bje /, «hrabě» / hrabje /, и «грабе» / грабе /. Мне кажется, что удаление диакритических знаков - чисто графический вопрос, не зависящий от фонетики или истории письма. Такие буквы, как ä ö ü, были созданы добавлением надстрочного индекса «е» к основным буквам, таким образом, разложение «ае» имеет исторический смысл. Это зависит от цели - удалить графические метки или разложить букву на символы ASCII.

IS4 11.11.2015 23:25

Эта функция не зависит от языка. Он не знает, написана ли строка на немецком или на другом языке. Если мы примем во внимание, что можно заменить ö на oe в немецком тексте, но нет никакого смысла делать это с турецким, то мы увидим, что без определения языка эта проблема на самом деле не разрешима.

thorn̈ 04.08.2016 15:02

Таким образом, это решение не работает для норвежского ø, польского ł, турецкого ı, азербайджанского ə и т. д. В основном, для любых букв без разделительных диакритических знаков.

thorn̈ 04.08.2016 15:26

@thorn: Ага, и это неправильно для немецкого языка - если вы не считаете ä "графически правильным";) Но это может быть хорошим способом нормализовать турецкий i.

Stefan Steiger 29.03.2017 19:14

Другой неработающий пример: Volaa (название альбома «Volaa Land» от художника Draugsól). Не уменьшается до o.

PeterCo 16.04.2017 18:59

не забудьте добавить using System; using System.Globalization; using System.Text; using System.Text.RegularExpressions;

Serge 24.05.2017 16:12

Я не думаю, что цель состоит в том, чтобы иметь идеально понятный способ его написания, а скорее в том, чтобы иметь что-то, что пройдет через большинство приложений. Я использую это для автоматизации, в основном для создания учетной записи и имени с диакритическими знаками и странными символами. Active Directory и многие другие приложения не очень хорошо воспринимают эти символы, поэтому при создании любой учетной записи я нормализую их к чему-то более «нормальному», например, для имени учетной записи.

Johnny Prescott 13.03.2018 17:05

По какой-то причине String.Normalize не работает на производственных сборках ?

Alexander 18.04.2018 16:24

См. Мой ответ ниже для метода расширения строки, который более подробно описывает, как и что он преобразует: stackoverflow.com/a/56797567/479701

Andy Raddatz 03.07.2019 01:05

@StefanSteiger: эта функция не предназначена для «латинизации» или «транслитерации» символов. Он просто предназначен для выполнения того, что он назвал - «RemoveDiacritics». То, чего вы от него ожидаете, - это совсем другая проблема. Ответ CIRCLE ниже может помочь вам больше.

derekantrican 22.10.2019 18:55

после normalizedString мы можем просто вернуться в одном плавном синтаксисе, таком как этот return normalizedString.Aggregate(new StringBuilder(), (c,e) => CharUnicodeInfo.GetUnicodeCategory(e) == UnicodeCategory.NonSpacingMark ? c : c.Append(e)) .ToString().Normalize(NormalizationForm.FormC);

King King 11.10.2020 06:48

Если кому-то интересно, вот 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();
    }
}

вместо stripped + = nrml.charAt (i) используйте StringBuilder. у вас здесь спрятано время выполнения O (n²).

Andreas Petersson 09.09.2009 12:50

Этот и другие ответы Java здесь - это просто беспорядок в этой теме. Вопрос касается C# (.NET), а не Java!

suchoss 14.11.2019 13:28

Если кому-то интересно, я искал что-то подобное и закончил писать следующее:

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
}

Вы должны предварительно выделить буфер StringBuilder для name.Length, чтобы минимизировать накладные расходы на выделение памяти. Интересен последний вызов Split / Join для удаления последовательного дубликата _. Возможно, нам просто не следует добавлять их в цикл. Установите флаг для предыдущего символа, являющегося _, и не выдавать его, если он истинен.

IDisposable 02.09.2009 01:18

2 действительно хороших момента, я перепишу его, если когда-нибудь у меня будет время вернуться к этой части кода :)

Luk 08.09.2009 13:14

Хороший. В дополнение к комментарию IDisposables нам, вероятно, следует проверить наличие c < 128, чтобы убедиться, что мы не забираем какие-либо символы UTF, глянь сюда.

Christian Gollhardt 05.01.2018 20:48

Или, возможно, более эффективно c < 123. см. ASCI

Christian Gollhardt 05.01.2018 20:56

Это отлично работает в java.

Он в основном преобразует все акцентированные символы в их деакцентированные аналоги, за которыми следуют их комбинированные диакритические знаки. Теперь вы можете использовать регулярное выражение, чтобы убрать диакритические знаки.

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

Или в Java 7 "\\p{Block=CombiningDiacriticalMarks}"

Brent Faust 19.05.2015 23:18

Зачем размещать решение Java, если вопрос конкретно касается .NET?

David 17.04.2017 18:42

@David Этот вопрос является самым популярным в Google по вопросу "java drop acnts". Не говорю, что это здесь место, но здесь полезно.

blubb 23.03.2018 14:42

это помогло мне ...

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

быстро и коротко!

почему вы используете ISO-8859-8 для кодирования строки и UTF8 для ее декодирования? Не лучше ли использовать ISO-8859-8 для декодирования массива байтов?

Dominik Weber 11.10.2013 09:09

@DominikvonWeber, это всего лишь небольшая «проблема», но решение намного лучше, чем то, за которое проголосовали наибольшее количество голосов. этот, по крайней мере, работает для таких символов, как ł, ą, ó и т. д. :)

Gutek 31.10.2013 13:26

Это лучший метод, который я когда-либо видел.

Cleiton 31.01.2014 07:27

Это работает для ł и ø, в отличие от тех, которые используют Normalize () в C#.

Noble_Bright_Life 24.04.2014 09:01

Мне нравится это решение, и оно хорошо работает для приложений Магазина Windows. Однако это не работает для приложений Windows Phone, поскольку кодировка ISO-8859-8, похоже, недоступна. Можно ли вместо этого использовать другую кодировку?

Philip Colmer 28.07.2014 10:58

Я видел этот код следующим образом: tempBytes = Encoding.GetEncoding(1251).GetBytes(s); asciiStr = Encoding.ASCII.GetString(tempBytes); Мне кажется, что исходное кодирование сильно связано с используемой системой кодирования по умолчанию (к сожалению, это может отличаться в среде разработки и продукта).

Matt Stuvysant 18.05.2015 19:12

Это будет работать для большинства распространенных символов, но многие специальные символы, такие как «» и (как один символ), будут изменены в процессе, что не относится к принятому решению.

The_Black_Smurf 24.03.2017 22:23

Обратите внимание, что это не работает в .NET Core в Linux: System.ArgumentException: 'ISO-8859-8' is not a supported encoding name.

EM0 22.02.2018 13:30

Если вы используете .NET Core, установите System.Text.Encoding.CodePages из nuget, затем вызовите это, чтобы зарегистрировать поставщика: Encoding.RegisterProvider(CodePagesEncodingProvider.Instance‌​); - как только вы это сделаете, вы можете использовать ISO-8859-8

SpaceBison 23.01.2020 18:34

Для UTF8 это работает, но как мы обрабатываем источник в кодировке ANSI?

Michel van Engelen 27.03.2020 12:00

Отлично, может кто-нибудь посоветует мне, как его использовать на StringBuilder? Пытался использовать, как показано ниже, но, похоже, не работает. StringBuilder sb = новый StringBuilder (); byte [] tempBytes; tempBytes = System.Text.Encoding.GetEncoding («ISO-8859-8»). GetBytes (sb); строка asciiStr = System.Text.Encoding.UTF8.GetString (tempBytes); sb.AppendLine (MyDataHeader); sb.AppendLine (MyDataString);

Kelvin Morel 18.02.2021 09:04

ЭТО ВЕРСИЯ VB (работает с GREEK):

Импортирует System.Text

Система импорта. Глобализация

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

Может быть, старый ответ, но почему вы используете отдельные строки для объявления переменных и первого присваивания?

NiKiZe 12.03.2018 15:51

Я часто использую метод расширения, основанный на другой версии, которую я нашел здесь (см. Замена символов в C# (ascii)) Краткое объяснение:

  • Нормализация для формирования D разбивает символы, такие как è, на е и нераспространяющийся `
  • Отсюда убираются символы без пробела.
  • Результат нормализуется обратно в форму C (я не уверен, что это необходимо)

Код:

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

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

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

        return cleanStr;
    }

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

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

Попробуйте Пакет HelperSharp.

Есть метод RemoveAccents:

 public static string RemoveAccents(this string source)
 {
     //8 bit characters 
     byte[] b = Encoding.GetEncoding(1251).GetBytes(source);

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

Вот как я заменяю диакритические символы на недиакритические во всех моих программах .NET.

C#:

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

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

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

VB .NET:

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

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

вы можете использовать расширение строки из пакета nuget MMLib.Extensions:

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

Страница Nuget: https://www.nuget.org/packages/MMLib.Extensions/ Сайт проекта Codeplex https://mmlib.codeplex.com/

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

Использование NFD вместо NFC приведет к изменениям, выходящим далеко за рамки запрошенных.

Jon Hanna 22.06.2015 17:04

Что сказал этот человек:

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

Фактически он разделяет подобные å, состоящие из одного символа (это код символа 00E5, нет0061 плюс модификатор 030A, который будет выглядеть одинаково) на a плюс какой-то модификатор, а затем преобразование ASCII удаляет модификатор, оставляя единственный a.

Мне нужно было что-то, что преобразует все основные символы Unicode, и проголосовавший ответ оставил некоторые из них, поэтому я создал версию CodeIgniter convert_accented_characters($str) на C#, которая легко настраивается:

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

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

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

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


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

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

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

использование

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

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

Ваша реализация выполняет свою работу, но ее следует улучшить перед использованием в производственном коде.

Pierre Arnaud 26.04.2016 07:05
github.com/apache/lucenenet/blob/master/src/…
Alexander 18.04.2018 23:31

почему бы просто не заменить этот if (entry.Key.IndexOf(c) != -1) на if (entry.Key.Contains(c))

Pawel Cioch 15.04.2019 21:12

Почему бы не использовать повторно RemoveDiacritics (char c) в цикле, почему бы не использовать StringBuilder. Я голосую за сложный словарь и рабочее решение, но код мог бы быть намного проще

Pawel Cioch 15.04.2019 21:16

Это решение предполагает, что вы знаете, что нужно обрабатывать диакритический знак ... Предполагая, что вы запускаете это со списком из многих имен, вам потребуется дополнительная логика для идентификации этих имен диакритическим знаком, чтобы добавить в это решение.

Dscoduc 24.06.2019 22:54

Я использовал ссылку @ Alexander, чтобы ответить ниже: stackoverflow.com/a/56797567/479701

Andy Raddatz 27.06.2019 22:51

Я не понимаю, почему так много обручей, чтобы использовать { "äæǽ", "ae" } вместо { "ä", "ae" }, { "æ", "ae" }, { "ǽ", "ae" } и просто звонить if (foreign_characters.TryGetValue(...)) .... Вы полностью лишили смысла индекс, который уже есть в словаре.

Bacon Bits 28.08.2019 15:39

"иностранный"? Как грубо! Может быть, чужое для тебя! Также разные языки имеют разные правила.

Asteroids With Wings 24.03.2020 22:11

Могу ли я использовать это в своем личном проекте?

Jotarata 09.04.2020 00:31

@Jotarata конечно;)

CIRCLE 09.04.2020 03:21

@BaconBits Еще лучше было бы использовать в качестве типа ключа char, а не строку.

Stewart 09.03.2021 20:08

CodePage Греческий (ISO) может это сделать

Информация об этой кодовой странице находится в System.Text.Encoding.GetEncodings(). Подробнее об этом: https://msdn.microsoft.com/pt-br/library/system.text.encodinginfo.getencoding(v=vs.110).aspx

Греческий (ISO) имеет кодовую страницу 28597 и имя iso-8859-7.

Перейти к коду ... \ o /

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

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

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

Итак, напишите эту функцию ...

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

Обратите внимание, что ... Encoding.GetEncoding("iso-8859-7") эквивалентен Encoding.GetEncoding(28597), потому что первое - это имя, а второе - кодовая страница кодирования.

Это блестяще! Коротко и эффективно!

krlzlx 02.09.2016 15:14

Отличный материал. Практически все персонажи, которые я тестировал, прошли успешно. (äáčďěéíľľňôóřŕšťúůýž ÄÁČĎĚÉÍĽĽŇÔÓŘŔŠŤÚŮÝŽ ÖÜË łŁđĐ ţŢşŞçÇ øı). Проблемы были обнаружены только с ßə, которые преобразованы в ?, но такие исключения всегда можно обрабатывать отдельно. Прежде чем запускать это в производство, лучше провести тест для всех областей Unicode, содержащих буквы с диакритическими знаками.

miroxlav 03.12.2016 17:18

Забавно, что на такой вопрос можно получить так много ответов, но ни один из них не соответствует моим требованиям :) Существует так много языков, полное языковое независимое решение, AFAIK, на самом деле невозможно, поскольку другие упоминали, что FormC или FormD вызывают проблемы.

Поскольку исходный вопрос был связан с французским языком, самый простой рабочий ответ действительно

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

1251 следует заменить кодом кодировки языка ввода.

Однако это заменяет только один символ одним символом. Поскольку я также работаю с немецким языком в качестве ввода, я сделал ручное преобразование

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

Возможно, он не обеспечивает лучшую производительность, но, по крайней мере, его очень легко читать и расширять. Regex - это НЕЛЬЗЯ, намного медленнее, чем любые символы char / string.

У меня также есть очень простой способ удалить пробел:

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

В конце концов, я использую комбинацию всех трех вышеуказанных расширений:

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

И небольшой юнит-тест для тех (не исчерпывающих), которые прошли успешно.

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

Мне очень нравится лаконичный и функциональный код, предоставляемый azrafe7. Итак, я немного изменил его, чтобы преобразовать в метод расширения:

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

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

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

Это единственный метод, который работает со всеми польскими диакритическими знаками. Принятый ответ не работает с символами Ł и ł.

yarecky 02.05.2020 11:17

Добавьте сюда эту библиотеку, если вы еще не думали об этом. Похоже, с ним есть полный набор юнит-тестов.

https://github.com/thomasgalliker/Diacritics.NET

Не имея достаточной репутации, видимо не могу комментировать отличную ссылку Александра. - Lucene кажется единственным решением, работающим в достаточно общих случаях.

Для тех, кто хочет простое решение для копирования и вставки, вот оно, используя код в Lucene:

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

Console.WriteLine (Lucene.latinizeLucene (стенд));

AAAACEIIOOOUUTHaaaaaaaeceeeeiiiidnoooouuaacDegiLlnOorSsszzsteu

//////////

public static class Lucene
{
    // source: https://raw.githubusercontent.com/apache/lucenenet/master/src/Lucene.Net.Analysis.Common/Analysis/Miscellaneous/ASCIIFoldingFilter.cs
    // idea: https://stackoverflow.com/questions/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;
    }
}

TL; DR - C# метод расширения строки

Я думаю, что лучшим решением для сохранения значения строки является преобразование символов, а не их удаление, что хорошо проиллюстрировано в примере crème brûlée в crme brle против creme brulee.

Я проверил Комментарий Александра выше и увидел, что код Lucene.Net имеет лицензию Apache 2.0, поэтому я преобразовал класс в простой метод расширения строки. Вы можете использовать это так:

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

Функция слишком длинная для публикации в ответе StackOverflow (~ 139 тыс. Символов из 30 тыс. Разрешенных lol), поэтому Я сформулировал суть и приписал авторов:

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

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

Надеюсь, это поможет кому-то другому, это самое надежное решение, которое я нашел!

Предостережения: 1) Концепция зависит от локали. Например, «ä» может быть «а» или «аа». 2) Неправильно названо / неправильно описано: результат не обязательно только из блока C0 Controls и Basic Latin. Он преобразует только латинские буквы и некоторые варианты символов в «эквиваленты». (Конечно, потом можно было бы выполнить еще один проход, чтобы заменить или удалить не-C0 Controls и символы основного латинского блока.) Но это будет делать то, что делает хорошо.

Tom Blodget 28.06.2019 00:50

Спасибо, что разместили это там. Я полагаю, у вас есть завершающая скобка } в конце файла.

Superman.Lopez 25.09.2020 11:00

Этот код работал у меня:

var updatedText = text.Normalize(NormalizationForm.FormD)
     .Where(c => CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
     .ToArray();

Однако, пожалуйста, не делайте этого с именами. Это не только оскорбление людей с умляутами / акцентами в своем имени, это также может быть опасно неправильным в определенных ситуациях (см. Ниже). Есть альтернативные варианты написания вместо того, чтобы просто убрать акцент.

Более того, это просто неправильно и опасно, например если пользователь должен указать свое имя именно так, как оно указано в паспорте.

Например, мое имя написано Zuberbühler, а в машиночитаемой части моего паспорта вы найдете Zuberbuehler. Если убрать умлаут, имя не будет совпадать ни с одной из частей. Это может вызвать проблемы у пользователей.

Лучше запретить умляуты / акцент в форме ввода для имен, чтобы пользователь мог правильно написать свое имя без умляута или акцента.

Практический пример: если веб-служба для подачи заявки на ESTA (https://www.application-esta.co.uk/special-characters-and) будет использовать приведенный выше код вместо правильного преобразования умляутов, заявка ESTA будет либо отклонена, либо у путешественника возникнут проблемы с американским пограничным контролем при въезде в Штаты.

Другой пример - авиабилеты. Предполагая, что у вас есть веб-приложение для бронирования авиабилетов, пользователь указывает свое имя с акцентом, а ваша реализация просто убирает акценты, а затем использует веб-службу авиакомпании для бронирования билета! Вашему клиенту может быть отказано в посадке, поскольку имя не соответствует ни одной части его / ее паспорта.

Другие вопросы по теме