using System.Globalization;
using System.Text;
namespace MaBibli.Shared.Textes;
///
/// Mise à plat d'un texte pour la recherche et le regroupement : minuscules, sans accents,
/// sans ponctuation, espaces réduits.
///
///
/// Pourquoi une forme stockée plutôt qu'une comparaison à la volée ? SQLite ne sait pas
/// comparer sans tenir compte des accents : sa collation NOCASE ne couvre que l'ASCII, et
/// il n'existe pas de unaccent() intégré. Faire le travail en C# à l'écriture, dans une
/// colonne dédiée, est la seule façon d'obtenir « Emile » → « Émile » sans charger tout
/// le catalogue en mémoire à chaque recherche.
///
/// Conséquence à ne pas oublier : toute écriture doit recalculer la forme normalisée, et le
/// terme cherché doit passer par la même fonction que la valeur stockée.
///
///
public static class NormalisationTexte
{
///
/// Forme comparable d'un texte quelconque : « Émile ZOLA » → « emile zola ».
///
///
/// La ponctuation devient une espace plutôt que de disparaître : « P.F. Hamilton » doit
/// donner trois mots (p f hamilton) et non un seul (pf hamilton), sans quoi
/// le rapprochement par initiales serait impossible.
///
public static string Normaliser(string? brut)
{
if (string.IsNullOrWhiteSpace(brut))
{
return string.Empty;
}
// NFD sépare la lettre de son accent ; on ne garde ensuite que la lettre.
var decompose = brut.Normalize(NormalizationForm.FormD);
var sortie = new StringBuilder(decompose.Length);
var espaceEnAttente = false;
foreach (var c in decompose)
{
if (CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.NonSpacingMark)
{
continue; // Accent détaché par la décomposition.
}
if (char.IsLetterOrDigit(c))
{
if (espaceEnAttente && sortie.Length > 0)
{
sortie.Append(' ');
}
espaceEnAttente = false;
sortie.Append(char.ToLowerInvariant(c));
continue;
}
// Tout le reste (espaces, points, virgules, tirets…) est un séparateur de mots.
espaceEnAttente = true;
}
return sortie.ToString().Normalize(NormalizationForm.FormC);
}
/// Mots de la forme normalisée, sans mot vide.
public static string[] Mots(string? brut)
{
var normalise = Normaliser(brut);
return normalise.Length == 0
? []
: normalise.Split(' ', StringSplitOptions.RemoveEmptyEntries);
}
}