Przykład:
>>> convert('CamelCase')
'camel_case'
NotCamelCase
alethisIs
Przykład:
>>> convert('CamelCase')
'camel_case'
NotCamelCase
alethisIs
Odpowiedzi:
import re
name = 'CamelCaseName'
name = re.sub(r'(?<!^)(?=[A-Z])', '_', name).lower()
print(name) # camel_case_name
Jeśli robisz to wiele razy, a powyższe jest wolne, skompiluj wcześniej wyrażenie regularne:
pattern = re.compile(r'(?<!^)(?=[A-Z])')
name = pattern.sub('_', name).lower()
Specjalnie do obsługi bardziej zaawansowanych przypadków (nie jest to już odwracalne):
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
na notCamelCase
i / lub NotCamelCase
?
s2.replace('__', '_')
W indeksie pakietów znajduje się biblioteka fleksji, która może obsłużyć te rzeczy za Ciebie. W takim przypadku będziesz szukał inflection.underscore()
:
>>> inflection.underscore('CamelCase')
'camel_case'
Nie wiem, dlaczego to wszystko jest takie skomplikowane.
w większości przypadków proste wyrażenie ([A-Z]+)
wystarczy
>>> 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'
Aby zignorować pierwszą postać, wystarczy spojrzeć za siebie (?!^)
>>> 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'
Jeśli chcesz oddzielić ALLCaps od all_caps i oczekujesz liczb w swoim ciągu, nadal nie musisz wykonywać dwóch osobnych przebiegów, po prostu użyj |
tego wyrażenia, ((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))
które poradzi sobie z każdym scenariuszem w książce
>>> 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'
Wszystko zależy od tego, czego chcesz, więc skorzystaj z rozwiązania, które najlepiej odpowiada Twoim potrzebom, ponieważ nie powinno być zbyt skomplikowane.
nJoy!
(?!^)
że nazywa się to „spojrzenie za siebie”. Chyba, że czegoś mi brakuje, tak naprawdę chcemy tutaj negatywnego spojrzenia, które należy wyrazić jako (?<!^)
. Z powodów, dla których nie rozumiem, wasze negatywne spojrzenie w przyszłość również (?!^)
wydaje się działać ...
"Camel2WARNING_Case_CASE"
staje się "camel2_warning_case__case"
. Możesz dodać (?<!_)
negatywny wygląd, aby go rozwiązać: re.sub('((?<=[a-z0-9])[A-Z]|(?!^)(?<!_)[A-Z](?=[a-z]))', r'_\1', "Camel2WARNING_Case_CASE").lower()
zwraca 'camel2_warning_case_case'
(?!^)
został niepoprawnie nazwany „patrz za siebie” i zamiast tego powinien zostać nazwany negatywnym stwierdzeniem z wyprzedzeniem . Jak pokazuje to miłe wyjaśnienie , negatywne spojrzenia zwykle pojawiają się po szukanym wyrażeniu. Więc można myśleć (?!^)
jako „znajdź ''
gdzie <start of string>
nie wynika”. Rzeczywiście, negatywny lookbehind również działa: można myśleć (?<!^)
jako „znaleźć ''
gdzie <start of string>
nie wyprzedza”.
stringcase jest moją biblioteką do tego celu; na przykład:
>>> from stringcase import pascalcase, snakecase
>>> snakecase('FooBarBaz')
'foo_bar_baz'
>>> pascalcase('foo_bar_baz')
'FooBarBaz'
Osobiście nie jestem pewien, jak cokolwiek używającego wyrażeń regularnych w pythonie można określić jako eleganckie. Większość odpowiedzi tutaj polega na wykonywaniu sztuczek RE typu „golf golfowy”. Eleganckie kodowanie powinno być łatwe do zrozumienia.
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'
+=
na sznurkach jest prawie zawsze złym pomysłem. Dołącz do listy i ''.join()
na końcu. Lub w tym przypadku po prostu dołącz do niego podkreślenie ...
Wolę unikać, re
jeśli to możliwe:
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
biblioteki i robienia rzeczy tylko w jednym wierszu przy użyciu wbudowanych metod str.methods! Jest podobny do tej odpowiedzi , ale unika używania krojenia i dodatkowego if ... else
, po prostu usuwając potencjalnie dodane „_” jako pierwszy znak. Najbardziej to lubię.
6.81 µs ± 22.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
ale za tę odpowiedź, 2.51 µs ± 25.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
która jest 2,5 razy szybsza! Kocham to!
Myślę, że to rozwiązanie jest prostsze niż poprzednie odpowiedzi:
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))
Które wyjścia:
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
Wyrażenie regularne pasuje do trzech wzorców:
[A-Z]?[a-z]+
: Kolejne małe litery, które opcjonalnie zaczynają się od dużej litery.[A-Z]{2,}(?=[A-Z][a-z]|\d|\W|$)
: Dwie lub więcej kolejnych wielkich liter. Wykorzystuje lookahead, aby wykluczyć ostatnią wielką literę, jeśli następuje po niej mała litera.\d+
: Kolejne numery.Korzystając z niego re.findall
, otrzymujemy listę pojedynczych „słów”, które można przekonwertować na małe litery i połączyć z podkreślnikami.
Nie mam pojęcia, dlaczego używam obu wywołań .sub ()? :) Nie jestem guru wyrażeń regularnych, ale uprościłem funkcję do tej, która jest odpowiednia dla moich niektórych potrzeb, po prostu potrzebowałem rozwiązania do konwersji camelCasedVars z żądania POST na vars_with_underscore:
def myFunc(...):
return re.sub('(.)([A-Z]{1})', r'\1_\2', "iTriedToWriteNicely").lower()
Nie działa z takimi nazwami jak getHTTPResponse, ponieważ słyszałem, że jest to zła konwencja nazewnictwa (powinna być jak getHTTPResponse, to oczywiście, że o wiele łatwiej jest zapamiętać ten formularz).
'HTTPConnectionFactory'
, tworzy kod 'h_tt_pconnection_factory'
, kod z zaakceptowanej odpowiedzi tworzy'http_connection_factory'
Oto moje rozwiązanie:
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)
Obsługuje te przypadki narożne omówione w komentarzach. Na przykład, będzie ona przekształcić getHTTPResponseCode
się get_http_response_code
tak jak powinien.
Dla zabawy:
>>> 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'
Lub więcej dla zabawy:
>>> 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
był przestarzały od wieków . Użyj ''.join(..)
zamiast tego.
Używanie wyrażeń regularnych może być najkrótsze, ale to rozwiązanie jest o wiele bardziej czytelne:
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
Tak wiele skomplikowanych metod ... Po prostu znajdź całą grupę „Tytułowa” i dołącz do jej małej wersji z podkreśleniem.
>>> 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'
Jeśli nie chcesz tworzyć liczb takich jak pierwszy znak grupy lub oddzielnej grupy - możesz użyć ([A-z][a-z0-9]*)
maski.
Nie w standardowej bibliotece, ale znalazłem ten skrypt, który wydaje się zawierać potrzebną funkcjonalność.
To nie jest elegancka metoda, jest to implementacja bardzo niskiego poziomu prostej maszyny stanów (maszyna stanu bitfielda), być może najbardziej anty-pythonowy tryb do rozwiązania tego problemu, jednak moduł ponownie implementuje zbyt złożoną maszynę stanu do rozwiązania tej prostej zadanie, więc myślę, że to dobre rozwiązanie.
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))
symbol podziału może analizować wszystkie typy przypadków: UpperSEQUENCEInterleaved, under_score, BIG_SYMBOLS i cammelCasedMethods
Mam nadzieję, że się przyda
Lekko dostosowany z https://stackoverflow.com/users/267781/matth, którzy korzystają z generatorów.
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()
Spójrz na doskonałą bibliotekę Schematics
https://github.com/schematics/schematics
Pozwala tworzyć struktury danych typowanych, które mogą serializować / deserializować z języka Python do Javascript, np .:
class MapPrice(Model):
price_before_vat = DecimalType(serialized_name='priceBeforeVat')
vat_rate = DecimalType(serialized_name='vatRate')
vat = DecimalType()
total_price = DecimalType(serialized_name='totalPrice')
Ta prosta metoda powinna wykonać zadanie:
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()
(wzięte stąd , patrz działający przykład online )
Wow, właśnie ukradłem to z fragmentów django. ref http://djangosnippets.org/snippets/585/
Całkiem elegancki
camelcase_to_underscore = lambda str: re.sub(r'(?<=[a-z])[A-Z]|[A-Z](?=[^A-Z])', r'_\g<0>', str).lower().strip('_')
Przykład:
camelcase_to_underscore('ThisUser')
Zwroty:
'this_user'
Przerażający przykład z użyciem wyrażeń regularnych (można to łatwo wyczyścić :)):
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")
Działa jednak dla getHTTPResponseCode!
Alternatywnie, używając 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")
EDYCJA: Powinno być również dość łatwo zauważyć, że jest miejsce na ulepszenia dla przypadków takich jak „Test”, ponieważ podkreślenie jest bezwarunkowo wstawione.
Oto coś, co zrobiłem, aby zmienić nagłówki w pliku rozdzielanym tabulatorami. Pomijam część, w której edytowałem tylko pierwszą linię pliku. Możesz łatwo dostosować go do Pythona za pomocą biblioteki re. Obejmuje to także oddzielanie liczb (ale utrzymuje cyfry razem). Zrobiłem to w dwóch krokach, ponieważ było to łatwiejsze niż mówienie, aby nie umieszczać podkreślenia na początku linii lub tabulacji.
Krok pierwszy ... znajdź wielkie litery lub liczby całkowite poprzedzone małymi literami i poprzedź je znakiem podkreślenia:
Szukaj:
([a-z]+)([A-Z]|[0-9]+)
Zastąpienie:
\1_\l\2/
Krok drugi ... weź powyższe i uruchom ponownie, aby przekonwertować wszystkie wielkie litery na małe:
Szukaj:
([A-Z])
Zastąpienie (to odwrotny ukośnik, mała litera L, odwrotny ukośnik, jeden):
\l\1
Szukałem rozwiązania tego samego problemu, tyle że potrzebowałem łańcucha; na przykład
"CamelCamelCamelCase" -> "Camel-camel-camel-case"
Zaczynając od fajnych rozwiązań dwóch słów tutaj, wymyśliłem:
"-".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"))
Większość skomplikowanej logiki polega na unikaniu zmniejszania pierwszego słowa. Oto prostsza wersja, jeśli nie masz nic przeciwko zmianie pierwszego słowa:
"-".join(x.group(1).lower() for x in re.finditer("(^[^A-Z]+|[A-Z][^A-Z]+)", "stringToSplit"))
Oczywiście można wstępnie kompilować wyrażenia regularne lub łączyć z podkreśleniem zamiast łącznika, jak omówiono w innych rozwiązaniach.
Zwięzłe bez wyrażeń regularnych, ale 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())])
Bez biblioteki:
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('__','_')
Trochę ciężko, ale
CamelCamelCamelCase -> camel_camel_camel_case
HTTPRequest -> http_request
GetHTTPRequest -> get_http_request
getHTTPRequest -> get_http_request
Bardzo ładne RegEx zaproponowane na tej stronie :
(?<!^)(?=[A-Z])
Jeśli python ma metodę podziału ciągu, powinien działać ...
W Javie:
String s = "loremIpsum";
words = s.split("(?<!^)(?=[A-Z])");
Na wypadek, gdyby ktoś musiał przekształcić pełny plik źródłowy, oto skrypt, który to zrobi.
# 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))
Miałem dość szczęścia z tym:
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)
Może to oczywiście być zoptymalizowane pod kątem szybkości jest malutki kawałek, jeśli chcesz.
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)
Posługiwać się: str.capitalize()
do konwersji pierwszej litery ciągu (zawartej w zmiennej str) na wielką literę i zwraca cały ciąg.
Przykład: Polecenie: „hello” .capitalize () Dane wyjściowe: Hello