Elegante funzione Python per convertire CamelCase in snake_case?


333

Esempio:

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

28
Per convertire nell'altra direzione, vedere questa altra domanda di StackOverflow.
Nathan,

10
nb quello è NotCamelCasemathisIs
Matt Richards il

5
@MattRichards È una questione controversa. wiki
NO_NAME il

@MattRichards Ad esempio in Java usano entrambi, CamelCase è usato per nominare le definizioni delle classi, mentre camelCase è usato per nominare le variabili inizializzate.
Darkless

Risposte:


798

Custodia da cammello a custodia da serpente

import re

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

Se lo fai molte volte e quanto sopra è lento, compila il regex in anticipo:

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

Per gestire appositamente i casi più avanzati (questo non è più reversibile):

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

Custodia a serpente per custodia a cammello

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

1
In questi casi questa soluzione ha esito negativo: _test_Method, __test__Method, _Test, getHTTPresponseCode, __CamelCase e _Camel_Case.
freegnu,

6
che ne dici del contrario? Convertire un not_camel_casein notCamelCasee / o NotCamelCase?
john2x,

9
Per evitare doppi trattini bassi durante la conversione, ad esempio camel_Case, aggiungi questa riga:s2.replace('__', '_')
Marcus Ahlberg

2
Nota che questo non è molto reversibile. getHTTPResponseCode dovrebbe essere convertito in get_h_t_t_p_response_code. getHttpResponseCode dovrebbe essere convertito in get_http_response_code
K2xL

4
@AnmolSinghJaggi Il primo regex gestisce il caso limite di un acronimo seguito da un'altra parola (ad esempio "HTTPResponse" -> "HTTP_Response") O il caso più normale di una parola iniziale minuscola seguita da una parola maiuscola (ad esempio "getResponse" -> " get_Response ". Il secondo regex gestisce il caso normale di due non acronimi (ad es." ResponseCode "->" Response_Code ") seguito da una chiamata finale per minuscolare tutto. Pertanto" getHTTPResponseCode "->" getHTTP_ResponseCode "->" get_HTTP_Response_Code "- > "get_http_response_code"
Jeff Moser,

188

C'è una libreria di flesso nell'indice del pacchetto che può gestire queste cose per te. In questo caso, dovresti cercare inflection.underscore():

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

44
Non capisco perché le persone stiano votando l'uso di funzioni personalizzate quando c'è una grande libreria che esegue questo compito. Non dovremmo reinventare la ruota.
Oden

88
@oden Forse perché l'aggiunta di una nuova dipendenza per svolgere il lavoro di una funzione a linea singola è un fragile over-kill?
Cecil Curry,

11
Ad esempio, sicuramente è eccessivo. Attraverso un'applicazione più ampia, non è necessario reinventare e offuscare la ruota.
Brad Koch,

11
Ritorna molto in una "linea singola", motivo per cui è molto più di una linea con test adeguati.
Studgeek,

12
@CecilCurry: Sono sicuro che sei un grande programmatore, ma non sono sicuro che non ci siano casi che non hai preso in considerazione, basta guardare altre risposte qui per esempi. Ecco perché sceglierò sempre una libreria, perché è la somma esperienza di molti più sviluppatori oltre a me.
Michael Scheper,

105

Non so perché siano così complicati.

per la maggior parte dei casi, l'espressione semplice ([A-Z]+)farà il trucco

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

Per ignorare il primo personaggio basta aggiungere uno sguardo dietro (?!^)

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

Se vuoi separare ALLCaps in all_caps e aspettarti numeri nella tua stringa, non hai ancora bisogno di fare due esecuzioni separate, usa solo |Questa espressione ((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))può gestire praticamente tutti gli scenari del libro

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

Tutto dipende da ciò che desideri, quindi usa la soluzione più adatta alle tue esigenze in quanto non dovrebbe essere eccessivamente complicata.

nJoy!


1
L'ultima iterazione è la più intelligente, IMO. Mi ci è voluto un po 'per capire che sta solo sostituendo il singolo carattere all'inizio di ogni parola - e questo era solo perché l'approccio era diverso da quello che avrei escogitato con me stesso. Ben fatto.
Justin Miller,

2
Ero perplesso (?!^)dall'espressione che veniva chiamata uno sguardo dietro. A meno che non mi manchi qualcosa, quello che vogliamo davvero qui è uno sguardo negativo che dovrebbe essere espresso come (?<!^). Per motivi che non riesco a capire, anche il tuo sguardo negativo (?!^)sembra funzionare ...
Apteryx,

7
Questo non gestisce bene i caratteri di sottolineatura preesistenti: "Camel2WARNING_Case_CASE"diventa "camel2_warning_case__case". Puoi aggiungere un (?<!_)aspetto negativo, per risolverlo: re.sub('((?<=[a-z0-9])[A-Z]|(?!^)(?<!_)[A-Z](?=[a-z]))', r'_\1', "Camel2WARNING_Case_CASE").lower() restituisce 'camel2_warning_case_case'
luckydonald il

@Apteryx Hai ragione, è (?!^)stato erroneamente definito uno "sguardo dietro" e avrebbe dovuto invece essere definito un'asserzione negativa . Come mostra questa bella spiegazione , i punti di vista negativi di solito vengono dopo l'espressione che stai cercando. Quindi puoi pensare (?!^)a "trova ''dove <start of string>non segue". In effetti, funziona anche un aspetto negativo: puoi pensare (?<!^)a "trova ''dove <start of string>non precede".
Nathaniel Jones,

18

stringcase è la mia biblioteca preferita per questo; per esempio:

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

11

Preferisco evitare rese possibile:

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
Questo è il più compatto che evita di usare la relibreria e di fare le cose in una sola riga usando solo i metodi str. È simile a questa risposta , ma evita di utilizzare lo slicing e ulteriori if ... elsesemplicemente eliminando "_" potenzialmente aggiunto come primo carattere. Mi piace di più.
Colidyre

Per una risposta accettata 6.81 µs ± 22.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)ma per questa risposta 2.51 µs ± 25.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)che è 2,5 volte più veloce! Ama questo!
WBAR

10

Personalmente non sono sicuro di come possa essere descritto come elegante qualsiasi cosa che usi espressioni regolari in Python. La maggior parte delle risposte qui stanno solo facendo trucchi di tipo RE "code golf". La codifica elegante dovrebbe essere facilmente comprensibile.

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
+=sulle stringhe è quasi sempre una cattiva idea. Aggiungi a un elenco e ''.join()alla fine. O in questo caso, semplicemente
uniscilo

22
In che modo un'espressione regolare a linea singola non è intrinsecamente superiore in quasi tutti i modi pratici (compresa la leggibilità) a inefficienti iterazione di caratteri a più righe e munging di stringhe a forza bruta? Python fornisce un supporto per le espressioni regolari pronto all'uso per un motivo.
Cecil Curry,

1
@CecilCurry - Le espressioni regolari sono MOLTO complesse. Guarda il compilatore e il parser che Python usa: svn.python.org/projects/python/trunk/Lib/sre_compile.py & svn.python.org/projects/python/trunk/Lib/sre_parse.py - Semplice manipolazione di stringhe come questo probabilmente molto più veloce di un RE che fa lo stesso.
Evan Borgstrom,

1
+1. Regexes può essere un vero dissipatore di CPU e su calcoli intensivi ridurrà drasticamente le tue prestazioni. Per compiti semplici, preferisci sempre funzioni semplici.
Fabien,

4
"Per compiti semplici, preferisci sempre funzioni semplici" è sicuramente un buon consiglio, ma questa risposta non è né una funzione semplice né elegante. Regex potrebbe essere più lento, ma il default di una funzione complicata come questa (che è anche non testata e ha numerosi potenziali punti di errore) è un'ottimizzazione completamente prematura
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

Penso che questa soluzione sia più semplice delle risposte precedenti:

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

Quali uscite:

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

L'espressione regolare corrisponde a tre modelli:

  1. [A-Z]?[a-z]+: Lettere minuscole consecutive che iniziano facoltativamente con una lettera maiuscola.
  2. [A-Z]{2,}(?=[A-Z][a-z]|\d|\W|$): Due o più lettere maiuscole consecutive. Usa un lookahead per escludere l'ultima lettera maiuscola se è seguita da una lettera minuscola.
  3. \d+: Numeri consecutivi.

Usando re.findallotteniamo un elenco di singole "parole" che possono essere convertite in lettere minuscole e unite con caratteri di sottolineatura.


1
C'è un buon esempio qui per ottenere i token numerici in modo indipendente.
math_law

1
Rotto: convert ("aB") -> 'a'
adw il

5

Non ho idea del perché usare entrambe le chiamate .sub ()? :) Non sono regex guru, ma ho semplificato la funzione di questo, che è adatto alle mie certe esigenze, avevo solo bisogno di una soluzione per convertire camelCasedVars dalla richiesta POST in vars_with_underscore:

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

Non funziona con nomi come getHTTPResponse, perché ho sentito che è una cattiva convenzione di denominazione (dovrebbe essere come getHttpResponse, è ovviamente che è molto più facile memorizzare questo modulo).


Ho dimenticato di dire che "{1}" non è necessario, ma a volte aiuta a chiarire un po 'di nebbia.
desper4do,

2
-1: questo non funziona. Prova ad esempio con 'HTTPConnectionFactory', il tuo codice produce 'h_tt_pconnection_factory', il codice dalla risposta accettata produce'http_connection_factory'
vartec

4

Ecco la mia soluzione:

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)

Supporta quei casi angolari discussi nei commenti. Per esempio, sarà convertito getHTTPResponseCodea get_http_response_codecome dovrebbe.


7
-1 perché questo è molto complicato rispetto all'utilizzo di regexps.
Eric O Lebigot,

7
EOL, sono sicuro che molte persone non regexp la penserebbero diversamente.
Evan Fosmark,

In questi casi questa soluzione ha esito negativo: _Method, _test_Method , __test__Method, getHTTPrespnseCode, __get_HTTPresponseCode, _Camel_Case, _Test e _test_Method.
freegnu,

3
@Evan, quelle persone sarebbero dei cattivi programmatori.
Jesse Dhillon,

3

Per divertirci:

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

O, più per divertimento:

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

1
Python non ha regex? A veloce 's / [az] \ K ([AZ] [az]) / _ \ L $ 1 / g; $ _ 'in Perl fa il lavoro (anche se non gestisce bene getHTTPResponseCode; ma ci si aspetta che dovrebbe essere chiamato getHttpResponseCode)
jrockway

5
str.joinè stato deprecato per anni . Usa ''.join(..)invece.
John Fouhy,

jrockway: ha espressioni regolari, tramite il modulo "re". Non dovrebbe essere troppo difficile farlo funzionare usando regex piuttosto che gli approcci pubblicati qui.
Matthew Iselin,

Python noob qui, ma perché return str.join ('', output)? Solo per creare una copia?
Tarks

3

L'uso di regex potrebbe essere il più breve, ma questa soluzione è molto più leggibile:

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 è completamente indipendente, questa domanda non ha nulla a che fare con il django.
3k

È solo un esempio, come HTTPResponseCode, gestito da stackoverflow.com/a/23561109/15690 .
azzurrato il

3

Tanti metodi complicati ... Basta trovare tutto il gruppo "Titolato" e unire la sua variante in maiuscolo con trattino basso.

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

Se non vuoi creare numeri come il primo carattere di un gruppo o un gruppo separato, puoi usare la ([A-z][a-z0-9]*)maschera.



2

Questo non è un metodo elegante, è un'implementazione molto "di basso livello" di una semplice macchina a stati (bitfield state machine), probabilmente la modalità più anti-pitonica per risolvere questo problema, tuttavia re module implementa anche una macchina a stati troppo complessa per risolvere questo semplice compito, quindi penso che questa sia una buona soluzione.

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 può analizzare tutti i tipi di casi: UpperSEQUENCEInterleaved, under_score, BIG_SYMBOLS e cammelCasedMethods

Spero sia utile


1
Orribile, ma corre circa 3 volte più veloce del metodo regex sulla mia macchina. :)
jdiaz5513


1

Dai un'occhiata all'eccellente lib Schematics

https://github.com/schematics/schematics

Ti consente di creare strutture di dati tipizzate che possono serializzare / deserializzare da Python a JavaScript, ad esempio:

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

Questo semplice metodo dovrebbe fare il lavoro:

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()
  • Cerchiamo lettere maiuscole precedute da qualsiasi numero di lettere maiuscole (o zero) e seguite da un numero qualsiasi di caratteri minuscoli.
  • Un carattere di sottolineatura viene inserito poco prima del verificarsi dell'ultima lettera maiuscola trovata nel gruppo e uno può essere posizionato prima di quella lettera maiuscola nel caso sia preceduto da altre lettere maiuscole.
  • Se sono presenti trattini bassi finali, rimuovili.
  • Infine, l'intera stringa del risultato viene modificata in lettere minuscole.

(preso da qui , vedi esempio di lavoro online )


Questa è una risposta per la domanda opposta (come convertire in caso di cammello).
Giustino,

1

Wow, l'ho appena rubato dai frammenti di Django. arbitro http://djangosnippets.org/snippets/585/

Abbastanza elegante

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

Esempio:

camelcase_to_underscore('ThisUser')

Ritorna:

'this_user'

REGICE DEMO


1
Modulo non valido che utilizza str come nome di variabile locale.
freegnu,

Ciò fallisce miseramente se ci sono caratteri di sottolineatura all'inizio o alla fine di una stringa e se ci sono caratteri di sottolineatura prima di una lettera maiuscola.
freegnu,

non tiene conto dei numeri 😬
villy393

0

Un esempio orribile che usa espressioni regolari (potresti facilmente ripulirlo :)):

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

Funziona con getHTTPResponseCode però!

In alternativa, usando 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")

EDIT: Dovrebbe anche essere abbastanza facile vedere che c'è spazio per miglioramenti per casi come "Test", perché il carattere di sottolineatura è inserito incondizionatamente.


0

Ecco qualcosa che ho fatto per modificare le intestazioni in un file delimitato da tabulazioni. Ometto la parte in cui ho modificato solo la prima riga del file. Potresti adattarlo a Python abbastanza facilmente con la nuova libreria. Ciò include anche la separazione dei numeri (ma mantiene le cifre insieme). L'ho fatto in due passaggi perché era più semplice che dirgli di non mettere un trattino basso all'inizio di una riga o di una scheda.

Primo passo ... trova lettere maiuscole o numeri interi preceduti da lettere minuscole e precedili con un trattino basso:

Ricerca:

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

Sostituzione:

\1_\l\2/

Passaggio due ... prendi quanto sopra ed eseguilo di nuovo per convertire tutti i maiuscoli in minuscolo:

Ricerca:

([A-Z])

Sostituzione (barra rovesciata, L minuscola, barra rovesciata, una):

\l\1

0

Stavo cercando una soluzione allo stesso problema, tranne per il fatto che avevo bisogno di una catena; per esempio

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

Partendo dalle simpatiche soluzioni di due parole qui, ho pensato a quanto segue:

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

La maggior parte della logica complicata consiste nell'evitare di mettere in minuscolo la prima parola. Ecco una versione più semplice se non ti dispiace alterare la prima parola:

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

Ovviamente, puoi precompilare le espressioni regolari o unirle con trattino basso anziché trattino, come discusso nelle altre soluzioni.


0

Conciso senza espressioni regolari, ma 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

Senza alcuna biblioteca:

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

Un po 'pesante, ma

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

0

Molto bello RegEx proposto su questo sito :

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

Se python ha un metodo Split String, dovrebbe funzionare ...

In Java:

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

Sfortunatamente, il modulo di espressione regolare di Python (a partire dalla versione 3.6) non supporta la suddivisione su corrispondenze di lunghezza zero.
velocità

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

E se dobbiamo coprire un caso con input già senza camme:

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

0

Nel caso in cui qualcuno debba trasformare un file sorgente completo, ecco uno script che lo farà.

# 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

Ho avuto abbastanza fortuna con questo:

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)

Questo potrebbe ovviamente essere ottimizzato per la velocità di un piccolo po 'se si vuole.

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

Uso: str.capitalize() per convertire la prima lettera della stringa (contenuta nella variabile str) in una lettera maiuscola e restituisce l'intera stringa.

Esempio: comando: "ciao" .capitalize () Output: Ciao


Questo non è correlato alla domanda - l'OP vuole CamelCase -> snake_case, non Capitalization.
Brad Koch,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.