delimiters - Cómo hacer CamelCase split en python
split python 3 (7)
Lo que estaba tratando de lograr, era algo como esto:
>>> camel_case_split("CamelCaseXYZ")
[''Camel'', ''Case'', ''XYZ'']
>>> camel_case_split("XYZCamelCase")
[''XYZ'', ''Camel'', ''Case'']
Así que busqué y encontré esta expresión regular perfecta :
(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])
Como siguiente paso lógico intenté:
>>> re.split("(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])", "CamelCaseXYZ")
[''CamelCaseXYZ'']
¿Por qué esto no funciona y cómo logro el resultado de la pregunta vinculada en python?
Edición: resumen de la solución
Probé todas las soluciones provistas con algunos casos de prueba:
string: ''''
nfs: ['''']
casimir_et_hippolyte: []
two_hundred_success: []
kalefranz: string index out of range # with modification: either [] or ['''']
string: '' ''
nfs: ['' '']
casimir_et_hippolyte: []
two_hundred_success: ['' '']
kalefranz: ['' '']
string: ''lower''
all algorithms: [''lower'']
string: ''UPPER''
all algorithms: [''UPPER'']
string: ''Initial''
all algorithms: [''Initial'']
string: ''dromedaryCase''
nfs: [''dromedary'', ''Case'']
casimir_et_hippolyte: [''dromedary'', ''Case'']
two_hundred_success: [''dromedary'', ''Case'']
kalefranz: [''Dromedary'', ''Case''] # with modification: [''dromedary'', ''Case'']
string: ''CamelCase''
all algorithms: [''Camel'', ''Case'']
string: ''ABCWordDEF''
nfs: [''ABC'', ''Word'', ''DEF'']
casimir_et_hippolyte: [''ABC'', ''Word'', ''DEF'']
two_hundred_success: [''ABC'', ''Word'', ''DEF'']
kalefranz: [''ABCWord'', ''DEF'']
En resumen, se podría decir que la solución de @kalefranz no coincide con la pregunta (consulte el último caso) y la solución de @casimir et hippolyte come un espacio único, y por lo tanto viola la idea de que una división no debe cambiar las partes individuales. La única diferencia entre las dos alternativas restantes es que mi solución devuelve una lista con la cadena vacía en una entrada de cadena vacía y la solución por @ 200_success devuelve una lista vacía. No sé cómo se encuentra la comunidad python sobre ese tema, así que digo: estoy bien con cualquiera de los dos. Y como la solución 200_success es más simple, la acepté como la respuesta correcta.
Aquí hay otra solución que requiere menos código y sin expresiones regulares complicadas:
def camel_case_split(string):
bldrs = [[string[0].upper()]]
for c in string[1:]:
if bldrs[-1][-1].islower() and c.isupper():
bldrs.append([c])
else:
bldrs[-1].append(c)
return [''''.join(bldr) for bldr in bldrs]
Editar
El código anterior contiene una optimización que evita la reconstrucción de toda la cadena con cada carácter agregado. Dejando fuera esa optimización, una versión más simple (con comentarios) podría verse como
def camel_case_split2(string):
# set the logic for creating a "break"
def is_transition(c1, c2):
return c1.islower() and c2.isupper()
# start the builder list with the first character
# enforce upper case
bldr = [string[0].upper()]
for c in string[1:]:
# get the last character in the last element in the builder
# note that strings can be addressed just like lists
previous_character = bldr[-1][-1]
if is_transition(previous_character, c):
# start a new element in the list
bldr.append(c)
else:
# append the character to the last string
bldr[-1] += c
return bldr
Como @nfs ha explicado, re.split()
nunca se divide en una coincidencia de patrón vacía. Por lo tanto, en lugar de dividir, debe intentar encontrar los componentes que le interesan.
Aquí hay una solución que utiliza re.finditer()
que emula la división:
def camel_case_split(identifier):
matches = finditer(''.+?(?:(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|$)'', identifier)
return [m.group(0) for m in matches]
Creo que a continuación es el optimim
Def count_word (): Return (re.findall (''[AZ]? [Az] +'', input (''ingrese su cadena''))
Imprimir (count_word ())
La documentation para re.split
de re.split
dice:
Tenga en cuenta que la división nunca dividirá una cadena en una coincidencia de patrón vacía.
Al ver esto:
>>> re.findall("(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])", "CamelCaseXYZ")
['''', '''']
queda claro, por qué la división no funciona como se esperaba. El módulo re
encuentra coincidencias vacías, tal como lo pretende la expresión regular.
Dado que la documentación indica que esto no es un error, sino un comportamiento intencionado, debe evitarlo al intentar crear una división de fundas de camellos:
def camel_case_split(identifier):
matches = finditer(''(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])'', identifier)
split_string = []
# index of beginning of slice
previous = 0
for match in matches:
# get slice
split_string.append(identifier[previous:match.start()])
# advance index
previous = match.start()
# get remaining string
split_string.append(identifier[previous:])
return split_string
La mayoría de las veces, cuando no necesita verificar el formato de una cadena, una investigación global es más simple que una división (para el mismo resultado):
re.findall(r''[A-Z](?:[a-z]+|[A-Z]*(?=[A-Z]|$))'', ''CamelCaseXYZ'')
devoluciones
[''Camel'', ''Case'', ''XYZ'']
Para tratar con el dromedario también, puede utilizar:
re.findall(r''[A-Z]?[a-z]+|[A-Z]+(?=[A-Z]|$)'', ''camelCaseXYZ'')
Nota: (?=[AZ]|$)
puede acortarse usando una negación doble (un lookahead negativo con una clase de caracteres negada): (?![^AZ])
Me topé con este caso y escribí una expresión regular para resolverlo. Debería funcionar para cualquier grupo de palabras, en realidad.
RE_WORDS = re.compile(r''''''
# Find words in a string. Order matters!
[A-Z]+(?=[A-Z][a-z]) | # All upper case before a capitalized word
[A-Z]?[a-z]+ | # Capitalized words / all lower case
[A-Z]+ | # All upper case
/d+ # Numbers
'''''', re.VERBOSE)
La clave aquí es la búsqueda anticipada del primer caso posible. Unirá (y preservará) las palabras en mayúscula antes de las mayúsculas:
assert RE_WORDS.findall(''FOOBar'') == [''FOO'', ''Bar'']
Utilice re.sub()
y split()
import re
name = ''CamelCaseTest123''
splitted = re.sub(''(?!^)([A-Z][a-z]+)'', r'' /1'', name).split()
resultado
[''Camel'', ''Case'', ''Test123'']