From 0fde244d0bea1e6231366e3cecbfe5517ac79a50 Mon Sep 17 00:00:00 2001 From: mathieu Date: Tue, 18 Aug 2026 02:43:07 +0200 Subject: [PATCH] Couper le role BnF meme quand le prenom finit par une initiale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NettoyageIsbd.Auteur("Cormen, Thomas H. Auteur du texte") renvoyait « Thomas H. Auteur du texte Cormen » : le point de l initiale etait protege, donc plus aucun point ne servait de separateur de role. Ce qui tranche : dans « Nom, Prenom », la seule chose qui puisse suivre une initiale et faire encore partie du nom est une autre initiale (« Tolkien, J. R. R. »). Tout le reste est le role. Le point est conserve quand il clot une initiale, pour ne pas amputer le prenom. Co-Authored-By: Claude Opus 5 --- MaBibli.Api/Services/Isbn/NettoyageIsbd.cs | 50 +++++++++++++++------- MaBibli.Tests/NettoyageIsbdTests.cs | 17 +++++--- 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/MaBibli.Api/Services/Isbn/NettoyageIsbd.cs b/MaBibli.Api/Services/Isbn/NettoyageIsbd.cs index fa6deab..563b85e 100644 --- a/MaBibli.Api/Services/Isbn/NettoyageIsbd.cs +++ b/MaBibli.Api/Services/Isbn/NettoyageIsbd.cs @@ -128,7 +128,9 @@ public static partial class NettoyageIsbd var pointRole = IndexDuPointDeRole(s); if (pointRole >= 0) { - s = s[..pointRole]; + // Quand le rôle suit une initiale (« Cormen, Thomas H. Auteur du texte »), le point + // qui sert de séparateur appartient aussi au nom : on le conserve. + s = EstPointDInitiale(s, pointRole) ? s[..(pointRole + 1)] : s[..pointRole]; } s = RetirerPonctuationFinale(s); @@ -174,12 +176,7 @@ public static partial class NettoyageIsbd while (s.EndsWith('.')) { - var i = s.Length - 1; - var estInitiale = i >= 1 - && char.IsUpper(s[i - 1]) - && (i == 1 || s[i - 2] == ' ' || s[i - 2] == '-' || s[i - 2] == '\''); - - if (estInitiale) + if (EstPointDInitiale(s, s.Length - 1)) { break; } @@ -194,9 +191,15 @@ public static partial class NettoyageIsbd /// Position du point qui introduit le rôle, ou -1. /// /// - /// Un point suivi d'une espace sépare le rôle du nom. On ignore les points d'initiales - /// (« Cormen, Thomas H. Auteur ») : un point précédé d'une majuscule isolée n'est pas - /// un séparateur de rôle. + /// Un point suivi d'une espace sépare le rôle du nom. Le point d'une initiale + /// (« Cormen, Thomas H. ») n'en est pas un — le couper amputerait le prénom. + /// + /// Mais protéger l'initiale ne doit pas protéger le rôle qui la suit : « Cormen, Thomas H. + /// Auteur du texte » renvoyait auparavant « Thomas H. Auteur du texte Cormen ». Ce qui + /// tranche : dans « Nom, Prénom », la seule chose qui puisse suivre une initiale et faire + /// encore partie du nom est une autre initiale (« Tolkien, J. R. R. »). Tout le + /// reste est le rôle. + /// /// private static int IndexDuPointDeRole(string s) { @@ -207,16 +210,31 @@ public static partial class NettoyageIsbd continue; } - var estInitiale = i >= 1 - && char.IsUpper(s[i - 1]) - && (i == 1 || s[i - 2] == ' ' || s[i - 2] == '-' || s[i - 2] == '\''); - - if (!estInitiale) + if (EstPointDInitiale(s, i) && CommenceParUneInitiale(s, i + 2)) { - return i; + continue; } + + return i; } return -1; } + + /// + /// Le point en clôt-il une initiale (majuscule isolée) ? + /// + private static bool EstPointDInitiale(string s, int i) + => i >= 1 + && char.IsUpper(s[i - 1]) + && (i == 1 || s[i - 2] == ' ' || s[i - 2] == '-' || s[i - 2] == '\''); + + /// + /// Le texte à partir de commence-t-il par une initiale + /// (une majuscule suivie d'un point ou de la fin de chaîne) ? + /// + private static bool CommenceParUneInitiale(string s, int debut) + => debut < s.Length + && char.IsUpper(s[debut]) + && (debut + 1 == s.Length || s[debut + 1] == '.'); } diff --git a/MaBibli.Tests/NettoyageIsbdTests.cs b/MaBibli.Tests/NettoyageIsbdTests.cs index 493af12..7c11296 100644 --- a/MaBibli.Tests/NettoyageIsbdTests.cs +++ b/MaBibli.Tests/NettoyageIsbdTests.cs @@ -85,12 +85,17 @@ public class NettoyageIsbdTests public void Auteur_laisse_passer_ce_qui_est_deja_propre(string brut, string attendu) => Assert.Equal(attendu, NettoyageIsbd.Auteur(brut)); - [Fact] - public void Auteur_ne_coupe_pas_sur_une_initiale() - { - // « H. » est une initiale, pas un séparateur de rôle : la couper amputerait le prénom. - Assert.Equal("Thomas H. Cormen", NettoyageIsbd.Auteur("Cormen, Thomas H.")); - } + [Theory] + // Contre-exemples : le nom se termine légitimement par une initiale, il n'y a pas de rôle + // à retirer — couper au point amputerait le prénom. + [InlineData("Cormen, Thomas H.", "Thomas H. Cormen")] + [InlineData("Tolkien, J. R. R.", "J. R. R. Tolkien")] + // Cas réel : le rôle suit une initiale. Protéger le point de l'initiale ne doit pas + // protéger le rôle — sinon on obtient « Thomas H. Auteur du texte Cormen ». + [InlineData("Cormen, Thomas H. Auteur du texte", "Thomas H. Cormen")] + [InlineData("Tolkien, J. R. R. (1892-1973). Auteur du texte", "J. R. R. Tolkien")] + public void Auteur_distingue_une_initiale_d_un_role(string brut, string attendu) + => Assert.Equal(attendu, NettoyageIsbd.Auteur(brut)); [Theory] [InlineData("le Livre de poche (Paris)", "le Livre de poche")]