Пример:
>>> convert('CamelCase')
'camel_case'
Пример:
>>> convert('CamelCase')
'camel_case'
Это довольно подробно:
def convert(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()
Работает со всеми этими (и не вредит уже не-верблюдам версии):
>>> convert('CamelCase')
'camel_case'
>>> convert('CamelCamelCase')
'camel_camel_case'
>>> convert('Camel2Camel2Case')
'camel2_camel2_case'
>>> convert('getHTTPResponseCode')
'get_http_response_code'
>>> convert('get2HTTPResponseCode')
'get2_http_response_code'
>>> convert('HTTPResponseCode')
'http_response_code'
>>> convert('HTTPResponseCodeXYZ')
'http_response_code_xyz'
Или, если вы собираетесь называть это миллион раз, вы можете предварительно скомпилировать регулярные выражения:
first_cap_re = re.compile('(.)([A-Z][a-z]+)')
all_cap_re = re.compile('([a-z0-9])([A-Z])')
def convert(name):
s1 = first_cap_re.sub(r'\1_\2', name)
return all_cap_re.sub(r'\1_\2', s1).lower()
Не забудьте импортировать модуль регулярных выражений
import re
В индексе пакета есть библиотека флексов, которая может обрабатывать эти вещи для вас. В этом случае вы будете искать inflection.underscore()
:
>>> inflection.underscore('CamelCase')
'camel_case'
Я не знаю, почему все это так сложно.
в большинстве случаев простое выражение ([A-Z]+)
поможет вам
>>> 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'
Чтобы проигнорировать первого персонажа, просто добавьте взгляд позади (?!^)
>>> 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'
Если вы хотите разделить ALLCaps на all_caps и ожидать чисел в вашей строке, вам все равно не нужно выполнять два отдельных запуска, просто используйте |
Это выражение ((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))
может обрабатывать практически каждый сценарий в книге
>>> 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'
Все зависит от того, что вы хотите, поэтому используйте решение, которое наилучшим образом соответствует вашим потребностям, поскольку оно не должно быть слишком сложным.
NJoy!
Лично я не уверен, что что-либо, использующее регулярные выражения в python, может быть описано как элегантное. Большинство ответов здесь просто делают трюки типа "Code Golf" типа RE. Элегантное кодирование должно быть легко понято.
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'
stringcase - моя библиотека для этого; например:
>>> from stringcase import pascalcase, snakecase
>>> snakecase('FooBarBaz')
'foo_bar_baz'
>>> pascalcase('foo_bar_baz')
'FooBarBaz'
''.join('_'+c.lower() if c.isupper() else c for c in "DeathToCamelCase").strip('_')
re.sub("(.)([A-Z])", r'\1_\2', 'DeathToCamelCase').lower()
Я не понимаю, зачем использовать оба вызова .sub()?:) Я не регулярный гуру, но я упростил функцию к этому, что подходит для моих определенных потребностей, мне просто нужно было решение для преобразования camelCasedVars из запроса POST в vars_with_underscore:
def myFunc(...):
return re.sub('(.)([A-Z]{1})', r'\1_\2', "iTriedToWriteNicely").lower()
Он не работает с такими именами, как getHTTPResponse, потому что я слышал, что это плохое соглашение об именах (должно быть похоже на getHttpResponse, очевидно, что он намного легче запоминает эту форму).
Я думаю, что это решение более прямолинейно, чем предыдущие ответы:
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 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))
Какие выходы:
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-Z]?[a-z]+
: последовательные строчные буквы, которые необязательно начинаются с буквы верхнего регистра.[A-Z]{2,}(?=[A-Z][a-z]|\d|\W|$)
: Два или более последовательных прописных буквы. Он использует lookahead, чтобы исключить последнюю букву верхнего регистра, если за ней следует строчная буква.\d+
: последовательные номера.Используя re.findall
, мы получаем список отдельных "слов", которые могут быть преобразованы в нижний регистр и соединены с символами подчеркивания.
Здесь мое решение:
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)
Он поддерживает те угловые случаи, которые обсуждаются в комментариях. Например, он преобразует getHTTPResponseCode
в get_http_response_code
так, как должен.
Для удовольствия:
>>> 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'
Или больше для удовольствия:
>>> 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'
Не в стандартной библиотеке, но я нашел этот script, который, как представляется, содержит необходимые вам функции.
Это не элегантный метод, это очень "низкий уровень" реализации простой конечной машины (конечный автомат битполя), возможно, самый антипитонический режим для решения этой проблемы, однако модуль также реализует слишком сложный конечный автомат для разрешите эту простую задачу, поэтому я думаю, что это хорошее решение.
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 может анализировать все типы case: UpperSEQUENCEInterleaved, under_score, BIG_SYMBOLS и cammelCasedMethods
Я надеюсь, что это полезно
Так много сложных методов... Просто найдите всю группу "Titled" и присоедините ее вариант в нижнем регистре с подчеркиванием.
>>> 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'
Если вы не хотите, чтобы числа были похожи на первый символ группы или отдельной группы - вы можете использовать маску ([Az][a-z0-9]*)
.
Ух ты, я просто украл это из фрагментов Джанго. ссылка http://djangosnippets.org/snippets/585/
Довольно элегантно
camelcase_to_underscore = lambda str: re.sub(r'(?<=[a-z])[A-Z]|[A-Z](?=[^A-Z])', r'_\g<0>', str).lower().strip('_')
Пример:
camelcase_to_underscore('ThisUser')
Возвращает:
'this_user'
Использование регулярных выражений может быть самым коротким, но это решение более читаемо:
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
Легко адаптирован из https://stackoverflow.com/users/267781/matth которые используют генераторы.
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()
Взгляните на превосходный Schematics lib
https://github.com/schematics/schematics
Он позволяет создавать типизированные структуры данных, которые могут сериализоваться/десериализоваться с питона на Javascript, например:
class MapPrice(Model):
price_before_vat = DecimalType(serialized_name='priceBeforeVat')
vat_rate = DecimalType(serialized_name='vatRate')
vat = DecimalType()
total_price = DecimalType(serialized_name='totalPrice')
Я предпочитаю избегать повтора, если это возможно:
myString="ThisStringIsCamelCase"
''.join(['_'+i.lower() if i.isupper() else i for i in myString]).lstrip('_')
'this_string_is_camel_case'
Ужасный пример с использованием регулярных выражений (вы можете легко его очистить:)):
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")
Работает для getHTTPResponseCode, хотя!
Альтернативно, используя лямбда:
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: также должно быть довольно легко увидеть, что есть место для улучшения для таких случаев, как "Тест", потому что подчеркивание безоговорочно вставлено.
Очень хороший RegEx, предложенный на этот сайт:
(?<!^)(?=[A-Z])
Если python имеет метод String Split, он должен работать...
В Java:
String s = "loremIpsum";
words = s.split("(?<!^)(?=[A-Z])");
Здесь я сделал что-то, чтобы изменить заголовки в файле с разделителями табуляции. Я опускаю часть, где я только редактировал первую строку файла. Вы можете легко адаптировать его к Python с помощью библиотеки re. Это также включает разделение номеров (но сохраняет цифры вместе). Я сделал это за два шага, потому что это было проще, чем сказать, что не стоит подчеркивать в начале строки или вкладки.
Шаг первый... найдите заглавные буквы или целые числа, которым предшествуют строчные буквы, и предварите их символом подчеркивания:
Поиск:
([a-z]+)([A-Z]|[0-9]+)
Замена:
\1_\l\2/
Шаг второй... возьмите вышеуказанное и запустите его снова, чтобы преобразовать все кепки в нижний регистр:
Поиск:
([A-Z])
Замена (это обратная косая черта, нижний регистр L, обратная косая черта, одна):
\l\1
Я искал решение той же проблемы, за исключением того, что мне нужна цепочка; например.
"CamelCamelCamelCase" -> "Camel-camel-camel-case"
Начиная с приятных двухсловных решений, я придумал следующее:
"-".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"))
Большая часть сложной логики заключается в том, чтобы избежать сглаживания первого слова. Здесь более простая версия, если вы не возражаете изменить первое слово:
"-".join(x.group(1).lower() for x in re.finditer("(^[^A-Z]+|[A-Z][^A-Z]+)", "stringToSplit"))
Конечно, вы можете предварительно скомпилировать регулярные выражения или присоединиться к подчеркиванию вместо дефиса, как описано в других решениях.
Краткий без регулярных выражений, но 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())])
Без какой-либо библиотеки:
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('__','_')
Немного тяжело, но
CamelCamelCamelCase -> camel_camel_camel_case
HTTPRequest -> http_request
GetHTTPRequest -> get_http_request
getHTTPRequest -> get_http_request
Этот простой метод должен выполнять следующее задание:
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()
(взято из здесь, см. рабочий пример онлайн)
def convert(name):
return reduce(
lambda x, y: x + ('_' if y.isupper() else '') + y,
name
).lower()
И если нам нужно покрыть случай с уже неадаптивным вводом:
def convert(name):
return reduce(
lambda x, y: x + ('_' if y.isupper() and not x.endswith('_') else '') + y,
name
).lower()
На случай, если кому-то понадобится преобразовать полный исходный файл, вот скрипт, который это сделает.
# 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))
Мне очень повезло с этим:
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)
Это может быть оптимизировано для скорости, если вы хотите.
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)
def convert(camel_str):
temp_list = []
for letter in camel_str:
if letter.islower():
temp_list.append(letter)
else:
temp_list.append('_')
temp_list.append(letter)
result = "".join(temp_list)
return result.lower()
Используйте: str.capitalize()
для преобразования первой буквы строки (содержащейся в переменной str) в заглавную букву и возвращает всю строку.
Пример: Команда: "привет".capitalize() Вывод: Hello