30 ответов:
Это довольно основательная:
def convert(name): s1 = re.sub('(.)([A-Z][a-z]+)', r'_', name) return re.sub('([a-z0-9])([A-Z])', r'_', s1).lower()работает со всеми этими (и не вредит уже-un-cameled версии):
>>> 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'_', name) return all_cap_re.sub(r'_', s1).lower()не забудьте импортировать модуль регулярных выражений
import re
здесь перегиб библиотека в индексе пакет, который может обрабатывать эти вещи для вас. В этом случае, вы будете искать
inflection.underscore():>>> inflection.underscore('CamelCase') 'camel_case'
Я не знаю, почему все это так усложняет.
в большинстве случаев простое выражение
([A-Z]+)будет делать трюк>>> re.sub('([A-Z]+)', r'_','CamelCase').lower() '_camel_case' >>> re.sub('([A-Z]+)', r'_','camelCase').lower() 'camel_case' >>> re.sub('([A-Z]+)', r'_','camel2Case2').lower() 'camel2_case2' >>> re.sub('([A-Z]+)', r'_','camelCamelCase').lower() 'camel_camel_case' >>> re.sub('([A-Z]+)', r'_','getHTTPResponseCode').lower() 'get_httpresponse_code'чтобы игнорировать первый символ, просто добавьте взгляд назад
(?!^)>>> re.sub('(?!^)([A-Z]+)', r'_','CamelCase').lower() 'camel_case' >>> re.sub('(?!^)([A-Z]+)', r'_','CamelCamelCase').lower() 'camel_camel_case' >>> re.sub('(?!^)([A-Z]+)', r'_','Camel2Camel2Case').lower() 'camel2_camel2_case' >>> re.sub('(?!^)([A-Z]+)', r'_','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'_', 'getHTTPResponseCode').lower() 'get_http_response_code' >>> a.sub(r'_', 'get2HTTPResponseCode').lower() 'get2_http_response_code' >>> a.sub(r'_', 'get2HTTPResponse123Code').lower() 'get2_http_response123_code' >>> a.sub(r'_', 'HTTPResponseCode').lower() 'http_response_code' >>> a.sub(r'_', 'HTTPResponseCodeXYZ').lower() 'http_response_code_xyz'все зависит от того, что вы хотите, чтобы использовать решение, которое наилучшим образом соответствует вашим потребностям, а оно не должно быть чрезмерно сложным.
nJoy!
лично я не уверен, как что-либо с использованием регулярных выражений в python можно описать как элегантный. Большинство ответов здесь просто делают трюки типа" code golf". Элегантное кодирование должно быть легко понято.
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'
''.join('_'+c.lower() if c.isupper() else c for c in "DeathToCamelCase").strip('_') re.sub("(.)([A-Z])", r'_', 'DeathToCamelCase').lower()
stringcase Это моя библиотека для этого; например:
>>> from stringcase import pascalcase, snakecase >>> snakecase('FooBarBaz') 'foo_bar_baz' >>> pascalcase('foo_bar_baz') 'FooBarBaz'
Я не понимаю, почему с помощью обоих .sub () звонки? :) Я не гуру регулярных выражений, но я упростил функцию для этого, которая подходит для моих определенных потребностей, мне просто нужно было решение для преобразования camelCasedVars из POST-запроса в vars_with_underscore:
def myFunc(...): return re.sub('(.)([A-Z]{1})', r'_', "iTriedToWriteNicely").lower()Он не работает с такими именами, как getHTTPResponse, потому что я слышал, что это плохое соглашение об именах (должно быть похоже на getHttpResponse, очевидно, что гораздо легче запомнить эту форму).
вот мое решение:
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)он поддерживает те угловые случаи, которые обсуждаются в комментариях. Например, он преобразует
getHTTPResponseCodetoget_http_response_codeкак и должно быть.
Я думаю, что это решение является более простым, чем предыдущие ответы:
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))выходы:
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(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'
не в стандартной библиотеке, но я нашел этот скрипт это, кажется, содержит необходимую функциональность.
Это не элегантный метод, это очень "низкоуровневая" реализация простой государственной машины (bitfield state machine), возможно, самый анти-питонный режим для решения этой проблемы, однако модуль re также реализует слишком сложную государственную машину для решения этой простой задачи, поэтому я думаю, что это хорошее решение.
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 может анализировать все типы регистра: UpperSEQUENCEInterleaved, under_score, BIG_SYMBOLS и cammelCasedMethods
Я надеюсь, что это полезно
Ничего себе я только что украл это из фрагментов django. ref http://djangosnippets.org/snippets/585/
довольно элегантных
camelcase_to_underscore = lambda str: re.sub('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))', '_\1', 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()
взгляните на отличные схемы lib
https://github.com/schematics/schematics
Он позволяет создавать типизированные структуры данных, которые могут сериализовать / десериализовать от python до 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'
так много сложных методов... Просто найдите все "титульные" группы и присоединитесь к их нижнему варианту с подчеркиванием.
>>> 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'Если вы не хотите делать числа как первый символ группы или отдельной группы - вы можете использовать
([A-z][a-z0-9]*)маска.
ужасный пример использования регулярных выражений (вы могли бы легко это очистить :) ):
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: также должно быть довольно легко увидеть, что есть место для улучшения для таких случаев, как "тест", потому что подчеркивание безоговорочно вставлено.
вот что я сделал, чтобы изменить заголовки в файл с разделителями табуляции. Я пропускаю ту часть, где я редактировал только первую строку файла. Вы можете легко адаптировать его к Python с помощью библиотеки re. Это также включает в себя разделение чисел (но сохраняет цифры вместе). Я сделал это в два шага, потому что это было проще, чем сказать ему не ставить подчеркивание в начале строки или вкладки.
Шаг Первый...найти прописные буквы или целые числа, которым предшествуют строчные буквы, а перед ними подчеркивание:
найти:
([a-z]+)([A-Z]|[0-9]+)замена:
_\l/Шаг Второй...возьмите выше и запустите его снова, чтобы преобразовать все заглавные буквы в нижний регистр:
найти:
([A-Z])замена (это обратная косая черта, строчная L, обратная косая черта, один):
\l
Я искал решение той же проблемы, за исключением того, что мне нужна была цепочка; например
"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
очень хорошее регулярное выражение, предложенное на этот сайт:
(?<!^)(?=[A-Z])Если python имеет метод разделения строк, он должен работать...
В Java:
String s = "loremIpsum"; words = s.split("(?<!^)(?=[A-Z])");
этот простой метод должен выполнить задание:
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()и если нам нужно покрыть случай с уже-un-cameled входным сигналом:
def convert(name): return reduce( lambda x, y: x + ('_' if y.isupper() and not x.endswith('_') else '') + y, name ).lower()
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()
на всякий случай, если кому-то нужно преобразовать полный исходный файл, вот скрипт, который это сделает.
# 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'_', name) return re.sub('([a-z0-9])([A-Z])', r'_', 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)
использование:
str.capitalize()для преобразования первой буквы строки (содержащейся в переменной str) в заглавную букву и возвращает всю строку.пример: Команда: "привет".капитализировать() Вывод: Привет
Comments