Elegante Python-Funktion zum Konvertieren von CamelCase in snake_case?


333

Beispiel:

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

28
Informationen zum Konvertieren in die andere Richtung finden Sie in dieser anderen Frage zum Stapelüberlauf.
Nathan

10
nb das ist NotCamelCaseaberthisIs
Matt Richards

5
@ MattRichards Es ist umstritten. Wiki
NO_NAME

@MattRichards In Java verwenden sie beispielsweise beide, CamelCase wird zum Benennen von Klassendefinitionen verwendet, während camelCase zum Benennen initialisierter Variablen verwendet wird.
dunkellos

Antworten:


796

Kamelkoffer zu Schlangenkoffer

import re

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

Wenn Sie dies viele Male tun und das oben Gesagte langsam ist, kompilieren Sie den regulären Ausdruck im Voraus:

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

Speziell für fortgeschrittenere Fälle (dies ist nicht mehr umkehrbar):

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

Schlangenkoffer zu Kamelkoffer

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

1
Diese Lösung schlägt in den folgenden Fällen fehl: _test_Method, __test__Method, _Test, getHTTPresponseCode, __CamelCase und _Camel_Case.
Freegnu

6
wie wäre es mit dem Gegenteil? Konvertieren Sie ein not_camel_casein notCamelCaseund / oder NotCamelCase?
John2x

9
Um doppelte Unterstriche bei der Konvertierung von z. B. camel_Case zu vermeiden, fügen Sie diese Zeile hinzu:s2.replace('__', '_')
Marcus Ahlberg

2
Beachten Sie, dass dies nicht sehr reversibel ist. getHTTPResponseCode sollte in get_h_t_t_p_response_code konvertieren. getHttpResponseCode sollte in get_http_response_code konvertieren
K2xL

4
@AnmolSinghJaggi Der erste reguläre Ausdruck behandelt den Rand eines Akronyms, gefolgt von einem anderen Wort (z. B. "HTTPResponse" -> "HTTP_Response") ODER den normaleren Fall eines anfänglichen Kleinbuchstabens, gefolgt von einem großgeschriebenen Wort (z. B. "getResponse" -> ". get_Response ". Der zweite reguläre Ausdruck behandelt den Normalfall von zwei Nicht-Akronymen (z. B." ResponseCode "->" Response_Code "), gefolgt von einem letzten Aufruf, alles in Kleinbuchstaben zu schreiben. Daher" getHTTPResponseCode "->" getHTTP_ResponseCode "->" get_HTTP_Response_Code "- > "get_http_response_code"
Jeff Moser

188

Es gibt eine Flexionsbibliothek im Paketindex, die diese Dinge für Sie erledigen kann. In diesem Fall würden Sie suchen inflection.underscore():

>>> inflection.underscore('CamelCase')
'camel_case'

44
Ich verstehe nicht, warum die Leute über die Verwendung von benutzerdefinierten Funktionen abstimmen, wenn es eine großartige Bibliothek gibt, die diese Aufgabe ausführt. Wir sollten das Rad nicht neu erfinden.
Oden

87
@oden Vielleicht, weil das Hinzufügen einer völlig neuen Abhängigkeit für die Ausführung einer einzeiligen Funktion fragil ist.
Cecil Curry

11
Zum einen sicher, es ist übertrieben. Bei einer größeren Anwendung muss das Rad nicht neu erfunden und verschleiert werden.
Brad Koch

11
Regexes viel zurück in eine "einzelne Zeile", weshalb es viel mehr als eine Zeile mit richtigen Tests ist.
Studgeek

12
@CecilCurry: Ich bin sicher, dass Sie ein großartiger Programmierer sind, aber ich bin nicht sicher, ob es keine Fälle gibt, die Sie nicht berücksichtigt haben. Schauen Sie sich einfach andere Antworten hier an, um Beispiele zu finden. Deshalb werde ich immer eine Bibliothek auswählen, weil es die Gesamterfahrung von viel mehr Entwicklern als nur mir ist.
Michael Scheper

104

Ich weiß nicht, warum das alles so kompliziert ist.

für die meisten Fälle, die einfachen Ausdruck ([A-Z]+)den Trick

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

Um das erste Zeichen zu ignorieren, fügen Sie einfach einen Blick nach hinten hinzu (?!^)

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

Wenn Sie ALLCaps von all_caps trennen möchten und Zahlen in Ihrer Zeichenfolge erwarten möchten, müssen Sie immer noch nicht zwei separate Läufe ausführen. Verwenden Sie einfach. |Dieser Ausdruck ((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))kann nahezu jedes Szenario im Buch verarbeiten

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

Es hängt alles davon ab, was Sie möchten. Verwenden Sie daher die Lösung, die Ihren Anforderungen am besten entspricht, da sie nicht zu kompliziert sein sollte.

nJoy!


1
Die letzte Iteration ist die klügste, IMO. Ich brauchte ein wenig, um zu verstehen, dass es nur das einzelne Zeichen am Anfang jedes Wortes ersetzt - und das nur, weil der Ansatz anders war als der, den ich mir ausgedacht hatte. Schön gemacht.
Justin Miller

2
Ich war verwirrt über den (?!^)Ausdruck, der als Rückblick bezeichnet wurde. Wenn ich nichts vermisse, wollen wir hier wirklich einen negativen Rückblick, der ausgedrückt werden sollte als (?<!^). Aus Gründen, die ich nicht verstehen kann, (?!^)scheint Ihr negativer Ausblick auch zu funktionieren ...
Apteryx

7
Dies kann mit bereits vorhandenen Unterstrichen nicht gut umgehen: "Camel2WARNING_Case_CASE"wird "camel2_warning_case__case". Sie können ein (?<!_)negatives Aussehen hinzufügen , um es zu lösen: re.sub('((?<=[a-z0-9])[A-Z]|(?!^)(?<!_)[A-Z](?=[a-z]))', r'_\1', "Camel2WARNING_Case_CASE").lower() Rückkehr 'camel2_warning_case_case'
Glücksfall

@Apteryx Du hast Recht, (?!^)wurde fälschlicherweise als "Blick nach hinten" bezeichnet und hätte stattdessen als negative Lookahead-Behauptung bezeichnet werden sollen . Wie diese nette Erklärung zeigt, kommen negative Lookaheads normalerweise nach dem Ausdruck , nach dem Sie suchen. Sie können sich also (?!^)als "Finden, ''wo <start of string>nicht folgt" vorstellen. In der Tat funktioniert auch ein negativer Lookbehind: Sie können sich vorstellen, (?<!^)"herauszufinden, ''wo <start of string>nicht vorangeht".
Nathaniel Jones

17

stringcase ist dafür meine Anlaufstelle ; z.B:

>>> from stringcase import pascalcase, snakecase
>>> snakecase('FooBarBaz')
'foo_bar_baz'
>>> pascalcase('foo_bar_baz')
'FooBarBaz'

11

Persönlich bin ich mir nicht sicher, wie etwas, das reguläre Ausdrücke in Python verwendet, als elegant beschrieben werden kann. Die meisten Antworten hier machen nur RE-Tricks vom Typ "Code Golf". Elegante Codierung soll leicht verständlich sein.

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
+=auf Saiten ist fast immer eine schlechte Idee. An eine Liste anhängen und ''.join()am Ende. Oder in diesem Fall einfach mit einem Unterstrich verbinden ...
ThiefMaster

21
Wie ist ein einzeiliger regulärer Ausdruck nicht in jeder praktischen Hinsicht (einschließlich Lesbarkeit) von Natur aus einer ineffizienten mehrzeiligen Zeicheniteration und Brute-Force-String-Munging überlegen? Python bietet aus einem bestimmten Grund sofort Unterstützung für reguläre Ausdrücke.
Cecil Curry

1
@CecilCurry - Reguläre Ausdrücke sind SEHR komplex. Siehe den von Python verwendeten Compiler und Parser: svn.python.org/projects/python/trunk/Lib/sre_compile.py & svn.python.org/projects/python/trunk/Lib/sre_parse.py - Einfache Zeichenfolgenmanipulation wie Dies ist wahrscheinlich viel schneller als ein RE, der dasselbe tut.
Evan Borgstrom

1
+1. Regexes können eine echte CPU-Senke sein und bei intensiven Berechnungen Ihre Leistung drastisch senken. Bevorzugen Sie für einfache Aufgaben immer einfache Funktionen.
Fabien

4
"Für einfache Aufgaben immer einfache Funktionen bevorzugen" ist definitiv ein guter Rat, aber diese Antwort ist weder eine einfache noch eine elegante Funktion. Regex mag langsamer sein, aber die Standardeinstellung einer komplizierten Funktion wie dieser (die AUCH ungetestet ist und zahlreiche potenzielle Fehlerpunkte aufweist) ist eine völlig vorzeitige Optimierung
kevlarr

9

Ich vermeide es rewenn möglich:

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
Dies ist die kompakteste, die es vermeidet, die reBibliothek zu verwenden und das Zeug nur in einer Zeile zu erledigen, nur mit eingebauten str.methods! Es ähnelt dieser Antwort , vermeidet jedoch das Verwenden von Slicing und zusätzlichem, if ... elseindem möglicherweise das potenziell hinzugefügte "_" als erstes Zeichen entfernt wird. Das gefällt mir am besten.
Colidyre

Für akzeptierte Antwort, 6.81 µs ± 22.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)aber für diese Antwort, 2.51 µs ± 25.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)die 2,5x schneller ist! Ich liebe das!
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

Ich denke, diese Lösung ist einfacher als frühere Antworten:

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

Welche Ausgänge:

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

Der reguläre Ausdruck entspricht drei Mustern:

  1. [A-Z]?[a-z]+: Aufeinanderfolgende Kleinbuchstaben, die optional mit einem Großbuchstaben beginnen.
  2. [A-Z]{2,}(?=[A-Z][a-z]|\d|\W|$): Zwei oder mehr aufeinanderfolgende Großbuchstaben. Es verwendet einen Lookahead, um den letzten Großbuchstaben auszuschließen, wenn ihm ein Kleinbuchstabe folgt.
  3. \d+: Fortlaufende Nummern.

Durch die Verwendung erhalten re.findallwir eine Liste einzelner "Wörter", die in Kleinbuchstaben umgewandelt und mit Unterstrichen verbunden werden können.


1
Hier gibt es ein gutes Beispiel, um die Numerik unabhängig voneinander zu tokenisieren.
math_law

1
Gebrochen: konvertieren ("aB") -> 'a'
adw

5

Ich habe keine Ahnung, warum beide .sub () -Aufrufe verwendet werden. :) Ich bin kein Regex-Guru, aber ich habe die Funktion auf diese vereinfacht, die für meine bestimmten Bedürfnisse geeignet ist. Ich brauchte nur eine Lösung, um camelCasedVars von der POST-Anfrage in vars_with_underscore zu konvertieren:

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

Es funktioniert nicht mit Namen wie getHTTPResponse, weil ich gehört habe, dass es sich um eine schlechte Namenskonvention handelt (sollte wie getHttpResponse sein, es ist offensichtlich, dass es viel einfacher ist, sich dieses Formular zu merken).


Ich habe vergessen zu erwähnen, dass '{1}' nicht benötigt wird, aber manchmal hilft es, etwas Nebel zu klären.
Desper4do

2
-1: Das funktioniert einfach nicht. Versuchen Sie zum Beispiel mit 'HTTPConnectionFactory', Ihr Code erzeugt 'h_tt_pconnection_factory', Code aus akzeptierter Antwort erzeugt'http_connection_factory'
vartec

4

Hier ist meine Lösung:

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)

Es unterstützt die in den Kommentaren diskutierten Eckfälle. Zum Beispiel wird es so konvertiert getHTTPResponseCode, get_http_response_codewie es sollte.


7
-1, da dies im Vergleich zur Verwendung von regulären Ausdrücken sehr kompliziert ist.
Eric O Lebigot

7
EOL, ich bin sicher, dass viele Nicht-Regexp-Leute anders denken würden.
Evan Fosmark

Diese Lösung schlägt in folgenden Fällen fehl: _Method, _test_Method , __test__Method, getHTTPrespnseCode, __get_HTTPresponseCode, _Camel_Case, _Test und _test_Method.
Freegnu

3
@Evan, diese Leute wären schlechte Programmierer.
Jesse Dhillon

3

Spaßeshalber:

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

Oder mehr zum Spaß:

>>> 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 () statt c in ABCEF ... Z
Jimmy

1
Python hat keine regulären Ausdrücke? A quick 's / [az] \ K ([AZ] [az]) / _ \ L $ 1 / g; lc $ _ 'in Perl erledigt den Job (obwohl es getHTTPResponseCode nicht gut verarbeitet; aber das wird erwartet, sollte getHttpResponseCode heißen)
jrockway

5
str.joinist seit Ewigkeiten veraltet . Verwenden Sie ''.join(..)stattdessen.
John Fouhy

jrockway: Es gibt reguläre Ausdrücke über das "re" -Modul. Es sollte nicht zu schwierig sein, diese Arbeit mit Regex zu machen, anstatt mit den hier veröffentlichten Ansätzen.
Matthew Iselin

Python noob hier, aber warum str.join ('', Ausgabe) zurückgeben? Nur um eine Kopie zu erstellen?
Tarks

3

Die Verwendung von regulären Ausdrücken mag am kürzesten sein, aber diese Lösung ist viel besser lesbar:

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 das hat nichts damit zu tun, diese Frage hat nichts mit Django zu tun.
3k

Dies ist nur ein Beispiel wie HTTPResponseCode, das von stackoverflow.com/a/23561109/15690 verarbeitet wird .
Blueyed

3

So viele komplizierte Methoden ... Finden Sie einfach alle "Betitelten" Gruppen und verbinden Sie die Variante mit dem unteren Gehäuse mit Unterstrich.

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

Wenn Sie keine Zahlen wie das erste Zeichen einer Gruppe oder eine separate Gruppe erstellen möchten, können Sie die ([A-z][a-z0-9]*)Maske verwenden.



2

Dies ist keine elegante Methode, sondern eine Implementierung einer einfachen Zustandsmaschine (Bitfeld-Zustandsmaschine) auf sehr niedriger Ebene, möglicherweise der anti-pythonischste Modus, um dies zu beheben. Das Modul re implementiert jedoch auch eine zu komplexe Zustandsmaschine, um diese einfache zu lösen Aufgabe, also denke ich, dass dies eine gute Lösung ist.

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 kann alle Falltypen analysieren: UpperSEQUENCEInterleaved, under_score, BIG_SYMBOLS und cammelCasedMethods

Ich hoffe es ist nützlich


1
Schrecklich, aber es läuft ungefähr 3x schneller als die Regex-Methode auf meinem Computer. :)
jdiaz5513


1

Schauen Sie sich die exzellente Schematics lib an

https://github.com/schematics/schematics

Sie können typisierte Datenstrukturen erstellen, die von Python nach Javascript serialisiert / deserialisiert werden können, z.

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

Diese einfache Methode sollte den Job erledigen:

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()
  • Wir suchen nach Großbuchstaben, denen eine beliebige Anzahl von (oder null) Großbuchstaben vorangestellt ist, gefolgt von einer beliebigen Anzahl von Kleinbuchstaben.
  • Ein Unterstrich wird unmittelbar vor dem Auftreten des letzten in der Gruppe gefundenen Großbuchstabens gesetzt, und einer kann vor diesem Großbuchstaben gesetzt werden, falls ihm andere Großbuchstaben vorangestellt sind.
  • Wenn nachfolgende Unterstriche vorhanden sind, entfernen Sie diese.
  • Schließlich wird die gesamte Ergebniszeichenfolge in Kleinbuchstaben geändert.

(von hier aus siehe Online-Arbeitsbeispiel )


Dies ist eine Antwort auf die entgegengesetzte Frage (wie man in Kamelkoffer konvertiert ).
Justin

1

Wow, ich habe das gerade aus Django-Schnipsel gestohlen. ref http://djangosnippets.org/snippets/585/

Ziemlich elegant

camelcase_to_underscore = lambda str: re.sub(r'(?<=[a-z])[A-Z]|[A-Z](?=[^A-Z])', r'_\g<0>', str).lower().strip('_')

Beispiel:

camelcase_to_underscore('ThisUser')

Kehrt zurück:

'this_user'

REGEX DEMO


1
Schlechte Form mit str als lokalem Variablennamen.
Freegnu

Dies schlägt kläglich fehl, wenn am Anfang oder Ende eines Strings Unterstriche und vor einem Großbuchstaben Unterstriche stehen.
Freegnu

berücksichtigt nicht die Zahlen 😬
villy393

Manchmal ist es keine gute Idee, Code zu stehlen.
Colidyre

0

Ein schreckliches Beispiel mit regulären Ausdrücken (Sie könnten leicht bereinigen :)):

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

Funktioniert aber für getHTTPResponseCode!

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

BEARBEITEN: Es sollte auch ziemlich leicht zu erkennen sein, dass in Fällen wie "Test" Verbesserungspotenzial besteht, da der Unterstrich unbedingt eingefügt wird.


0

Hier ist etwas, was ich getan habe, um die Header einer durch Tabulatoren getrennten Datei zu ändern. Ich lasse den Teil weg, in dem ich nur die erste Zeile der Datei bearbeitet habe. Sie können es mit der Re-Bibliothek ziemlich einfach an Python anpassen. Dies beinhaltet auch das Trennen von Zahlen (hält aber die Ziffern zusammen). Ich habe es in zwei Schritten gemacht, weil das einfacher war, als zu sagen, dass am Anfang einer Zeile oder eines Tabulators kein Unterstrich gesetzt werden soll.

Erster Schritt ... Suchen Sie Großbuchstaben oder Ganzzahlen, denen Kleinbuchstaben vorangestellt sind, und stellen Sie ihnen einen Unterstrich voran:

Suche:

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

Ersatz:

\1_\l\2/

Schritt 2 ... Nehmen Sie die obigen Schritte und führen Sie sie erneut aus, um alle Großbuchstaben in Kleinbuchstaben umzuwandeln:

Suche:

([A-Z])

Ersatz (das ist Backslash, Kleinbuchstabe L, Backslash, eins):

\l\1

0

Ich suchte nach einer Lösung für das gleiche Problem, außer dass ich eine Kette brauchte; z.B

"CamelCamelCamelCase" -> "Camel-camel-camel-case"

Ausgehend von den netten Zwei-Wort-Lösungen hier habe ich Folgendes gefunden:

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

Der größte Teil der komplizierten Logik besteht darin, zu vermeiden, dass das erste Wort in Kleinbuchstaben geschrieben wird. Hier ist eine einfachere Version, wenn es Ihnen nichts ausmacht, das erste Wort zu ändern:

"-".join(x.group(1).lower() for x in re.finditer("(^[^A-Z]+|[A-Z][^A-Z]+)", "stringToSplit"))

Natürlich können Sie die regulären Ausdrücke vorkompilieren oder mit einem Unterstrich anstelle eines Bindestrichs verbinden, wie in den anderen Lösungen beschrieben.


0

Prägnant ohne reguläre Ausdrücke, aber 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

Ohne Bibliothek:

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('__','_')

Ein bisschen schwer, aber

CamelCamelCamelCase ->  camel_camel_camel_case
HTTPRequest         ->  http_request
GetHTTPRequest      ->  get_http_request
getHTTPRequest      ->  get_http_request

0

Sehr schönes RegEx auf dieser Seite vorgeschlagen :

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

Wenn Python eine String Split-Methode hat, sollte es funktionieren ...

In Java:

String s = "loremIpsum";
words = s.split("(?&#60;!^)(?=[A-Z])");

Leider unterstützt das Python-Modul für reguläre Ausdrücke (ab Version 3.6) die Aufteilung bei Übereinstimmungen mit der Länge Null nicht.
Geschwindigkeit

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

Und wenn wir einen Fall mit bereits nicht getarntem Input abdecken müssen:

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

0

Nur für den Fall, dass jemand eine vollständige Quelldatei transformieren muss, finden Sie hier ein Skript, das dies ausführt.

# 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

Ich hatte ziemlich viel Glück mit diesem:

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)

Dies könnte natürlich für die Geschwindigkeit eines optimiert werden winzige Bit , wenn Sie wollen.

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

Verwenden Sie:, str.capitalize()um den ersten Buchstaben der Zeichenfolge (in der Variablen str enthalten) in einen Großbuchstaben umzuwandeln und die gesamte Zeichenfolge zurückzugeben.

Beispiel: Befehl: "Hallo" .capitalize () Ausgabe: Hallo


Dies hängt nicht mit der Frage zusammen - das OP möchte CamelCase -> snake_case, nicht Capitalization.
Brad Koch
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.