Elegancka funkcja Pythona do konwersji CamelCase na snake_case?


333

Przykład:

>>> convert('CamelCase')
'camel_case'

28
Aby przekonwertować w innym kierunku, zobacz to inne pytanie dotyczące przepływu stosu.
— Nathan

10
nb to NotCamelCasealethisIs
— Matt Richards,

5
@MattRichards Jest to kwestia sporna. wiki
— NO_NAME

@MattRichards Na przykład w Javie używają obu, CamelCase służy do nazewnictwa definicji klas, podczas gdy camelCase służy do nazewnictwa zainicjowanych zmiennych.
— ciemne

Odpowiedzi:


797

Etui na wielbłąda na etui z wężem

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

Etui z węża na etui z wielbłądem

name = 'snake_case_name'
name = ''.join(word.title() for word in name.split('_'))
print(name)  # SnakeCaseName

1
To rozwiązanie zawiedzie w następujących przypadkach: _test_Method, __test__Method, _Test, getHTTPresponseCode, __CamelCase i _Camel_Case.
— freegnu

6
co powiesz na odwrót? Konwertujesz not_camel_casena notCamelCasei / lub NotCamelCase?
— john2x

9
Aby uniknąć podwójnych znaków podkreślenia podczas konwersji np. Camel_Case, dodaj tę linię:s2.replace('__', '_')
— Marcus Ahlberg

2
Uwaga: nie jest to bardzo odwracalne. getHTTPResponseCode powinien zostać przekonwertowany na kod get_h_t_t_p_response_code. getHttpResponseCode powinien zostać przekonwertowany na kod get_http_response_code
— K2xL

4
@AnmolSinghJaggi Pierwsze wyrażenie regularne obsługuje wielkość liter w akronimie, po której następuje inne słowo (np. „HTTPResponse” -> „HTTP_Response”) LUB bardziej normalny przypadek początkowego małego słowa, po którym następuje wielkie litery (np. „GetResponse” -> ” get_Response ". Drugi regex obsługuje normalny przypadek dwóch nieakronimów (np.„ ResponseCode ”->„ Response_Code ”), a następnie końcowe wywołanie, aby wszystko napisać małymi literami. Zatem„ getHTTPResponseCode ”->„ getHTTP_ResponseCode ”->„ get_HTTP_Response_Code ”- > „get_http_response_code”
— Jeff Moser

188

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'

44
Nie rozumiem, dlaczego ludzie głosują na użycie niestandardowych funkcji, gdy istnieje świetna biblioteka, która wykonuje to zadanie. Nie powinniśmy wymyślać koła na nowo.
— oden

88
@oden Może dlatego, że dodanie całej nowej zależności do wykonywania funkcji jednowierszowej jest delikatnym nadmiernym zabijaniem?
— Cecil Curry

11
Na przykład, pewnie to przesada. W przypadku większych aplikacji nie trzeba wymyślać i zaciemniać koła.
— Brad Koch,

11
Powraca dużo do „pojedynczej linii”, dlatego jest to więcej niż jedna linia z odpowiednim testowaniem.
— studgeek

12
@CecilCurry: Jestem pewien, że jesteś świetnym programistą, ale nie jestem pewien, że nie ma przypadków, których nie wziąłeś pod uwagę - wystarczy spojrzeć na inne odpowiedzi tutaj, aby uzyskać przykłady. Dlatego zawsze wybieram bibliotekę, ponieważ jest to suma doświadczeń znacznie większej liczby deweloperów niż tylko mnie.
— Michael Scheper,

104

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!


1
Ostatnia iteracja jest najmądrzejsza, IMO. Trochę zajęło mi zrozumienie, że zastępuje on tylko jeden znak na początku każdego słowa - i to tylko dlatego, że podejście było inne niż to, które sam wymyśliłem. Ładnie wykonane.
— Justin Miller

2
Byłem zdziwiony tym, (?!^)ż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ć ...
— Apteryx

7
To nie radzi sobie dobrze z istniejącymi podkreśleniami: "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'
— luckydonald

@Apteryx Masz rację, (?!^)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”.
— Nathaniel Jones

17

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'

11

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'

1
+=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 ...
— ThiefMaster

21
W jaki sposób jednowierszowe wyrażenie regularne nie jest z natury lepsze pod każdym względem (w tym czytelności) od nieefektywnej wieloliniowej iteracji znaków i mungingu ciągu siłowego? Python zapewnia obsługę wyrażeń regularnych od razu po wyjęciu z pudełka.
— Cecil Curry,

1
@CecilCurry - Wyrażenia regularne są BARDZO złożone. Zobacz kompilator i analizator składni, z którego korzysta Python: svn.python.org/projects/python/trunk/Lib/sre_compile.py & svn.python.org/projects/python/trunk/Lib/sre_parse.py - Proste manipulowanie ciągami, takie jak to prawdopodobnie znacznie szybciej niż RE robi to samo.
— Evan Borgstrom

1
+1. Regeksy mogą być prawdziwym ujściem procesora, a przy intensywnych obliczeniach radykalnie obniżą wydajność. W przypadku prostych zadań zawsze preferuj proste funkcje.
— Fabien

4
„W przypadku prostych zadań zawsze preferuj proste funkcje” to zdecydowanie dobra rada, ale ta odpowiedź nie jest ani funkcją prostą, ani elegancką. Regex może być wolniejszy, ale
— domyślnie złożona

9

Wolę unikać, rejeś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'

1
Jest to najbardziej kompaktowy, który pozwala uniknąć korzystania z rebiblioteki 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ę.
— colidyre

Za zaakceptowaną odpowiedź, 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!
— WBAR

8
''.join('_'+c.lower() if c.isupper() else c for c in "DeathToCamelCase").strip('_')
re.sub("(.)([A-Z])", r'\1_\2', 'DeathToCamelCase').lower()

7

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:

  1. [A-Z]?[a-z]+: Kolejne małe litery, które opcjonalnie zaczynają się od dużej litery.
  2. [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.
  3. \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.


1
Jest tutaj dobry przykład na niezależne tokenizację liczb.
— math_law

1
Uszkodzony: konwersja („aB”) -> „a”
— adw

5

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


Zapomniałem wspomnieć, że „{1}” nie jest potrzebne, ale czasem pomaga wyjaśnić mgłę.
— desper4do

2
-1: to po prostu nie działa. Spróbuj na przykład z 'HTTPConnectionFactory', tworzy kod 'h_tt_pconnection_factory', kod z zaakceptowanej odpowiedzi tworzy'http_connection_factory'
— vartec

4

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ć getHTTPResponseCodesię get_http_response_codetak jak powinien.


7
-1, ponieważ jest to bardzo skomplikowane w porównaniu do używania wyrażeń regularnych.
— Eric O Lebigot,

7
EOL, jestem pewien, że wiele osób, które nie mają wyrażeń regularnych, pomyślałoby inaczej.
— Evan Fosmark

To rozwiązanie zawiedzie w następujących przypadkach: _Method, _test_Method , __test__Method, getHTTPrespnseCode, __get_HTTPresponseCode, _Camel_Case, _Test i _test_Method.
— freegnu

3
@Evan, ci ludzie byliby złymi programistami.
— Jesse Dhillon

3

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'

3
c.isupper () zamiast c w ABCEF ... Z
— Jimmy

1
Python nie ma wyrażeń regularnych? Szybkie 's / [az] \ K ([AZ] [az]) / _ \ L $ 1 / g; LC $ _”w Perl wykonuje pracę (choć nie obsługuje getHTTPResponseCode dobrze, ale to z oczekiwaniami, które powinny być nazwane getHttpResponseCode)
— jrockway

5
str.joinbył przestarzały od wieków . Użyj ''.join(..)zamiast tego.
— John Fouhy,

jrockway: Ma wyrażenia regularne za pośrednictwem modułu „re”. Wykonanie tej operacji przy użyciu wyrażenia regularnego nie powinno być trudne, niż opisane tutaj metody.
— Matthew Iselin

Python noob tutaj, ale po co zwracać str.join ('', wyjście)? Wystarczy utworzyć kopię?
— Tarks

3

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

@blueyed, które jest całkowicie niezwiązane, to pytanie nie ma nic wspólnego z django.
— 3k-

To tylko przykład, jak HTTPResponseCode, który jest obsługiwany przez stackoverflow.com/a/23561109/15690 .
— niebieskawy

3

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.



2

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


1
Ohydne, ale działa około 3 razy szybciej niż metoda regex na moim komputerze. :)
— jdiaz5513


1

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

1

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()
  • Szukamy wielkich liter poprzedzonych dowolną liczbą (lub zero) wielkich liter, a następnie dowolną liczbą małych liter.
  • Podkreślenie jest umieszczane tuż przed wystąpieniem ostatniej dużej litery znalezionej w grupie, a przed tą wielką literą można umieścić ją, jeśli poprzedza ją inna duża litera.
  • Jeśli są podkreślenia końcowe, usuń je.
  • Na koniec cały ciąg wyników jest zamieniany na małe litery.

(wzięte stąd , patrz działający przykład online )


To jest odpowiedź na przeciwne pytanie (jak przekonwertować na skrzynkę wielbłąda).
— Justin,

1

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'

REGEX DEMO


1
Zła forma przy użyciu str jako nazwy zmiennej lokalnej.
— freegnu

Nie udaje się to źle, jeśli na początku lub na końcu łańcucha znajdują się podkreślenia, a przed wielką literą występują podkreślenia.
— freegnu

nie bierze pod uwagę liczb 😬
— villy393,

0

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.


0

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

0

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.


0

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())])

0

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

0

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("(?&#60;!^)(?=[A-Z])");

Niestety moduł wyrażeń regularnych Python (od wersji 3.6) nie obsługuje podziału na dopasowania o zerowej długości.
— rspeed

0
def convert(name):
    return reduce(
        lambda x, y: x + ('_' if y.isupper() else '') + y, 
        name
    ).lower()

A jeśli musimy pokryć skrzynkę z już nie rozpiętym wejściem:

def convert(name):
    return reduce(
        lambda x, y: x + ('_' if y.isupper() and not x.endswith('_') else '') + y, 
        name
    ).lower()

0

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

-1

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)

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

-3

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


Nie ma to związku z pytaniem - OP chce CamelCase -> snake_case, a nie wielkie litery.
— Brad Koch,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.