Exemplo:
>>> convert('CamelCase')
'camel_case'
NotCamelCase
, masthisIs
Exemplo:
>>> convert('CamelCase')
'camel_case'
NotCamelCase
, masthisIs
Respostas:
import re
name = 'CamelCaseName'
name = re.sub(r'(?<!^)(?=[A-Z])', '_', name).lower()
print(name) # camel_case_name
Se você fizer isso muitas vezes e o acima for lento, compile o regex antecipadamente:
pattern = re.compile(r'(?<!^)(?=[A-Z])')
name = pattern.sub('_', name).lower()
Para lidar com casos mais avançados especialmente (isso não é mais reversível):
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
para notCamelCase
e / ou NotCamelCase
?
s2.replace('__', '_')
Há uma biblioteca de inflexão no índice do pacote que pode lidar com essas coisas para você. Nesse caso, você procuraria inflection.underscore()
:
>>> inflection.underscore('CamelCase')
'camel_case'
Não sei por que tudo isso é tão complicado.
na maioria dos casos, a expressão simples ([A-Z]+)
fará o truque
>>> 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 o primeiro caractere, basta adicionar look behind (?!^)
>>> 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'
Se você deseja separar ALLCaps em all_caps e esperar números em sua string, ainda não precisa executar duas execuções separadas, basta usar |
Esta expressão ((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))
pode lidar com praticamente todos os cenários do livro
>>> 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'
Tudo depende do que você deseja, então use a solução que melhor se adapte às suas necessidades, pois ela não deve ser muito complicada.
nJoy!
(?!^)
expressão sendo chamada de olhar para trás. A menos que eu esteja perdendo alguma coisa, o que realmente queremos aqui é um olhar negativo por trás, que deve ser expresso como (?<!^)
. Por razões que não posso compreender o seu negativo olhar em frente (?!^)
parece funcionar, também ...
"Camel2WARNING_Case_CASE"
torna - se "camel2_warning_case__case"
. Você pode adicionar um (?<!_)
lookbehind negativo, para resolvê-lo: re.sub('((?<=[a-z0-9])[A-Z]|(?!^)(?<!_)[A-Z](?=[a-z]))', r'_\1', "Camel2WARNING_Case_CASE").lower()
retornos 'camel2_warning_case_case'
(?!^)
foi chamado incorretamente de "olhar para trás" e deveria ter sido chamado de afirmação negativa à vista . Como essa boa explicação mostra, os lookaheads negativos geralmente vêm após a expressão que você está procurando. Então você pode pensar (?!^)
em "encontrar o ''
que <start of string>
não segue". De fato, um olhar negativo por trás também funciona: você pode pensar (?<!^)
em "encontrar ''
onde <start of string>
não precede".
stringcase é minha biblioteca preferida para isso; por exemplo:
>>> from stringcase import pascalcase, snakecase
>>> snakecase('FooBarBaz')
'foo_bar_baz'
>>> pascalcase('foo_bar_baz')
'FooBarBaz'
Pessoalmente, não tenho certeza de como algo usando expressões regulares em python pode ser descrito como elegante. A maioria das respostas aqui estão apenas fazendo truques do tipo "código golfe" RE. A codificação elegante deve ser facilmente entendida.
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'
+=
nas cordas é quase sempre uma má ideia. Anexar a uma lista e ''.join()
no final. Ou, neste caso, simplesmente juntá-lo com um sublinhado ...
Prefiro evitar, re
se possível:
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 e fazer as coisas apenas em uma linha usando apenas str.methods embutidos! É semelhante a essa resposta , mas evita o uso de fatias e adicionais if ... else
simplesmente removendo "_" potencialmente adicionado como primeiro caractere. Eu gosto mais disso.
6.81 µs ± 22.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
mas para essa resposta 2.51 µs ± 25.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
que é 2,5 vezes mais rápida! Amo isso!
Eu acho que essa solução é mais direta que as respostas 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))
Quais saídas:
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
A expressão regular corresponde a três padrões:
[A-Z]?[a-z]+
: Letras minúsculas consecutivas que, opcionalmente, começam com uma letra maiúscula.[A-Z]{2,}(?=[A-Z][a-z]|\d|\W|$)
: Duas ou mais letras maiúsculas consecutivas. Ele usa um lookahead para excluir a última letra maiúscula, se for seguida por uma letra minúscula.\d+
: Números consecutivos.Ao usar re.findall
, obtemos uma lista de "palavras" individuais que podem ser convertidas em minúsculas e associadas a sublinhados.
Não sei por que usar as duas chamadas .sub ()? :) Eu não sou um guru de regex, mas simplifiquei a função para este, que é adequada para minhas necessidades, só precisava de uma solução para converter camelCasedVars da solicitação POST em vars_with_underscore:
def myFunc(...):
return re.sub('(.)([A-Z]{1})', r'\1_\2', "iTriedToWriteNicely").lower()
Ele não funciona com nomes como getHTTPResponse, porque ouvi dizer que é uma má convenção de nomenclatura (deve ser como getHttpResponse, é óbvio que é muito mais fácil memorizar este formulário).
'HTTPConnectionFactory'
, seu código produz 'h_tt_pconnection_factory'
, código de resposta aceita produz'http_connection_factory'
Aqui está a minha solução:
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)
Ele suporta os casos de canto discutidos nos comentários. Por exemplo, ele será convertido getHTTPResponseCode
para o get_http_response_code
que deveria.
Para se divertir:
>>> 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'
Ou, mais pela diversão:
>>> 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
foi descontinuado por idades . Use em ''.join(..)
vez disso.
O uso de expressões regulares pode ser o mais curto, mas esta solução é muito mais legível:
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 ... Basta encontrar todo o grupo "Titled" e associar sua variante mais baixa com sublinhado.
>>> 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'
Se você não deseja criar números como o primeiro caractere do grupo ou o grupo separado - pode usar a ([A-z][a-z0-9]*)
máscara.
Não está na biblioteca padrão, mas encontrei este script que parece conter a funcionalidade necessária.
Este não é um método elegante, é uma implementação de 'nível muito baixo' de uma máquina de estado simples (máquina de estado de campo de bits), possivelmente o modo mais anti-pitonico para resolver isso, no entanto, o módulo reimplementar também implementa uma máquina de estado muito complexa para resolver essa questão simples. tarefa, então eu acho que esta é uma boa solução.
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 pode analisar todos os tipos de caso: UpperSEQUENCEInterleaved, under_score, BIG_SYMBOLS e cammelCasedMethods
Espero que seja útil
Levemente adaptado de https://stackoverflow.com/users/267781/matth que usa geradores.
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()
Dê uma olhada na excelente biblioteca de Esquemas
https://github.com/schematics/schematics
Ele permite que você crie estruturas de dados digitados que podem serializar / desserializar do tipo python para Javascript, por exemplo:
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 simples deve fazer o trabalho:
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()
(extraído daqui , veja o exemplo de trabalho online )
Uau, acabei de roubar isso de trechos 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('_')
Exemplo:
camelcase_to_underscore('ThisUser')
Devoluções:
'this_user'
Um exemplo horrendo usando expressões regulares (você pode facilmente limpar isso :)):
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")
Funciona para getHTTPResponseCode!
Como alternativa, 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")
EDIT: Também deve ser bastante fácil ver que há espaço para melhorias em casos como "Teste", porque o sublinhado é inserido incondicionalmente.
Aqui está algo que eu fiz para alterar os cabeçalhos em um arquivo delimitado por tabulações. Estou omitindo a parte em que apenas editei a primeira linha do arquivo. Você pode adaptá-lo facilmente ao Python com a re library. Isso também inclui separar números (mas mantém os dígitos juntos). Fiz isso em duas etapas, porque era mais fácil do que dizer para não colocar um sublinhado no início de uma linha ou tabulação.
Etapa 1 ... encontre letras maiúsculas ou números inteiros precedidos por letras minúsculas e preceda-os com um sublinhado:
Procurar:
([a-z]+)([A-Z]|[0-9]+)
Substituição:
\1_\l\2/
Etapa 2 ... execute o procedimento acima e execute-o novamente para converter todos os limites em minúsculas:
Procurar:
([A-Z])
Substituição (barra invertida, L minúsculo, barra invertida, uma):
\l\1
Eu estava procurando uma solução para o mesmo problema, exceto que precisava de uma corrente; por exemplo
"CamelCamelCamelCase" -> "Camel-camel-camel-case"
Começando pelas boas soluções de duas palavras aqui, criei o seguinte:
"-".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"))
A maior parte da lógica complicada é evitar minúsculas na primeira palavra. Aqui está uma versão mais simples, se você não se importa de alterar a primeira palavra:
"-".join(x.group(1).lower() for x in re.finditer("(^[^A-Z]+|[A-Z][^A-Z]+)", "stringToSplit"))
Obviamente, você pode pré-compilar as expressões regulares ou juntar-se ao sublinhado em vez do hífen, conforme discutido nas outras soluções.
Conciso sem expressões regulares, mas HTTPResponseCode => activationpresponse_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())])
Sem nenhuma 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('__','_')
Um pouco pesado, mas
CamelCamelCamelCase -> camel_camel_camel_case
HTTPRequest -> http_request
GetHTTPRequest -> get_http_request
getHTTPRequest -> get_http_request
RegEx muito bom proposto neste site :
(?<!^)(?=[A-Z])
Se o python tiver um método String Split, ele deve funcionar ...
Em Java:
String s = "loremIpsum";
words = s.split("(?<!^)(?=[A-Z])");
Caso alguém precise transformar um arquivo de origem completo, aqui está um script que fará isso.
# 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))
Eu tive muita sorte com 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)
Isso pode, obviamente, ser otimizado para velocidade de um minúsculo pouco se você quiser.
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)
Usar: str.capitalize()
para converter a primeira letra da string (contida na variável str) em uma letra maiúscula e retorna a string inteira.
Exemplo: Comando: "olá" .capitalize () Saída: Olá