Элегантная функция Python для преобразования CamelCase в snake_case?

333

Пример:

>>> convert('CamelCase')
'camel_case'
Шридхар Ратнакумар
источник
28
Чтобы преобразовать в другом направлении, см. Этот другой вопрос stackoverflow.
Натан
10
NB это NotCamelCaseноthisIs
Мэтт Ричардс
5
@MattRichards Это спорный вопрос. wiki
NO_NAME
@MattRichards Например, в Java они используют оба, CamelCase используется для именования определений классов, а camelCase используется для именования инициализированных переменных.
темноты

Ответы:

798

Верблюжий кейс для змей

import re

name = 'CamelCaseName'
name = re.sub(r'(?<!^)(?=[A-Z])', '_', name).lower()
print(name)  # camel_case_name

Если вы делаете это много раз, а вышеприведенное медленно, скомпилируйте регулярное выражение заранее:

pattern = re.compile(r'(?<!^)(?=[A-Z])')
name = pattern.sub('_', name).lower()

Специально для обработки более сложных случаев (это уже необратимо):

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
эпост
источник
1
Это решение дает сбой в следующих случаях: _test_Method, __test__Method, _Test, getHTTPresponseCode, __CamelCase и _Camel_Case.
freegnu
6
как насчет обратного? Преобразовать not_camel_caseв notCamelCaseи / или NotCamelCase?
john2x
9
Чтобы избежать двойных подчеркиваний при конвертации, например, camel_Case, добавьте эту строку:s2.replace('__', '_')
Маркус Альберг
2
Обратите внимание, что это не очень обратимо. getHTTPResponseCode должен преобразоваться в get_h_t_t_p_response_code. getHttpResponseCode должен преобразовываться в get_http_response_code
K2xL
4
@AnmolSinghJaggi Первое регулярное выражение обрабатывает крайний край аббревиатуры, за которым следует другое слово (например, «HTTPResponse» -> «HTTP_Response») ИЛИ более нормальный регистр начального строчного слова, за которым следует заглавное слово (например, «getResponse» -> » get_Response ". Второе регулярное выражение обрабатывает обычный регистр двух неакронимов (например," ResponseCode "->" Response_Code "), за которым следует последний вызов, чтобы прописать все. Таким образом," getHTTPResponseCode "->" getHTTP_ResponseCode "->" get_HTTP_Response_C > "get_http_response_code"
Джефф Мозер
188

В индексе пакета есть библиотека перегибов, которая может обработать эти вещи для вас. В этом случае вы будете искать inflection.underscore():

>>> inflection.underscore('CamelCase')
'camel_case'
Брэд Кох
источник
44
Я не понимаю, почему люди голосуют за использование пользовательских функций, когда есть отличная библиотека, которая выполняет эту задачу. Мы не должны изобретать велосипед.
Оден
88
@oden Может быть, потому что добавление совершенно новой зависимости для выполнения функции однострочной функции является хрупким чрезмерным уничтожением?
Сесил Карри
11
Например, уверен, что это излишне. В более широком приложении нет необходимости заново изобретать и запутывать колесо.
Брэд Кох
11
Регулярные выражения часто возвращаются в «одну строку», поэтому при правильном тестировании это намного больше, чем одна строка.
Studgeek
12
@CecilCurry: Я уверен, что вы отличный программист, но я не уверен, что нет случаев, которые вы не рассматривали - просто посмотрите на другие ответы здесь для примеров. Вот почему я всегда выбираю библиотеку, потому что это совокупный опыт многих разработчиков, кроме меня.
Майкл Шепер
105

Я не знаю, почему все это так сложно.

в большинстве случаев простое выражение ([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!

nickl-
источник
1
Последняя итерация самая умная, ИМО. Мне потребовалось немного времени, чтобы понять, что он заменяет только один символ в начале каждого слова - и это только потому, что подход отличался от того, который я придумал сам. Красиво сделано.
Джастин Миллер
2
Я был озадачен (?!^)выражением, которое называли «оглядывающимся сзади». Если я чего-то не упустил, то, что мы действительно хотим здесь, это негативный взгляд, который должен быть выражен как (?<!^). По причинам, которые я не могу понять, твой негативный прогноз, (?!^)похоже, тоже работает ...
Apteryx
7
Это плохо обрабатывает существовавшие ранее подчеркивания: "Camel2WARNING_Case_CASE"становится "camel2_warning_case__case". Вы можете добавить (?<!_)отрицательный взгляд назад, чтобы решить: re.sub('((?<=[a-z0-9])[A-Z]|(?!^)(?<!_)[A-Z](?=[a-z]))', r'_\1', "Camel2WARNING_Case_CASE").lower() возвращение 'camel2_warning_case_case'
luckydonald
@Apteryx Вы правы, (?!^)неправильно были названы «взглядом позади», и вместо этого должны были быть названы негативным прогнозом . Как показывает это приятное объяснение , негативные взгляды обычно появляются после искомого выражения. Таким образом, вы можете думать (?!^)как «найти, ''где <start of string>не следует». В самом деле, отрицательный взгляд также работает: вы можете думать, (?<!^)как «найти, ''где <start of string>не предшествует».
Натаниэль Джонс
18

stringcase - моя библиотека для этого; например:

>>> from stringcase import pascalcase, snakecase
>>> snakecase('FooBarBaz')
'foo_bar_baz'
>>> pascalcase('foo_bar_baz')
'FooBarBaz'
кавалер
источник
11

Я предпочитаю избегать, reесли возможно:

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'
колидир
источник
1
Это самая компактная программа, которая не использует reбиблиотеку и не выполняет всю работу в одну строку, используя только встроенные методы str.methods! Это похоже на этот ответ , но избегает использования нарезки и дополнительных if ... else, просто убирая потенциально добавленный «_» в качестве первого символа. Мне это нравится больше всего.
Колидир
За принятый ответ, 6.81 µs ± 22.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)но за этот ответ, 2.51 µs ± 25.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)который в 2,5 раза быстрее! Люблю это!
WBAR
10

Лично я не уверен, что что-либо, использующее регулярные выражения в 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'
TehTris
источник
1
+=на струнах это почти всегда плохая идея. Добавить в список и ''.join()это в конце. Или в этом случае просто присоединитесь к нему с подчеркиванием ...
ThiefMaster
22
Как однострочное регулярное выражение не превосходит практически по всем параметрам (включая удобочитаемость) неэффективную итерацию многострочных символов и перебор строк методом перебора? По какой-то причине Python предоставляет поддержку регулярных выражений.
Сесил Карри
1
@CecilCurry - Регулярные выражения очень сложны. Посмотрите компилятор и парсер, которые использует Python: svn.python.org/projects/python/trunk/Lib/sre_compile.py & svn.python.org/projects/python/trunk/Lib/sre_parse.py - простая манипуляция строками, например это вероятно намного быстрее чем RE, делающий то же самое.
Эван Боргстром
1
+1. Регулярные выражения могут быть реальным приемником ЦП, и при интенсивных вычислениях резко снизят ваши показатели. Для простых задач всегда предпочитайте простые функции.
Фабьен
4
«Для простых задач всегда предпочитайте простые функции», безусловно, хороший совет, но этот ответ не является ни простой функцией, ни элегантной. Регулярное выражение может быть медленнее, но по умолчанию такая сложная функция (которая ТАКЖЕ не проверена и имеет множество потенциальных точек ошибки) является полностью преждевременной оптимизацией
kevlarr
8
''.join('_'+c.lower() if c.isupper() else c for c in "DeathToCamelCase").strip('_')
re.sub("(.)([A-Z])", r'\1_\2', 'DeathToCamelCase').lower()
Джимми
источник
7

Я думаю, что это решение является более простым, чем предыдущие ответы:

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

Регулярное выражение соответствует трем шаблонам:

  1. [A-Z]?[a-z]+Последовательные строчные буквы, которые необязательно начинаются с заглавной буквы.
  2. [A-Z]{2,}(?=[A-Z][a-z]|\d|\W|$): Две или более последовательных заглавных буквы. Он использует заглядывание, чтобы исключить последнюю заглавную букву, если за ней следует строчная буква.
  3. \d+: Последовательные номера.

Используя, re.findallмы получаем список отдельных «слов», которые можно преобразовать в нижний регистр и объединить с подчеркиванием.

rspeed
источник
1
Здесь есть хороший пример независимого токенизации чисел.
math_law
1
Broken: convert ("aB") -> 'a'
adw
5

Я не понимаю, зачем использовать оба вызова .sub ()? :) Я не гуру регулярных выражений, но я упростил функцию до этой, которая подходит для моих определенных потребностей, мне просто нужно решение для преобразования camelCasedVars из запроса POST в vars_with_underscore:

def myFunc(...):
  return re.sub('(.)([A-Z]{1})', r'\1_\2', "iTriedToWriteNicely").lower()

Он не работает с такими именами, как getHTTPResponse, потому что я слышал, что это плохое соглашение об именах (должно быть, как getHttpResponse, очевидно, что эту форму гораздо легче запомнить).

desper4do
источник
Я забыл упомянуть, что «{1}» не требуется, но иногда это помогает прояснить некоторый туман.
desper4do
2
-1: это просто не работает Попробуйте, например, с 'HTTPConnectionFactory', ваш код производит 'h_tt_pconnection_factory', код из принятого ответа производит'http_connection_factory'
vartec
4

Вот мое решение:

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как надо.

оборота Эван Фосмарк
источник
7
-1, потому что это очень сложно по сравнению с использованием регулярных выражений.
Эрик О Лебиго
7
EOL, я уверен, что многие не-регулярные выражения будут думать иначе.
Эван Фосмарк
Это решение дает сбой в следующих случаях: _Method, _test_Method , __test__Method, getHTTPrespnseCode, __get_HTTPresponseCode, _Camel_Case, _Test и _test_Method.
Freegnu
3
@ Эван, эти люди будут плохими программистами.
Джесси Диллон
3

Для удовольствия:

>>> 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'
оборота гауаа
источник
3
c.isupper (), а не c в ABCEF ... Z
Джимми
1
Python не имеет регулярных выражений? Быстрый 's / [az] \ K ([AZ] [az]) / _ \ L $ 1 / г; lc $ _ 'в Perl делает работу (хотя он не очень хорошо обрабатывает getHTTPResponseCode; но ожидается, что он должен называться getHttpResponseCode)
jrockway
5
str.joinосуждается целую вечность . Используйте ''.join(..)вместо этого.
Джон Фухи
jrockway: у него есть регулярные выражения через модуль re. Не должно быть слишком сложно заставить эту работу использовать регулярные выражения, а не подходы, опубликованные здесь.
Мэтью Изелин
Python noob здесь, но зачем возвращать str.join ('', output)? Просто чтобы создать копию?
Тарлы
3

Использование регулярных выражений может быть самым коротким, но это решение более читабельно:

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
3k-
источник
@blueyed это совершенно не связано, этот вопрос не имеет никакого отношения к django.
3k-
Это просто пример, такой как HTTPResponseCode, который обрабатывается stackoverflow.com/a/23561109/15690 .
синеватый
3

Так много сложных методов ... Просто найдите всю группу "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'

Если вы не хотите, чтобы цифры были похожи на первый символ группы или отдельной группы - вы можете использовать ([A-z][a-z0-9]*)маску.

unitto
источник
2

Не в стандартной библиотеке, но я обнаружил, что этот скрипт содержит необходимые вам функции.

Стефано Борини
источник
2

Это не элегантный метод, это реализация очень низкого уровня простого конечного автомата (конечного автомата с битовым полем), возможно, наиболее анти-питонический режим для решения этой проблемы, однако модуль 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

Надеюсь это полезно

оборота jdavidls
источник
1
Отвратительно, но он работает примерно в 3 раза быстрее, чем метод регулярных выражений на моей машине. :)
jdiaz5513
1

Легко адаптировано из 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()
Сальваторе
источник
1

Взгляните на превосходную библиотеку Schematics

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')
Иан хантер
источник
1

Этот простой метод должен сделать работу:

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()
  • Мы ищем заглавные буквы, которым предшествует любое количество (или ноль) заглавных букв, а затем любое количество строчных букв.
  • Подчеркивание ставится непосредственно перед появлением последней заглавной буквы, найденной в группе, и можно поставить перед этой заглавной буквой, если ей предшествуют другие заглавные буквы.
  • Если есть завершающие подчеркивания, удалите их.
  • Наконец, вся строка результата изменяется на нижний регистр.

(взято отсюда , см. рабочий пример онлайн )

Матье Родич
источник
Это ответ на противоположный вопрос (как перевести на верблюжий случай).
Джастин,
1

Вау, я только что украл это из фрагментов Джанго. ссылка 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'

REGEX DEMO

brianray
источник
1
Неверная форма использования str в качестве имени локальной переменной.
Freegnu
Это с треском проваливается, если в начале или в конце строки есть какие-либо подчеркивания и если есть заглавные буквы перед заглавной буквой.
Freegnu
не учитывает числа 😬
villy393
0

Ужасный пример с использованием регулярных выражений (это можно легко очистить :)):

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")

РЕДАКТИРОВАТЬ: также должно быть довольно легко увидеть, что есть место для улучшений для таких случаев, как «Тест», потому что подчеркивание безоговорочно вставляется.

Мэтью Изелин
источник
0

Вот что я сделал, чтобы изменить заголовки файла с разделителями табуляции. Я опускаю ту часть, где я редактировал только первую строку файла. Вы можете легко адаптировать его к Python с помощью библиотеки re. Это также включает в себя разделение чисел (но сохраняет цифры вместе). Я сделал это в два этапа, потому что это было проще, чем указывать не ставить подчеркивание в начале строки или табуляции.

Шаг первый ... найдите заглавные буквы или целые числа, перед которыми стоят строчные буквы, и поставьте перед ними подчеркивание:

Поиск:

([a-z]+)([A-Z]|[0-9]+)

Замена:

\1_\l\2/

Шаг второй ... возьмите приведенное выше и запустите его снова, чтобы преобразовать все заглавные буквы в нижний регистр:

Поиск:

([A-Z])

Замена (это обратный слеш, строчные буквы L, обратный слеш, один):

\l\1
Джо Трикарико
источник
0

Я искал решение той же проблемы, за исключением того, что мне нужна была цепочка; например

"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"))

Конечно, вы можете предварительно скомпилировать регулярные выражения или объединить с подчеркиванием вместо дефиса, как обсуждалось в других решениях.

Джим Пиварски
источник
0

Кратко без регулярных выражений, но 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())])
оборота Данталион
источник
0

Без какой-либо библиотеки:

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
bibmartin
источник
0

Очень хороший RegEx предложил на этом сайте :

(?<!^)(?=[A-Z])

Если у python есть метод String Split, он должен работать ...

В Java:

String s = "loremIpsum";
words = s.split("(?&#60;!^)(?=[A-Z])");
Jmini
источник
К сожалению, модуль регулярных выражений Python (начиная с версии 3.6) не поддерживает разбиение на совпадения нулевой длины.
rspeed
0
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()
dmrz
источник
0

На случай, если кому-то понадобится преобразовать полный исходный файл, вот скрипт, который это сделает.

# 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))
Паскаль Т.
источник
-1

Мне очень повезло с этим:

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)
codekoala
источник
-1
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()
JohnBoy
источник
-3

Использование: str.capitalize() для преобразования первой буквы строки (содержащейся в переменной str) в заглавную букву и возвращает всю строку.

Пример: Команда: "привет" .capitalize () Вывод: Привет

Аршин
источник
Это не связано с вопросом - ОП хочет CamelCase -> snake_case, а не Capitalization.
Брэд Кох