Je veux diviser les noms brésiliens en parties. Cependant, il existe des noms comme ci-dessous où "de"
, "da"
(et autres) ne sont pas des parties séparées et ils vont toujours avec le mot suivant. Donc, une scission normale ne fonctionne pas.
test1 = "Francisco da Sousa Rodrigues" #special split
test2 = "Emiliano Rodrigo Carrasco" #normal split
test3 = "Alberto de Francia" #special split
test4 = "Bruno Rezende" #normal split
Ma sortie attendue serait:
[Francisco, da Sousa, Rodrigues] #1
[Emiliano, Rodrigo, Carrasco] #2
[Alberto, de Francia] #3
[Bruno, Rezende] #4
Pour les cas particuliers, j'ai essayé ce modèle:
PATTERN = re.compile(r"\s(?=[da, de, do, dos, das])")
re.split(PATTERN, test1) (...)
mais le résultat n'est pas ce à quoi je m'attendais:
['Francisco', 'da Sousa Rodrigues'] #1
['Alberto', 'de Francia'] #3
Toute idée de comment résoudre ce problème? Existe-t-il un moyen d'utiliser un seul motif pour les cas "normal" et "spécial"?
Les noms seront-ils toujours écrits de manière "canonique", c’est-à-dire avec chaque partie en majuscule sauf da, de, do, ...?
Dans ce cas, vous pouvez utiliser ce fait:
>>> import re
>>> for t in (test1, test2, test3, test4):
... print(re.findall(r"(?:[a-z]+ )?[A-Z]\w+", t, re.UNICODE))
['Francisco', 'da Sousa', 'Rodrigues']
['Emiliano', 'Rodrigo', 'Carrasco']
['Alberto', 'de Francia']
['Bruno', 'Rezende']
>>>
La "bonne" façon de faire ce que vous voulez faire (à part ne pas le faire du tout) serait un aspect négatif: séparez-vous lorsque vous vous trouvez sur un espace qui n'est précédé d'aucun da, de, de, ... . Malheureusement, c'est (AFAIK) impossible, parce que re
nécessite que les contours soient de même largeur. Si aucun nom fin dans les syllabes, que vous ne pouvez vraiment pas assumer, vous pouvez procédez comme suit:
PATTERN = re.compile(r"(?<! da| de| do|dos|das)\s")
Vous pouvez parfois tomber sur des cas qui ne fonctionnent pas: si la première lettre est un caractère accentué (ou si l'article contient, en théorie, celui qui est contenu), la correspondance sera incorrecte. Pour résoudre ce problème, vous ne pourrez pas utiliser une bibliothèque externe. regex
.
Votre nouveau findall ressemblera alors à ceci:
regex.findall(r"(?:\p{Ll}+ )?\p{Lu}\w+", "Luiz Ângelo de Urzêda")
Le \p{Ll}
fait référence à tout lettre minuscule et \p{Lu}
à tout lettre majuscule.
Vous pouvez utiliser cette expression rationnelle dans findall
avec un groupe optionnel:
(?:(?:da|de|do|dos|das)\s+)?\S+
Ici, nous faisons (?:da|de|do|dos|das)
et 1+ espaces à la suite de cette option.
Exemple de code:
test1 = "Francisco da Sousa Rodrigues" #special split
test2 = "Emiliano Rodrigo Carrasco" #normal split
test3 = "Alberto de Francia" #special split
test4 = "Bruno Rezende" #normal split
PATTERN = re.compile(r'(?:(?:da|de|do|dos|das)\s+)?\S+')
>>> print re.findall(PATTERN, test1)
['Francisco', 'da Sousa', 'Rodrigues']
>>> print re.findall(PATTERN, test2)
['Emiliano', 'Rodrigo', 'Carrasco']
>>> print re.findall(PATTERN, test3)
['Alberto', 'de Francia']
>>> print re.findall(PATTERN, test4)
['Bruno', 'Rezende']
Avec la fonction regex.split()
de la bibliothèque regex
de python offrant des fonctionnalités supplémentaires:
installation:
pip install regex
usage:
import regex as re
test_names = ["Francisco da Sousa Rodrigues", "Emiliano Rodrigo Carrasco",
"Alberto de Francia", "Bruno Rezende"]
for n in test_names:
print(re.split(r'(?<!das?|de|dos?)\s+', n))
Le résultat:
['Francisco', 'da Sousa', 'Rodrigues']
['Emiliano', 'Rodrigo', 'Carrasco']
['Alberto', 'de Francia']
['Bruno', 'Rezende']
(?<!das?|de|dos?)\s+
- rechercher une assertion négative (?<!...)
garantit que les espaces \s+
ne sont pas précédés de l'un des cas spéciaux da|das|de|do|dos
On peut réaliser cette étape après avoir remplacé da avec da_ et de avec avec_:
lst = ["Francisco da Sousa Rodrigues" ,
"Emiliano Rodrigo Carrasco" ,
"Alberto de Francia" ,
"Bruno Rezende" ]
# replace da with da_ and de with de_
lst = list(map(lambda x: x.replace(" da ", " da_"), lst) )
lst = list(map(lambda x: x.replace(" de ", " de_"), lst) )
# now split names and then convert back _ to space:
lst = [ [k.replace("_", " ")
for k in l.split()]
for l in lst ]
print(lst)
Sortie:
[['Francisco', 'da Sousa', 'Rodrigues'],
['Emiliano', 'Rodrigo', 'Carrasco'],
['Alberto', 'de Francia'],
['Bruno', 'Rezende']]
Edit: en réponse au commentaire, si les noms de type "Fernanda Rezende" sont présents, vous pouvez remplacer " da "
par " da_"
(le code ci-dessus est passé de "da "
à "da_"
).
On peut aussi définir une fonction simple permettant d’apporter des modifications à toutes les chaînes d’une liste, puis l’utiliser:
def strlist_replace(slist, oristr, newstr):
return [ s.replace(oristr, newstr)
for s in slist ]
lst = strlist_replace(lst, " da ", " da_")
lst = strlist_replace(lst, " de ", " de_")
Peut-être que vous pouvez essayer quelque chose comme ça?
b_o_g=['da', 'de', 'do', 'dos', 'das']
test1 = "Francisco da Sousa Rodrigues"
test3= "Alberto de Francia"
def _custom_split (bag_of_words,string_t):
s_o_s = string_t.split()
for _,__ in enumerate(s_o_s):
if __ in bag_of_words:
try:
s_o_s[_]="{} {}".format(s_o_s[_],s_o_s[_+1])
del s_o_s [ _ + 1]
except IndexError:
pass
return s_o_s
print(_custom_split(b_o_g,test1))
print(_custom_split(b_o_g,test3))
sortie:
['Francisco', 'da Sousa', 'Rodrigues']
['Alberto', 'de Francia']
Il convient de souligner que nous parlons ici de titres, pas de noms.
Celles-ci se traduisent presque toutes par quelque chose comme "de" ou "de" et la partie suivante fait généralement référence à un lieu et elles ont pour origine des titres de noblesse.
Vous essayez d’adapter un non-nom à un contexte de nom, ce qui rend tout difficile.
C'est bizarre d'essayer de supprimer tout cela comme s'il n'existait pas. Par exemple, si vous prenez un nom tel que "Steve From New York" et essayez simplement de laisser tomber le nom et faites de New York le "nom de famille".
Ceux-ci n'ont jamais été destinés à être des noms de famille ou à agir comme ce que la plupart des gens seraient un nom de famille. Les choses ont juste un peu évolué dans cette direction avec le temps en essayant de faire rentrer des piquets ronds dans des trous carrés.
Vous pouvez ajouter un champ de titre à votre page d'inscription ou quelque chose et demander à ce qu'il soit utilisé par des personnes avec des titres comme solution plus élégante.
Cela se produit parce que vous divisez la chaîne selon votre modèle spécial. Cela va effectivement diviser la chaîne en deux parties.
Vous pouvez essayer de scinder la deuxième partie plus loin, en utilisant "" comme délimiteur une fois de plus. Notez que cela ne fonctionne pas s'il existe plusieurs instances de délimiteurs spéciaux.
Une autre approche consisterait à conserver la division en utilisant "" comme séparateur et à associer chaque séparateur spécial au nom suivant. Par exemple:
[Francisco, da, Sousa, Rodrigues] # becomes...
[Francisco, da Sousa, Rodrigues]
Peut-être pas la meilleure façon ou élégante, mais cela fonctionnera. J'ai aussi ajouté le test5 juste pour être sûr.
special_chars = ['da', 'de', 'do', 'dos', 'das']
test1 = "Francisco da Sousa Rodrigues" #special split
test2 = "Emiliano Rodrigo Carrasco" #normal split
test3 = "Alberto de Francia" #special split
test4 = "Bruno Rezende" #normal split
test5 = 'Francisco da Sousa de Rodrigues'
def cut(test):
t1 = test.split()
for i in range(len(t1)):
if t1[i] in special_chars:
t1[i+1] = t1[i] + ' ' + t1[i+1]
for i in t1:
if i in special_chars:
t1.remove(i)
print(t1)
cut(test1)
cut(test2)
cut(test3)
cut(test4)
cut(test5)
Les résultats sont:
['Francisco', 'da Sousa', 'Rodrigues']
['Emiliano', 'Rodrigo', 'Carrasco']
['Alberto', 'de Francia']
['Bruno', 'Rezende']
['Francisco', 'da Sousa', 'de Rodrigues']