Ejemplo:
>>> convert('CamelCase')
'camel_case'
NotCamelCase
perothisIs
Ejemplo:
>>> convert('CamelCase')
'camel_case'
NotCamelCase
perothisIs
Respuestas:
import re
name = 'CamelCaseName'
name = re.sub(r'(?<!^)(?=[A-Z])', '_', name).lower()
print(name) # camel_case_name
Si hace esto muchas veces y lo anterior es lento, compile la expresión regular de antemano:
pattern = re.compile(r'(?<!^)(?=[A-Z])')
name = pattern.sub('_', name).lower()
Para manejar casos más avanzados especialmente (esto ya no es reversible):
def camel_to_snake(name):
name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower()
print(camel_to_snake('camel2_camel2_case')) # camel2_camel2_case
print(camel_to_snake('getHTTPResponseCode')) # get_http_response_code
print(camel_to_snake('HTTPResponseCodeXYZ')) # http_response_code_xyz
name = 'snake_case_name'
name = ''.join(word.title() for word in name.split('_'))
print(name) # SnakeCaseName
not_camel_case
a notCamelCase
y / o NotCamelCase
?
s2.replace('__', '_')
Hay una biblioteca de inflexiones en el índice del paquete que puede manejar estas cosas por usted. En este caso, estarías buscando inflection.underscore()
:
>>> inflection.underscore('CamelCase')
'camel_case'
No sé por qué todo esto es tan complicado.
para la mayoría de los casos, la expresión simple ([A-Z]+)
hará el truco
>>> re.sub('([A-Z]+)', r'_\1','CamelCase').lower()
'_camel_case'
>>> re.sub('([A-Z]+)', r'_\1','camelCase').lower()
'camel_case'
>>> re.sub('([A-Z]+)', r'_\1','camel2Case2').lower()
'camel2_case2'
>>> re.sub('([A-Z]+)', r'_\1','camelCamelCase').lower()
'camel_camel_case'
>>> re.sub('([A-Z]+)', r'_\1','getHTTPResponseCode').lower()
'get_httpresponse_code'
Para ignorar el primer personaje simplemente agrega mirar detrás (?!^)
>>> re.sub('(?!^)([A-Z]+)', r'_\1','CamelCase').lower()
'camel_case'
>>> re.sub('(?!^)([A-Z]+)', r'_\1','CamelCamelCase').lower()
'camel_camel_case'
>>> re.sub('(?!^)([A-Z]+)', r'_\1','Camel2Camel2Case').lower()
'camel2_camel2_case'
>>> re.sub('(?!^)([A-Z]+)', r'_\1','getHTTPResponseCode').lower()
'get_httpresponse_code'
Si desea separar ALLCaps a all_caps y espera números en su cadena, todavía no necesita hacer dos ejecuciones separadas, solo use |
Esta expresión ((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))
puede manejar casi todos los escenarios del libro
>>> a = re.compile('((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))')
>>> a.sub(r'_\1', 'getHTTPResponseCode').lower()
'get_http_response_code'
>>> a.sub(r'_\1', 'get2HTTPResponseCode').lower()
'get2_http_response_code'
>>> a.sub(r'_\1', 'get2HTTPResponse123Code').lower()
'get2_http_response123_code'
>>> a.sub(r'_\1', 'HTTPResponseCode').lower()
'http_response_code'
>>> a.sub(r'_\1', 'HTTPResponseCodeXYZ').lower()
'http_response_code_xyz'
Todo depende de lo que desee, así que use la solución que mejor se adapte a sus necesidades, ya que no debería ser demasiado complicado.
nJoy!
(?!^)
expresión que se llamaba mirar hacia atrás. A menos que me falte algo, lo que realmente queremos aquí es una mirada negativa que debe expresarse como (?<!^)
. Por razones que no entiendo, su mirada negativa hacia adelante (?!^)
parece funcionar también ...
"Camel2WARNING_Case_CASE"
convierte en "camel2_warning_case__case"
. Puede agregar una (?<!_)
mirada negativa hacia atrás, para resolverlo: re.sub('((?<=[a-z0-9])[A-Z]|(?!^)(?<!_)[A-Z](?=[a-z]))', r'_\1', "Camel2WARNING_Case_CASE").lower()
retornos 'camel2_warning_case_case'
(?!^)
se llamó incorrectamente "mirar hacia atrás" y en su lugar debería haberse llamado afirmación negativa anticipada . Como muestra esta buena explicación , las miradas negativas generalmente aparecen después de la expresión que estás buscando. Por lo tanto, puede pensar (?!^)
en "encontrar ''
donde <start of string>
no sigue". De hecho, una mirada hacia atrás negativa también funciona: se puede pensar (?<!^)
como "buscar ''
donde <start of string>
no precede".
stringcase es mi biblioteca de acceso para esto; p.ej:
>>> from stringcase import pascalcase, snakecase
>>> snakecase('FooBarBaz')
'foo_bar_baz'
>>> pascalcase('foo_bar_baz')
'FooBarBaz'
Prefiero evitar re
si es posible:
def to_camelcase(s):
return ''.join(['_' + c.lower() if c.isupper() else c for c in s]).lstrip('_')
>>> to_camelcase("ThisStringIsCamelCase")
'this_string_is_camel_case'
re
biblioteca y hacer las cosas solo en una línea usando métodos str. incorporados! Es similar a esta respuesta , pero evita el uso de segmentación y adicional if ... else
simplemente quitando "_" potencialmente agregado como primer carácter. Esto me gusta más
6.81 µs ± 22.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
pero para esta respuesta 2.51 µs ± 25.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
que es 2.5 veces más rápida! ¡Me gusta esto!
Personalmente, no estoy seguro de cómo algo que usa expresiones regulares en Python puede describirse como elegante. La mayoría de las respuestas aquí solo están haciendo trucos RE de "código de golf". Se supone que la codificación elegante se entiende fácilmente.
def to_snake_case(not_snake_case):
final = ''
for i in xrange(len(not_snake_case)):
item = not_snake_case[i]
if i < len(not_snake_case) - 1:
next_char_will_be_underscored = (
not_snake_case[i+1] == "_" or
not_snake_case[i+1] == " " or
not_snake_case[i+1].isupper()
)
if (item == " " or item == "_") and next_char_will_be_underscored:
continue
elif (item == " " or item == "_"):
final += "_"
elif item.isupper():
final += "_"+item.lower()
else:
final += item
if final[0] == "_":
final = final[1:]
return final
>>> to_snake_case("RegularExpressionsAreFunky")
'regular_expressions_are_funky'
>>> to_snake_case("RegularExpressionsAre Funky")
'regular_expressions_are_funky'
>>> to_snake_case("RegularExpressionsAre_Funky")
'regular_expressions_are_funky'
+=
en cadenas es casi siempre una mala idea. Añadir a una lista y ''.join()
al final. O en este caso, simplemente
''.join('_'+c.lower() if c.isupper() else c for c in "DeathToCamelCase").strip('_')
re.sub("(.)([A-Z])", r'\1_\2', 'DeathToCamelCase').lower()
Creo que esta solución es más sencilla que las respuestas anteriores:
import re
def convert (camel_input):
words = re.findall(r'[A-Z]?[a-z]+|[A-Z]{2,}(?=[A-Z][a-z]|\d|\W|$)|\d+', camel_input)
return '_'.join(map(str.lower, words))
# Let's test it
test_strings = [
'CamelCase',
'camelCamelCase',
'Camel2Camel2Case',
'getHTTPResponseCode',
'get200HTTPResponseCode',
'getHTTP200ResponseCode',
'HTTPResponseCode',
'ResponseHTTP',
'ResponseHTTP2',
'Fun?!awesome',
'Fun?!Awesome',
'10CoolDudes',
'20coolDudes'
]
for test_string in test_strings:
print(convert(test_string))
Qué salidas:
camel_case
camel_camel_case
camel_2_camel_2_case
get_http_response_code
get_200_http_response_code
get_http_200_response_code
http_response_code
response_http
response_http_2
fun_awesome
fun_awesome
10_cool_dudes
20_cool_dudes
La expresión regular coincide con tres patrones:
[A-Z]?[a-z]+
: Letras minúsculas consecutivas que opcionalmente comienzan con una letra mayúscula.[A-Z]{2,}(?=[A-Z][a-z]|\d|\W|$)
: Dos o más letras mayúsculas consecutivas. Utiliza una búsqueda anticipada para excluir la última letra mayúscula si es seguida por una letra minúscula.\d+
: Numeros consecutivos.Al usarlo re.findall
, obtenemos una lista de "palabras" individuales que se pueden convertir a minúsculas y unir con guiones bajos.
No tengo idea de por qué usar ambas llamadas .sub ()? :) No soy regex guru, pero simplifiqué la función para este, que es adecuado para mis necesidades, solo necesitaba una solución para convertir camelCasedVars de la solicitud POST a vars_with_underscore:
def myFunc(...):
return re.sub('(.)([A-Z]{1})', r'\1_\2', "iTriedToWriteNicely").lower()
No funciona con nombres como getHTTPResponse, porque escuché que es una convención de nomenclatura incorrecta (debería ser como getHttpResponse, obviamente, es mucho más fácil memorizar este formulario).
'HTTPConnectionFactory'
, con , su código produce 'h_tt_pconnection_factory'
, el código de la respuesta aceptada produce'http_connection_factory'
Aquí está mi solución:
def un_camel(text):
""" Converts a CamelCase name into an under_score name.
>>> un_camel('CamelCase')
'camel_case'
>>> un_camel('getHTTPResponseCode')
'get_http_response_code'
"""
result = []
pos = 0
while pos < len(text):
if text[pos].isupper():
if pos-1 > 0 and text[pos-1].islower() or pos-1 > 0 and \
pos+1 < len(text) and text[pos+1].islower():
result.append("_%s" % text[pos].lower())
else:
result.append(text[pos].lower())
else:
result.append(text[pos])
pos += 1
return "".join(result)
Es compatible con los casos de esquina discutidos en los comentarios. Por ejemplo, se convertirá getHTTPResponseCode
a get_http_response_code
lo que debería.
Por diversión:
>>> def un_camel(input):
... output = [input[0].lower()]
... for c in input[1:]:
... if c in ('ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
... output.append('_')
... output.append(c.lower())
... else:
... output.append(c)
... return str.join('', output)
...
>>> un_camel("camel_case")
'camel_case'
>>> un_camel("CamelCase")
'camel_case'
O, más por el gusto de hacerlo:
>>> un_camel = lambda i: i[0].lower() + str.join('', ("_" + c.lower() if c in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" else c for c in i[1:]))
>>> un_camel("camel_case")
'camel_case'
>>> un_camel("CamelCase")
'camel_case'
str.join
ha quedado en desuso por siglos . Usar en su ''.join(..)
lugar.
El uso de expresiones regulares puede ser el más corto, pero esta solución es mucho más legible:
def to_snake_case(s):
snake = "".join(["_"+c.lower() if c.isupper() else c for c in s])
return snake[1:] if snake.startswith("_") else snake
Tantos métodos complicados ... Simplemente encuentre todo el grupo "Titulado" y únase a su variante en minúsculas con guión bajo.
>>> import re
>>> def camel_to_snake(string):
... groups = re.findall('([A-z0-9][a-z]*)', string)
... return '_'.join([i.lower() for i in groups])
...
>>> camel_to_snake('ABCPingPongByTheWay2KWhereIsOurBorderlands3???')
'a_b_c_ping_pong_by_the_way_2_k_where_is_our_borderlands_3'
Si no desea crear números como el primer carácter del grupo o un grupo separado, puede usar la ([A-z][a-z0-9]*)
máscara.
No en la biblioteca estándar, pero encontré este script que parece contener la funcionalidad que necesita.
Este no es un método elegante, es una implementación de 'nivel bajo' de una máquina de estado simple (máquina de estado de campo de bits), posiblemente el modo más antipitónico para resolver esto, sin embargo, re module también implementa una máquina de estado demasiado compleja para resolver este problema simple. tarea, así que creo que esta es una buena solución.
def splitSymbol(s):
si, ci, state = 0, 0, 0 # start_index, current_index
'''
state bits:
0: no yields
1: lower yields
2: lower yields - 1
4: upper yields
8: digit yields
16: other yields
32 : upper sequence mark
'''
for c in s:
if c.islower():
if state & 1:
yield s[si:ci]
si = ci
elif state & 2:
yield s[si:ci - 1]
si = ci - 1
state = 4 | 8 | 16
ci += 1
elif c.isupper():
if state & 4:
yield s[si:ci]
si = ci
if state & 32:
state = 2 | 8 | 16 | 32
else:
state = 8 | 16 | 32
ci += 1
elif c.isdigit():
if state & 8:
yield s[si:ci]
si = ci
state = 1 | 4 | 16
ci += 1
else:
if state & 16:
yield s[si:ci]
state = 0
ci += 1 # eat ci
si = ci
print(' : ', c, bin(state))
if state:
yield s[si:ci]
def camelcaseToUnderscore(s):
return '_'.join(splitSymbol(s))
splitsymbol puede analizar todos los tipos de casos: UpperSEQUENCEInterleaved, under_score, BIG_SYMBOLS y cammelCasedMethods
Espero que sea útil
Ligeramente adaptado de https://stackoverflow.com/users/267781/matth que usan generadores.
def uncamelize(s):
buff, l = '', []
for ltr in s:
if ltr.isupper():
if buff:
l.append(buff)
buff = ''
buff += ltr
l.append(buff)
return '_'.join(l).lower()
Echa un vistazo a la excelente lib Schematics
https://github.com/schematics/schematics
Le permite crear estructuras de datos mecanografiados que pueden serializar / deserializar desde Python a Javascript Javascript, por ejemplo:
class MapPrice(Model):
price_before_vat = DecimalType(serialized_name='priceBeforeVat')
vat_rate = DecimalType(serialized_name='vatRate')
vat = DecimalType()
total_price = DecimalType(serialized_name='totalPrice')
Este método simple debería hacer el trabajo:
import re
def convert(name):
return re.sub(r'([A-Z]*)([A-Z][a-z]+)', lambda x: (x.group(1) + '_' if x.group(1) else '') + x.group(2) + '_', name).rstrip('_').lower()
(tomado de aquí , ver ejemplo de trabajo en línea )
Wow, acabo de robar esto de los fragmentos de Django. ref http://djangosnippets.org/snippets/585/
Bastante elegante
camelcase_to_underscore = lambda str: re.sub(r'(?<=[a-z])[A-Z]|[A-Z](?=[^A-Z])', r'_\g<0>', str).lower().strip('_')
Ejemplo:
camelcase_to_underscore('ThisUser')
Devoluciones:
'this_user'
Un ejemplo horrendo que usa expresiones regulares (podría limpiar esto fácilmente :)):
def f(s):
return s.group(1).lower() + "_" + s.group(2).lower()
p = re.compile("([A-Z]+[a-z]+)([A-Z]?)")
print p.sub(f, "CamelCase")
print p.sub(f, "getHTTPResponseCode")
Sin embargo, funciona para getHTTPResponseCode.
Alternativamente, usando lambda:
p = re.compile("([A-Z]+[a-z]+)([A-Z]?)")
print p.sub(lambda x: x.group(1).lower() + "_" + x.group(2).lower(), "CamelCase")
print p.sub(lambda x: x.group(1).lower() + "_" + x.group(2).lower(), "getHTTPResponseCode")
EDITAR: También debería ser bastante fácil ver que hay margen de mejora para casos como "Prueba", porque el guión bajo se inserta incondicionalmente.
Aquí hay algo que hice para cambiar los encabezados en un archivo delimitado por tabulaciones. Estoy omitiendo la parte donde solo edité la primera línea del archivo. Puede adaptarlo a Python con bastante facilidad con la biblioteca re. Esto también incluye separar los números (pero mantiene los dígitos juntos). Lo hice en dos pasos porque era más fácil que decirle que no pusiera un guión bajo al comienzo de una línea o tabulación.
Paso uno ... encuentre letras mayúsculas o enteros precedidos por letras minúsculas y preceda con un guión bajo:
Buscar:
([a-z]+)([A-Z]|[0-9]+)
Reemplazo:
\1_\l\2/
Paso dos ... toma lo anterior y ejecútalo de nuevo para convertir todas las mayúsculas a minúsculas:
Buscar:
([A-Z])
Reemplazo (eso es barra invertida, L minúscula, barra invertida, una):
\l\1
Estaba buscando una solución al mismo problema, excepto que necesitaba una cadena; p.ej
"CamelCamelCamelCase" -> "Camel-camel-camel-case"
A partir de las buenas soluciones de dos palabras aquí, se me ocurrió lo siguiente:
"-".join(x.group(1).lower() if x.group(2) is None else x.group(1) \
for x in re.finditer("((^.[^A-Z]+)|([A-Z][^A-Z]+))", "stringToSplit"))
La mayor parte de la lógica complicada es evitar poner en minúscula la primera palabra. Aquí hay una versión más simple si no te importa alterar la primera palabra:
"-".join(x.group(1).lower() for x in re.finditer("(^[^A-Z]+|[A-Z][^A-Z]+)", "stringToSplit"))
Por supuesto, puede precompilar las expresiones regulares o unirse con guión bajo en lugar de guión, como se discutió en las otras soluciones.
Conciso sin expresiones regulares, pero HTTPResponseCode => httpresponse_code:
def from_camel(name):
"""
ThisIsCamelCase ==> this_is_camel_case
"""
name = name.replace("_", "")
_cas = lambda _x : [_i.isupper() for _i in _x]
seq = zip(_cas(name[1:-1]), _cas(name[2:]))
ss = [_x + 1 for _x, (_i, _j) in enumerate(seq) if (_i, _j) == (False, True)]
return "".join([ch + "_" if _x in ss else ch for _x, ch in numerate(name.lower())])
Sin ninguna biblioteca:
def camelify(out):
return (''.join(["_"+x.lower() if i<len(out)-1 and x.isupper() and out[i+1].islower()
else x.lower()+"_" if i<len(out)-1 and x.islower() and out[i+1].isupper()
else x.lower() for i,x in enumerate(list(out))])).lstrip('_').replace('__','_')
Un poco pesado, pero
CamelCamelCamelCase -> camel_camel_camel_case
HTTPRequest -> http_request
GetHTTPRequest -> get_http_request
getHTTPRequest -> get_http_request
Muy agradable RegEx propuesto en este sitio :
(?<!^)(?=[A-Z])
Si Python tiene un método de división de cadenas, debería funcionar ...
En Java:
String s = "loremIpsum";
words = s.split("(?<!^)(?=[A-Z])");
def convert(name):
return reduce(
lambda x, y: x + ('_' if y.isupper() else '') + y,
name
).lower()
Y si necesitamos cubrir un caso con una entrada ya sin camelar:
def convert(name):
return reduce(
lambda x, y: x + ('_' if y.isupper() and not x.endswith('_') else '') + y,
name
).lower()
En caso de que alguien necesite transformar un archivo fuente completo, aquí hay un script que lo hará.
# Copy and paste your camel case code in the string below
camelCaseCode ="""
cv2.Matx33d ComputeZoomMatrix(const cv2.Point2d & zoomCenter, double zoomRatio)
{
auto mat = cv2.Matx33d::eye();
mat(0, 0) = zoomRatio;
mat(1, 1) = zoomRatio;
mat(0, 2) = zoomCenter.x * (1. - zoomRatio);
mat(1, 2) = zoomCenter.y * (1. - zoomRatio);
return mat;
}
"""
import re
def snake_case(name):
s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
def lines(str):
return str.split("\n")
def unlines(lst):
return "\n".join(lst)
def words(str):
return str.split(" ")
def unwords(lst):
return " ".join(lst)
def map_partial(function):
return lambda values : [ function(v) for v in values]
import functools
def compose(*functions):
return functools.reduce(lambda f, g: lambda x: f(g(x)), functions, lambda x: x)
snake_case_code = compose(
unlines ,
map_partial(unwords),
map_partial(map_partial(snake_case)),
map_partial(words),
lines
)
print(snake_case_code(camelCaseCode))
He tenido bastante buena suerte con este:
import re
def camelcase_to_underscore(s):
return re.sub(r'(^|[a-z])([A-Z])',
lambda m: '_'.join([i.lower() for i in m.groups() if i]),
s)
Obviamente, esto puede ser optimizado para la velocidad de una pequeña poco si así lo desea.
import re
CC2US_RE = re.compile(r'(^|[a-z])([A-Z])')
def _replace(match):
return '_'.join([i.lower() for i in match.groups() if i])
def camelcase_to_underscores(s):
return CC2US_RE.sub(_replace, s)
Utilizar: str.capitalize()
para convertir la primera letra de la cadena (contenida en una cadena variable) a una letra mayúscula y devuelve la cadena completa.
Ejemplo: Comando: "hola" .capitalize () Salida: Hola