Bagaimana cara menghapus diakritik (aksen) dari string di .NET?

433

Saya mencoba untuk mengkonversi beberapa string yang dalam bahasa Prancis Kanada dan pada dasarnya, saya ingin dapat mengeluarkan tanda aksen Prancis dalam huruf sambil menjaga surat itu. (Misalnya dikonversi émenjadi e, jadi crème brûléeakan menjadi creme brulee)

Apa metode terbaik untuk mencapai ini?

James Hall
sumber
14
Peringatan: Pendekatan ini mungkin berfungsi dalam beberapa kasus tertentu, tetapi secara umum Anda tidak bisa hanya menghapus tanda diakritik. Dalam beberapa kasus dan beberapa bahasa ini dapat mengubah arti teks. Anda tidak mengatakan mengapa Anda ingin melakukan ini; jika demi membandingkan string atau pencarian Anda kemungkinan besar lebih baik dengan menggunakan perpustakaan unicode-aware untuk ini.
JacquesB
1
Karena sebagian besar teknik untuk mencapai hal ini bergantung pada Unicode normalisasi, dokumen ini menggambarkan standar mungkin berguna untuk membaca: unicode.org/reports/tr15
LuddyPants
Saya pikir tim Azure memperbaiki masalah ini, saya mencoba mengunggah file dengan nama ini "Mémo de la réunion.pdf" dan operasi berhasil.
Rady

Jawaban:

532

Saya belum pernah menggunakan metode ini, tetapi Michael Kaplan menjelaskan metode untuk melakukannya di posting blog-nya (dengan judul yang membingungkan) yang berbicara tentang stripping diakritik: Stripping adalah pekerjaan yang menarik (alias Tentang arti tanpa makna, alias Semua karakter Mn) non-spacing, tetapi beberapa lebih non-spacing daripada yang lain)

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

Perhatikan bahwa ini adalah tindak lanjut dari posting sebelumnya: Stripping diacritics ....

Pendekatan ini menggunakan String.Normalisasi untuk membagi string input menjadi mesin terbang konstituen (pada dasarnya memisahkan karakter "basis" dari diakritik) dan kemudian memindai hasilnya dan hanya mempertahankan karakter dasar. Itu hanya sedikit rumit, tetapi sebenarnya Anda sedang melihat masalah yang rumit.

Tentu saja, jika Anda membatasi diri Anda ke bahasa Prancis, Anda mungkin bisa lolos dengan pendekatan berbasis tabel sederhana di Cara menghapus aksen dan tilde dalam string C ++ std :: , seperti yang direkomendasikan oleh @David Dibben.

Blair Conrad
sumber
32
Ini salah. Karakter Jerman ä dan ö dan ü mendapatkan latinisasi sebagai ae ue dan oe, dan bukan sebagai ...
Stefan Steiger
20
Juga, huruf Polandia ł diabaikan.
Zbigniew Wiadro
4
Juga Norse ø diabaikan
Richard de Wit
28
@StefanSteiger Anda tahu, di Ceko ada huruf seperti áčěů, yang biasanya kita "latinize" ke aceu, meskipun kedengarannya berbeda dan dapat menyebabkan kebingungan dalam kata-kata seperti "hráb /" / hra: bje /, "hrabě" / hrabje /, dan "hrabe" / hrabe /. Bagi saya, tampaknya penghapusan diakritik adalah masalah yang murni grafis, tidak tergantung pada fonetik atau sejarah surat itu. Huruf seperti ä ö ü dibuat dengan menambahkan superskrip "e" ke huruf dasar, sehingga dekomposisi "ae" masuk akal secara historis. Tergantung pada tujuannya - untuk menghapus tanda grafis, atau untuk menguraikan huruf menjadi karakter ASCII.
IllidanS4 ingin Monica kembali
10
Fungsi ini adalah agnostik bahasa. Tidak tahu apakah senarnya dalam bahasa Jerman atau dalam bahasa lain. Jika kita memperhitungkan bahwa tidak apa-apa untuk mengganti ö dengan oe dalam teks Jerman, tetapi tidak masuk akal untuk melakukannya dengan bahasa Turki, maka kita akan melihat bahwa tanpa mendeteksi bahasa masalah ini tidak dapat dipecahkan.
durï
163

ini melakukan trik untuk saya ...

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

cepat & pendek!

azrafe7
sumber
9
Sejauh ini metode terbaik yang pernah saya lihat.
Cleiton
2
Saya suka solusi ini dan berfungsi baik untuk Aplikasi Windows Store. Namun, itu tidak berfungsi untuk Windows Phone Apps karena penyandian ISO-8859-8 tampaknya tidak tersedia. Apakah ada pengkodean lain yang bisa digunakan?
Philip Colmer
2
Ini akan berfungsi untuk karakter yang paling umum, tetapi banyak karakter khusus seperti « »dan (sebagai karakter tunggal) akan diubah dalam proses yang tidak terjadi pada solusi yang diterima.
The_Black_Smurf
7
Perhatikan bahwa ini tidak berfungsi pada .NET Core di Linux:System.ArgumentException: 'ISO-8859-8' is not a supported encoding name.
EM0
2
Jika Anda menggunakan .NET Core, instal System.Text.Encoding.CodePagesdari nuget, lalu panggil ini untuk mendaftarkan penyedia: Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);- setelah Anda selesai melakukannya, Anda dapat menggunakan ISO-8859-8
SpaceBison
32

Jika seseorang tertarik, saya mencari yang serupa dan akhirnya menulis yang berikut ini:

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
sumber
9
Anda harus mengalokasikan buffer StringBuilder ke nama. Panjang untuk meminimalkan overhead alokasi memori. Panggilan Split / Gabung terakhir untuk menghapus duplikat berurutan _ menarik. Mungkin kita harus menghindari menambahkannya dalam loop. Tetapkan bendera untuk karakter sebelumnya menjadi _ dan tidak memancarkannya jika benar.
IDisposable
2 poin benar-benar baik, saya akan menulis ulang jika aku pernah mendapatkan waktu untuk kembali ke bagian ini kode :)
Luk
Bagus. Selain komentar IDisposables, kami mungkin harus memeriksa c < 128, untuk memastikan bahwa kami tidak mengambil karakter UTF, lihat di sini .
Christian Gollhardt
1
Atau mungkin lebih efisien c < 123. lihat ASCI
Christian Gollhardt
masih tidak bekerja dengan karakter seperti ø
juanora
27

Saya membutuhkan sesuatu yang mengubah semua karakter unicode utama dan jawaban yang terpilih meninggalkan beberapa sehingga saya telah membuat versi CodeIgniter convert_accented_characters($str)menjadi C # yang mudah disesuaikan:

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

Pemakaian

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

// for chars
"Ã"[0].RemoveDiacritics (); // A
LINGKARAN
sumber
5
Implementasi Anda berhasil, tetapi harus ditingkatkan sebelum digunakan dalam kode produksi.
Pierre Arnaud
kenapa tidak ganti saja ini if (entry.Key.IndexOf(c) != -1)menjadiif (entry.Key.Contains(c))
Pawel Cioch
Mengapa tidak menggunakan kembali RemoveDiacritics (char c) di loop, mengapa tidak menggunakan StringBuilder. Saya memilih kamus yang kompleks dan solusi yang berfungsi, tetapi kode ini bisa lebih sederhana
Pawel Cioch
1
Saya tidak mengerti mengapa ada begitu banyak lompatan melompat untuk digunakan dan { "äæǽ", "ae" }bukan { "ä", "ae" }, { "æ", "ae" }, { "ǽ", "ae" }hanya menelepon if (foreign_characters.TryGetValue(...)) .... Anda telah benar-benar mengalahkan tujuan indeks yang sudah dimiliki kamus.
Bacon Bits
15

Jika ada yang tertarik, ini adalah padanan 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();
    }
}
KenE
sumber
3
bukannya dilucuti + = nrml.charAt (i) gunakan StringBuilder. Anda memiliki runtime O (n²) yang disembunyikan di sini.
Andreas Petersson
6
Ini dan jawaban Java lainnya di sini hanya mengacaukan utas ini. Pertanyaannya adalah tentang c # (.NET) bukan Java!
suchoss
15

Saya sering menggunakan metode extenstion berdasarkan versi lain yang saya temukan di sini (lihat Mengganti karakter dalam C # (ascii) ) Penjelasan cepat:

  • Normalisasi untuk membentuk D membagi karakter seperti è ke e dan nonspacing `
  • Dari ini, karakter nospacing dihapus
  • Hasilnya dinormalisasi kembali ke bentuk C (saya tidak yakin apakah ini perlu)

Kode:

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
sumber
9

CodePage of Greek (ISO) dapat melakukannya

Informasi tentang codepage ini dimasukkan System.Text.Encoding.GetEncodings(). Pelajari tentang di: https://msdn.microsoft.com/pt-br/library/system.text.encodinginfo.getencoding(v=vs.110).aspx

Bahasa Yunani (ISO) memiliki codepage 28597 dan nama iso-8859-7 .

Buka kode ... \ 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"

Jadi, tulis fungsi ini ...

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

Perhatikan bahwa ... Encoding.GetEncoding("iso-8859-7")sama dengan Encoding.GetEncoding(28597)karena pertama adalah nama, dan kedua codepage Pengkodean.

Sergio Cabral
sumber
3
Itu brilian! Singkat dan efisien!
krlzlx
1
Hal yang bagus. Hampir semua karakter yang saya uji lulus. ( äáčďěéíľľňôóřŕšťúůýž ÄÁČĎĚÉÍĽĽŇÔÓŘŔŠŤÚŮÝŽ ÖÜË łŁđĐ ţŢşŞçÇ øı). Masalah hanya ditemukan dengan ßə, yang dikonversi menjadi ?, tetapi pengecualian tersebut selalu dapat ditangani secara terpisah. Sebelum memasukkan ini ke dalam produksi, tes harus dilakukan dengan lebih baik terhadap semua area Unicode yang mengandung huruf dengan diakritik.
miroxlav
5

Lucu pertanyaan seperti itu bisa mendapatkan begitu banyak jawaban, dan belum ada yang memenuhi persyaratan saya :) Ada begitu banyak bahasa di sekitar, solusi agnostik bahasa penuh adalah AFAIK tidak benar-benar mungkin, karena orang lain telah menyebutkan bahwa FormC atau FormD memberikan masalah.

Karena pertanyaan aslinya terkait dengan bahasa Prancis, jawaban yang paling sederhana memang

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

1251 harus diganti dengan kode penyandian dari bahasa input.

Namun ini hanya mengganti satu karakter dengan satu karakter. Karena saya juga bekerja dengan Jerman sebagai input, saya melakukan konversi manual

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

Mungkin tidak memberikan kinerja terbaik, tetapi setidaknya sangat mudah dibaca dan diperluas. Regex adalah TIDAK PERGI, jauh lebih lambat daripada hal-hal char / string.

Saya juga memiliki metode yang sangat sederhana untuk menghapus ruang:

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

Akhirnya, saya menggunakan kombinasi ketiga ekstensi di atas:

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

Dan tes unit kecil untuk itu (tidak lengkap) yang berhasil lulus.

    [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
sumber
4

Ini berfungsi baik di java.

Ini pada dasarnya mengubah semua karakter beraksen menjadi rekan deAccented mereka diikuti oleh kombinasi diakritik mereka. Sekarang Anda dapat menggunakan regex untuk menghapus diakritik.

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
sumber
Atau di Jawa 7,"\\p{Block=CombiningDiacriticalMarks}"
Brent Faust
11
Mengapa Anda memposting solusi Java ketika pertanyaan secara spesifik menanyakan .NET?
David
2
@David Pertanyaan ini adalah yang paling populer di Google untuk "java drop accents". Tidak mengatakan itu milik di sini, tetapi berguna di sini.
blubb
3

Metode ekstensi string TL; DR - C #

Saya pikir solusi terbaik untuk mempertahankan makna string adalah dengan mengkonversi karakter daripada stripping mereka, yang diilustrasikan dengan baik dalam contoh crème brûléeke crme brlevs.creme brulee .

Saya memeriksa komentar Alexander di atas dan melihat kode Lucene.Net adalah lisensi Apache 2.0, jadi saya telah memodifikasi kelas menjadi metode ekstensi string sederhana. Anda bisa menggunakannya seperti ini:

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

Fungsi ini terlalu panjang untuk dikirim dalam jawaban StackOverflow (~ 139k karakter dari 30k diperbolehkan lol) jadi saya membuat intisari dan menghubungkan penulis :

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

Semoga itu bisa membantu orang lain, ini adalah solusi paling kuat yang saya temukan!

Andy Raddatz
sumber
Peringatan: 1) Konsep ini bergantung pada lokal. Misalnya, "ä" bisa menjadi "a" atau "aa". 2) Salah dinamai / salah dideskripsikan: Hasilnya tidak hanya dari blok C0 Controls dan Basic Latin. Ini hanya mengubah huruf Latin dan beberapa varian simbol menjadi "setara". (Tentu saja, seseorang dapat mengambil pass lain untuk mengganti atau menghapus Kontrol non-C0 dan karakter blok Latin Dasar sesudahnya.) Tapi ini akan melakukan apa yang dilakukannya dengan baik.
Tom Blodget
2

INI ADALAH VERSI VB (Bekerja dengan YUNANI):

Impor Sistem. Teks

Sistem Impor. Globalisasi

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
sumber
1
Mungkin menjadi jawaban lama, tetapi mengapa Anda menggunakan baris terpisah untuk deklarasi variabel dan tugas pertama?
NiKiZe
2

Coba paket HelperSharp .

Ada metode 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
sumber
2

Ini adalah bagaimana saya mengganti karakter diakritik ke yang non-diakritik di semua program .NET saya

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
sumber
2

Anda dapat menggunakan ekstensi string dari paket nuget MMLib.Extensions:

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

Halaman Nuget: https://www.nuget.org/packages/MMLib.Extensions/ situs proyek Codeplex https://mmlib.codeplex.com/

Mino
sumber
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
sumber
Menggunakan NFD alih-alih NFC akan menyebabkan perubahan yang jauh melampaui yang diminta.
Jon Hanna
1

Apa kata orang ini:

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

Ini benar-benar membagi suka åyang merupakan satu karakter (yang merupakan kode karakter 00E5, bukan 0061 ditambah pengubah 030Ayang akan terlihat sama) menjadi aditambah beberapa jenis pengubah, dan kemudian konversi ASCII menghapus pengubah, meninggalkan satu-satunya a.

Komunitas
sumber
1

Saya sangat suka kode ringkas dan fungsional yang disediakan oleh azrafe7 . Jadi, saya telah mengubahnya sedikit untuk mengubahnya menjadi metode ekstensi:

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
sumber
Ini adalah satu-satunya metode yang bekerja dengan semua diakritik polish. Jawaban yang diterima tidak berfungsi dengan karakter Ł dan ł.
yarecky
-3

Tidak memiliki reputasi yang cukup, tampaknya saya tidak dapat mengomentari tautan yang sangat bagus dari Alexander. - Lucene tampaknya menjadi satu-satunya solusi yang bekerja dalam kasus-kasus yang cukup umum.

Bagi mereka yang menginginkan solusi salin-tempel sederhana, ini dia, memanfaatkan kode di 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
sumber