Supprimer les diacritiques

Dans le cadre d'un projet en cours, j'ai été amené à normaliser des chaînes de caractères pour en faciliter la recherche.

Avant de commencer, en C#, les chaînes de caractères sont par défaut Unicode et sous la forme NFC.

Les chaînes Unicodes peuvent être représentées sous 4 formes, NFC, NFD, NFKC et NFKD.

Dans notre cas, nous allons uniquement parler des formes NFC et NFD. Sur l'image ci-dessous, nous avons deux exemples de caractères accentués.

Source: http://www.unicode.org/reports/tr15/

Comme nous pouvons le voir, la différence entre les deux représentations est que dans la forme NFD chaque lettre est décomposée par sa lettre de base et on y associe sa diacritique.

La lettre de base peut avoir la catégorie UnicodeCategory.UppercaseLetter ou UnicodeCategory.LowercaseLetter.
Les accents ont la catégorie UnicodeCategory.NonSpacingMark.

Si on reprend tout ça en code C# voilà le petit helper qui fait bien l'affaire:

using System.Globalization;
using System.Text;

public static class StringHelpers
{
    public static string NormalizeString(this string str)
    {
        if (string.IsNullOrWhiteSpace(str))
            return str;

        // On transforme notre chaîne Unicode NFC en NFD
        str = str.Normalize(NormalizationForm.FormD);
        // On récupère tous les caractères sauf les diacritiques
        var chars = str.Where(c => CharUnicodeInfo.GetUnicodeCategory(c) != 
                                    UnicodeCategory.NonSpacingMark).ToArray();
        // On retransforme la nouvelle chaîne Unicode NFD en NFC
        return new string(chars).Normalize(NormalizationForm.FormC);
    }
}

Dernier point important, pensez à toujours remettre votre chaîne de caractères en forme NFC. Sinon vous pourrez avoir des surprises lors de comparaisons de chaînes:

void Main()
{
	string nfc = "Sébastien";
	string nfd = nfc.Normalize(NormalizationForm.FormD);
	Console.WriteLine($"NFC: {nfc}");
	Console.WriteLine($"NFD: {nfd}");
	Console.WriteLine($"Equals: {nfc == nfd}");
}

Dans la console vous verrez:

NFC: Sébastien
NFD: Sébastien
Equals: False