Esempio:
>>> convert('CamelCase')
'camel_case'
NotCamelCase
mathisIs
Esempio:
>>> convert('CamelCase')
'camel_case'
NotCamelCase
mathisIs
Risposte:
import re
name = 'CamelCaseName'
name = re.sub(r'(?<!^)(?=[A-Z])', '_', name).lower()
print(name) # camel_case_name
Se lo fai molte volte e quanto sopra è lento, compila il regex in anticipo:
pattern = re.compile(r'(?<!^)(?=[A-Z])')
name = pattern.sub('_', name).lower()
Per gestire appositamente i casi più avanzati (questo non è più reversibile):
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
in notCamelCase
e / o NotCamelCase
?
s2.replace('__', '_')
C'è una libreria di flesso nell'indice del pacchetto che può gestire queste cose per te. In questo caso, dovresti cercare inflection.underscore()
:
>>> inflection.underscore('CamelCase')
'camel_case'
Non so perché siano così complicati.
per la maggior parte dei casi, l'espressione semplice ([A-Z]+)
farà il trucco
>>> 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'
Per ignorare il primo personaggio basta aggiungere uno sguardo dietro (?!^)
>>> 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 vuoi separare ALLCaps in all_caps e aspettarti numeri nella tua stringa, non hai ancora bisogno di fare due esecuzioni separate, usa solo |
Questa espressione ((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))
può gestire praticamente tutti gli scenari 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'
Tutto dipende da ciò che desideri, quindi usa la soluzione più adatta alle tue esigenze in quanto non dovrebbe essere eccessivamente complicata.
nJoy!
(?!^)
dall'espressione che veniva chiamata uno sguardo dietro. A meno che non mi manchi qualcosa, quello che vogliamo davvero qui è uno sguardo negativo che dovrebbe essere espresso come (?<!^)
. Per motivi che non riesco a capire, anche il tuo sguardo negativo (?!^)
sembra funzionare ...
"Camel2WARNING_Case_CASE"
diventa "camel2_warning_case__case"
. Puoi aggiungere un (?<!_)
aspetto negativo, per risolverlo: re.sub('((?<=[a-z0-9])[A-Z]|(?!^)(?<!_)[A-Z](?=[a-z]))', r'_\1', "Camel2WARNING_Case_CASE").lower()
restituisce 'camel2_warning_case_case'
(?!^)
stato erroneamente definito uno "sguardo dietro" e avrebbe dovuto invece essere definito un'asserzione negativa . Come mostra questa bella spiegazione , i punti di vista negativi di solito vengono dopo l'espressione che stai cercando. Quindi puoi pensare (?!^)
a "trova ''
dove <start of string>
non segue". In effetti, funziona anche un aspetto negativo: puoi pensare (?<!^)
a "trova ''
dove <start of string>
non precede".
stringcase è la mia biblioteca preferita per questo; per esempio:
>>> from stringcase import pascalcase, snakecase
>>> snakecase('FooBarBaz')
'foo_bar_baz'
>>> pascalcase('foo_bar_baz')
'FooBarBaz'
Preferisco evitare re
se possibile:
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
libreria e di fare le cose in una sola riga usando solo i metodi str. È simile a questa risposta , ma evita di utilizzare lo slicing e ulteriori if ... else
semplicemente eliminando "_" potenzialmente aggiunto come primo carattere. Mi piace di più.
6.81 µs ± 22.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
ma per questa risposta 2.51 µs ± 25.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
che è 2,5 volte più veloce! Ama questo!
Personalmente non sono sicuro di come possa essere descritto come elegante qualsiasi cosa che usi espressioni regolari in Python. La maggior parte delle risposte qui stanno solo facendo trucchi di tipo RE "code golf". La codifica elegante dovrebbe essere facilmente comprensibile.
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'
+=
sulle stringhe è quasi sempre una cattiva idea. Aggiungi a un elenco e ''.join()
alla fine. O in questo caso, semplicemente
''.join('_'+c.lower() if c.isupper() else c for c in "DeathToCamelCase").strip('_')
re.sub("(.)([A-Z])", r'\1_\2', 'DeathToCamelCase').lower()
Penso che questa soluzione sia più semplice delle risposte precedenti:
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))
Quali uscite:
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
L'espressione regolare corrisponde a tre modelli:
[A-Z]?[a-z]+
: Lettere minuscole consecutive che iniziano facoltativamente con una lettera maiuscola.[A-Z]{2,}(?=[A-Z][a-z]|\d|\W|$)
: Due o più lettere maiuscole consecutive. Usa un lookahead per escludere l'ultima lettera maiuscola se è seguita da una lettera minuscola.\d+
: Numeri consecutivi.Usando re.findall
otteniamo un elenco di singole "parole" che possono essere convertite in lettere minuscole e unite con caratteri di sottolineatura.
Non ho idea del perché usare entrambe le chiamate .sub ()? :) Non sono regex guru, ma ho semplificato la funzione di questo, che è adatto alle mie certe esigenze, avevo solo bisogno di una soluzione per convertire camelCasedVars dalla richiesta POST in vars_with_underscore:
def myFunc(...):
return re.sub('(.)([A-Z]{1})', r'\1_\2', "iTriedToWriteNicely").lower()
Non funziona con nomi come getHTTPResponse, perché ho sentito che è una cattiva convenzione di denominazione (dovrebbe essere come getHttpResponse, è ovviamente che è molto più facile memorizzare questo modulo).
'HTTPConnectionFactory'
, il tuo codice produce 'h_tt_pconnection_factory'
, il codice dalla risposta accettata produce'http_connection_factory'
Ecco la mia soluzione:
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)
Supporta quei casi angolari discussi nei commenti. Per esempio, sarà convertito getHTTPResponseCode
a get_http_response_code
come dovrebbe.
Per divertirci:
>>> 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, più per divertimento:
>>> 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
è stato deprecato per anni . Usa ''.join(..)
invece.
L'uso di regex potrebbe essere il più breve, ma questa soluzione è molto più leggibile:
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
Tanti metodi complicati ... Basta trovare tutto il gruppo "Titolato" e unire la sua variante in maiuscolo con trattino basso.
>>> 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 non vuoi creare numeri come il primo carattere di un gruppo o un gruppo separato, puoi usare la ([A-z][a-z0-9]*)
maschera.
Non nella libreria standard, ma ho trovato questo script che sembra contenere le funzionalità necessarie.
Questo non è un metodo elegante, è un'implementazione molto "di basso livello" di una semplice macchina a stati (bitfield state machine), probabilmente la modalità più anti-pitonica per risolvere questo problema, tuttavia re module implementa anche una macchina a stati troppo complessa per risolvere questo semplice compito, quindi penso che questa sia una buona soluzione.
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 può analizzare tutti i tipi di casi: UpperSEQUENCEInterleaved, under_score, BIG_SYMBOLS e cammelCasedMethods
Spero sia utile
Adattato leggermente da https://stackoverflow.com/users/267781/matth che utilizzano i generatori.
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()
Dai un'occhiata all'eccellente lib Schematics
https://github.com/schematics/schematics
Ti consente di creare strutture di dati tipizzate che possono serializzare / deserializzare da Python a JavaScript, ad esempio:
class MapPrice(Model):
price_before_vat = DecimalType(serialized_name='priceBeforeVat')
vat_rate = DecimalType(serialized_name='vatRate')
vat = DecimalType()
total_price = DecimalType(serialized_name='totalPrice')
Questo semplice metodo dovrebbe fare il lavoro:
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()
(preso da qui , vedi esempio di lavoro online )
Wow, l'ho appena rubato dai frammenti di Django. arbitro http://djangosnippets.org/snippets/585/
Abbastanza elegante
camelcase_to_underscore = lambda str: re.sub(r'(?<=[a-z])[A-Z]|[A-Z](?=[^A-Z])', r'_\g<0>', str).lower().strip('_')
Esempio:
camelcase_to_underscore('ThisUser')
Ritorna:
'this_user'
Un esempio orribile che usa espressioni regolari (potresti facilmente ripulirlo :)):
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")
Funziona con getHTTPResponseCode però!
In 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: Dovrebbe anche essere abbastanza facile vedere che c'è spazio per miglioramenti per casi come "Test", perché il carattere di sottolineatura è inserito incondizionatamente.
Ecco qualcosa che ho fatto per modificare le intestazioni in un file delimitato da tabulazioni. Ometto la parte in cui ho modificato solo la prima riga del file. Potresti adattarlo a Python abbastanza facilmente con la nuova libreria. Ciò include anche la separazione dei numeri (ma mantiene le cifre insieme). L'ho fatto in due passaggi perché era più semplice che dirgli di non mettere un trattino basso all'inizio di una riga o di una scheda.
Primo passo ... trova lettere maiuscole o numeri interi preceduti da lettere minuscole e precedili con un trattino basso:
Ricerca:
([a-z]+)([A-Z]|[0-9]+)
Sostituzione:
\1_\l\2/
Passaggio due ... prendi quanto sopra ed eseguilo di nuovo per convertire tutti i maiuscoli in minuscolo:
Ricerca:
([A-Z])
Sostituzione (barra rovesciata, L minuscola, barra rovesciata, una):
\l\1
Stavo cercando una soluzione allo stesso problema, tranne per il fatto che avevo bisogno di una catena; per esempio
"CamelCamelCamelCase" -> "Camel-camel-camel-case"
Partendo dalle simpatiche soluzioni di due parole qui, ho pensato a quanto segue:
"-".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 maggior parte della logica complicata consiste nell'evitare di mettere in minuscolo la prima parola. Ecco una versione più semplice se non ti dispiace alterare la prima parola:
"-".join(x.group(1).lower() for x in re.finditer("(^[^A-Z]+|[A-Z][^A-Z]+)", "stringToSplit"))
Ovviamente, puoi precompilare le espressioni regolari o unirle con trattino basso anziché trattino, come discusso nelle altre soluzioni.
Conciso senza espressioni regolari, ma 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())])
Senza alcuna 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 po 'pesante, ma
CamelCamelCamelCase -> camel_camel_camel_case
HTTPRequest -> http_request
GetHTTPRequest -> get_http_request
getHTTPRequest -> get_http_request
Molto bello RegEx proposto su questo sito :
(?<!^)(?=[A-Z])
Se python ha un metodo Split String, dovrebbe funzionare ...
In Java:
String s = "loremIpsum";
words = s.split("(?<!^)(?=[A-Z])");
Nel caso in cui qualcuno debba trasformare un file sorgente completo, ecco uno script che lo farà.
# 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))
Ho avuto abbastanza fortuna con questo:
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)
Questo potrebbe ovviamente essere ottimizzato per la velocità di un piccolo po 'se si vuole.
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)
Uso: str.capitalize()
per convertire la prima lettera della stringa (contenuta nella variabile str) in una lettera maiuscola e restituisce l'intera stringa.
Esempio: comando: "ciao" .capitalize () Output: Ciao