From 4a8bc98f508cf6a3320daaf0a53a8bbfbb760169 Mon Sep 17 00:00:00 2001 From: mathieu Date: Tue, 18 Aug 2026 02:22:24 +0200 Subject: [PATCH] Retirer le nom de l'auteur colle en fin de titre BnF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cas mesure, ISBN 9782749942636 : dc:title = "Le plus grand defi ... (Nouvelle ed. augmentee) Aurélien Barrau" dc:creator = "Barrau, Aurélien (1973-....). Auteur du texte" Le nom est recolle en fin de titre sans le separateur ISBD « / », donc la regle de nettoyage existante ne peut rien y faire : la notice est sale a la source. L'heuristique est volontairement stricte — comparaison en fin de chaine uniquement, contre un auteur deja nettoye, et seulement si le nom commence sur une frontiere de mot. « Lettre a Émile Zola » n'est pas touche, et un titre qui EST le nom de l'auteur (biographie, recueil) est laisse intact plutot que reduit a rien. La comparaison passe par CompareOptions.IgnoreNonSpace plutot que par une normalisation NFD : elle ignore les accents sans changer les longueurs, donc les indices calcules restent valables sur la chaine d'origine. Co-Authored-By: Claude Opus 5 --- MaBibli.Api/Services/Isbn/NettoyageIsbd.cs | 66 ++++++++++++++++++++++ MaBibli.Tests/NettoyageIsbdTests.cs | 46 +++++++++++++++ 2 files changed, 112 insertions(+) diff --git a/MaBibli.Api/Services/Isbn/NettoyageIsbd.cs b/MaBibli.Api/Services/Isbn/NettoyageIsbd.cs index 2382b14..fa6deab 100644 --- a/MaBibli.Api/Services/Isbn/NettoyageIsbd.cs +++ b/MaBibli.Api/Services/Isbn/NettoyageIsbd.cs @@ -1,3 +1,4 @@ +using System.Globalization; using System.Text.RegularExpressions; namespace MaBibli.Api.Services.Isbn; @@ -43,6 +44,71 @@ public static partial class NettoyageIsbd return s.Length == 0 ? null : s; } + /// + /// Retire du titre le nom d'auteur qui y est collé en fin de chaîne. + /// + /// + /// Cas réel mesuré, ISBN 9782749942636 : + /// dc:title vaut « Le plus grand défi … (Nouvelle éd. augmentée) Aurélien Barrau » + /// alors que dc:creator vaut « Barrau, Aurélien (1973-....) ». Le nom est recollé + /// sans le séparateur ISBD « / », donc ne peut rien y faire : la + /// notice est sale à la source. + /// + /// L'heuristique est volontairement stricte — comparaison en fin de chaîne uniquement, + /// et seulement contre un auteur déjà nettoyé. Un titre qui contient légitimement un nom de + /// personne ailleurs (« Lettre à Émile Zola ») n'est pas touché, et un titre qui est + /// le nom de l'auteur (biographie, recueil) est laissé intact plutôt que réduit à rien. + /// + /// + public static string? RetirerAuteurEnFin(string? titre, IEnumerable auteurs) + { + var s = titre?.Trim(); + if (string.IsNullOrEmpty(s)) + { + return null; + } + + foreach (var auteur in auteurs) + { + var nom = auteur?.Trim(); + if (string.IsNullOrEmpty(nom) || nom.Length >= s.Length) + { + // Un nom aussi long que le titre ne laisserait rien : c'est une biographie + // ou un recueil, pas une notice sale. + continue; + } + + var debut = s.Length - nom.Length; + + // IgnoreNonSpace ignore les accents sans changer les longueurs, contrairement à une + // normalisation NFD : les indices calculés restent valables sur la chaîne d'origine. + var identique = string.Compare( + s, debut, nom, 0, nom.Length, + CultureInfo.InvariantCulture, + CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace) == 0; + + if (!identique) + { + continue; + } + + // Le nom doit commencer sur une frontière de mot, sinon « …Barrau » couperait aussi + // dans un mot qui se termine par les mêmes lettres. + if (char.IsLetterOrDigit(s[debut - 1])) + { + continue; + } + + var reste = s[..debut].TrimEnd(' ', ',', ';', '.', '-', '/', ':'); + if (reste.Length > 0) + { + return reste; + } + } + + return s; + } + /// /// Auteur : retirer les dates entre parenthèses, le rôle qui suit le point, /// puis inverser « Nom, Prénom » en « Prénom Nom ». diff --git a/MaBibli.Tests/NettoyageIsbdTests.cs b/MaBibli.Tests/NettoyageIsbdTests.cs index 6ce560f..493af12 100644 --- a/MaBibli.Tests/NettoyageIsbdTests.cs +++ b/MaBibli.Tests/NettoyageIsbdTests.cs @@ -22,6 +22,52 @@ public class NettoyageIsbdTests public void Titre_laisse_passer_ce_qui_est_deja_propre(string brut, string attendu) => Assert.Equal(attendu, NettoyageIsbd.Titre(brut)); + /// + /// Cas réel mesuré sur l'ISBN 9782749942636 : la BnF recolle le nom de l'auteur en + /// fin de titre sans le séparateur ISBD, ce que ne + /// peut donc pas voir. + /// + [Fact] + public void Titre_retire_le_nom_de_lauteur_colle_en_fin() + { + const string brut = "Le plus grand défi de l'histoire de l'humanité : face à la " + + "catastrophe écologique et sociale (Nouvelle éd. augmentée) Aurélien Barrau"; + + var titre = NettoyageIsbd.RetirerAuteurEnFin(brut, ["Aurélien Barrau"]); + + Assert.Equal( + "Le plus grand défi de l'histoire de l'humanité : face à la catastrophe écologique " + + "et sociale (Nouvelle éd. augmentée)", + titre); + } + + [Theory] + // Contre-exemple central : le nom est bien là, mais pas en fin de chaîne. On n'y touche pas. + [InlineData("Lettre à Émile Zola", "Guy de Maupassant")] + [InlineData("Émile Zola et son temps", "Émile Zola")] + // Le titre EST le nom de l'auteur (biographie, recueil) : le retirer ne laisserait rien. + [InlineData("Aurélien Barrau", "Aurélien Barrau")] + // Fin de mot fortuite : « Zola » ne doit pas être découpé dans « Gorgonzola ». + [InlineData("Éloge du gorgonzola", "Zola")] + // Auteur inconnu du titre. + [InlineData("Germinal", "Émile Zola")] + public void Titre_ne_mutile_pas_un_nom_legitime(string titre, string auteur) + => Assert.Equal(titre, NettoyageIsbd.RetirerAuteurEnFin(titre, [auteur])); + + [Fact] + public void Titre_tolere_une_difference_daccent_ou_de_casse_sur_le_nom() + => Assert.Equal( + "Le grand défi", + NettoyageIsbd.RetirerAuteurEnFin("Le grand défi AURELIEN BARRAU", ["Aurélien Barrau"])); + + [Fact] + public void Titre_sans_auteur_connu_reste_intact() + { + Assert.Equal("Germinal", NettoyageIsbd.RetirerAuteurEnFin("Germinal", [])); + Assert.Equal("Germinal", NettoyageIsbd.RetirerAuteurEnFin("Germinal", [null, " "])); + Assert.Null(NettoyageIsbd.RetirerAuteurEnFin(null, ["Émile Zola"])); + } + [Theory] [InlineData("Zola, Émile (1840-1902). Auteur du texte", "Émile Zola")] [InlineData("Saint-Exupéry, Antoine de (1900-1944). Auteur du texte", "Antoine de Saint-Exupéry")]