python - Dividir los nombres que incluyen "de", "da", etc. en primero, medio, último, etc.
regex python-3.x (9)
¿Los nombres siempre se escribirán en la forma "canónica", es decir, con cada parte en mayúscula excepto por da, de, do, ...?
En ese caso, puedes usar ese hecho:
>>> 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 forma "correcta" de hacer lo que usted quiere hacer (aparte de no hacerlo) sería una mirada negativa detrás de: dividirse cuando se encuentra en un espacio que no está precedido por ninguno de los siguientes: da, de, do, ... . Lamentablemente, esto es (AFAIK) imposible, porque re
requiere que el aspecto sea igual de ancho. Si ningún nombre termina en las sílabas, lo que realmente no puedes asumir, podrías hacer esto:
PATTERN = re.compile(r"(?<! da| de| do|dos|das)/s")
Puede o no tropezar ocasionalmente con casos que no funcionan: si la primera letra es un carácter acentuado (o el artículo, hipotéticamente, contiene uno), coincidirá incorrectamente. Para solucionar este problema, no podrá moverse usando una biblioteca externa; regex
Tu nuevo buscador se verá así entonces:
regex.findall(r"(?:/p{Ll}+ )?/p{Lu}/w+", "Luiz Ângelo de Urzêda")
La /p{Ll}
refiere a cualquier letra minúscula, y /p{Lu}
a cualquier letra mayúscula.
Quiero dividir los nombres brasileños en partes. Sin embargo, hay nombres como los siguientes: "de"
, "da"
(y otros) que no son partes separadas y siempre van con la siguiente palabra. Así que la división normal no funciona.
test1 = "Francisco da Sousa Rodrigues" #special split
test2 = "Emiliano Rodrigo Carrasco" #normal split
test3 = "Alberto de Francia" #special split
test4 = "Bruno Rezende" #normal split
Mi salida esperada sería:
[Francisco, da Sousa, Rodrigues] #1
[Emiliano, Rodrigo, Carrasco] #2
[Alberto, de Francia] #3
[Bruno, Rezende] #4
Para los casos especiales probé este patrón:
PATTERN = re.compile(r"/s(?=[da, de, do, dos, das])")
re.split(PATTERN, test1) (...)
pero la salida no es lo que esperaba
[''Francisco'', ''da Sousa Rodrigues''] #1
[''Alberto'', ''de Francia''] #3
¿Alguna de idea de cómo arreglarlo? ¿Hay alguna manera de usar un solo patrón para el caso "normal" y "especial"?
Cabe señalar que aquí estamos hablando de títulos, no de nombres.
Estos casi todos se traducen en algo como "de" o "de" y la parte posterior se refiere típicamente a un lugar y se originaron como títulos para la nobleza.
Estás tratando de encajar un no-nombre en un contexto de nombre, lo que dificulta todo.
Es raro tratar de eliminar todo esto como si no existiera. Por ejemplo, si toma un nombre como "Steve From New York" y simplemente intente quitar el nombre y hacer de New York el "apellido".
Estos nunca fueron intencionados para ser apellidos o para actuar como lo que para la mayoría de las personas sería un apellido. Las cosas simplemente se desviaron en esa dirección a lo largo del tiempo tratando de hacer que las clavijas redondas encajen en los agujeros cuadrados.
Puede agregar un campo de título a su página de registro o algo así y dirigirlo para que lo usen las personas con títulos como una solución más elegante.
Con la función regex.split()
de la biblioteca regex
de python, que ofrece una funcionalidad adicional:
instalación:
pip install regex
uso:
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))
La salida:
[''Francisco'', ''da Sousa'', ''Rodrigues'']
[''Emiliano'', ''Rodrigo'', ''Carrasco'']
[''Alberto'', ''de Francia'']
[''Bruno'', ''Rezende'']
-
(?<!das?|de|dos?)/s+
- ver detrás de la afirmación negativa(?<!...)
asegura que los espacios en blanco/s+
no estén precedidos por uno de los casos especialesda|das|de|do|dos
Esto sucede porque se divide la cadena en su patrón especial. Esto dividirá la cuerda en dos partes.
Podría intentar dividir la segunda parte más adelante, utilizando "" como un delimitador una vez más. Tenga en cuenta que esto no funciona en caso de que haya varias instancias de delimitadores especiales.
Otro enfoque sería seguir dividiendo usando "" como delimitador, y unir cada delimitador especial con el siguiente nombre. Por ejemplo:
[Francisco, da, Sousa, Rodrigues] # becomes...
[Francisco, da Sousa, Rodrigues]
Puede ser que puedas probar algo como esto?
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))
salida:
[''Francisco'', ''da Sousa'', ''Rodrigues'']
[''Alberto'', ''de Francia'']
Puede usar esta expresión regular en findall
con un grupo opcional:
(?:(?:da|de|do|dos|das)/s+)?/S+
Aquí hacemos (?:da|de|do|dos|das)
y 1+ espacios en blanco siguiendo este opcional.
Ejemplo de código:
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'']
Tal vez no sea de la mejor manera o elegante pero esto funcionará. También agregué el test5 solo para estar seguro.
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)
Los resultados son:
[''Francisco'', ''da Sousa'', ''Rodrigues'']
[''Emiliano'', ''Rodrigo'', ''Carrasco'']
[''Alberto'', ''de Francia'']
[''Bruno'', ''Rezende'']
[''Francisco'', ''da Sousa'', ''de Rodrigues'']
Tu expresión regular debe ser cambiada a
PATTERN = re.compile (r "/ s (? = [Da, de, do, dos, das]) (/ S + / s * / s / s * / S +)")
import re
test1 = "Francisco da Sousa Rodrigues" #special split
test3 = "Alberto de Francia" #special split
PATTERN = re.compile(r"/s(?=[da, de, do, dos, das])(/S+/s*/s/s*/S+)")
print re.split(PATTERN, test1)
print re.split(PATTERN, test3)
Esto funciona para mí dando los siguientes resultados,
[''Francisco'', ''da Sousa'', ''Rodrigues''] [''Alberto'', ''de Francia'', '''']
Uno puede lograr esto paso a paso después de reemplazar da con da_ y de con de_:
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)
Salida:
[[''Francisco'', ''da Sousa'', ''Rodrigues''],
[''Emiliano'', ''Rodrigo'', ''Carrasco''],
[''Alberto'', ''de Francia''],
[''Bruno'', ''Rezende'']]
Edición: en respuesta al comentario, si hay nombres de tipo "Fernanda Rezende", entonces se puede reemplazar " da "
con " da_"
(el código anterior cambió a esto desde la anterior "da "
a "da_"
)
También se puede definir una función simple para realizar cambios en todas las cadenas de una lista, y luego usarla:
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_")