Fonction Python élégante pour convertir CamelCase en snake_case?

333

Exemple:

>>> convert('CamelCase')
'camel_case'
Sridhar Ratnakumar
la source
28
Pour convertir dans l'autre sens, consultez cette autre question de stackoverflow.
Nathan
10
nb c'est NotCamelCasemaisthisIs
Matt Richards
5
@MattRichards C'est une question controversée. wiki
NO_NAME
@MattRichards Par exemple, en Java, ils utilisent les deux, CamelCase est utilisé pour nommer les définitions de classe, tandis que camelCase est utilisé pour nommer les variables initialisées.
darkless

Réponses:

798

Etui camel à étui serpent

import re

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

Si vous faites cela plusieurs fois et que ce qui précède est lent, compilez au préalable l'expression régulière:

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

Pour gérer des cas plus avancés spécialement (ce n'est plus réversible):

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 serpent à étui camel

name = 'snake_case_name'
name = ''.join(word.title() for word in name.split('_'))
print(name)  # SnakeCaseName
postel
la source
1
Cette solution échoue dans les cas suivants: _test_Method, __test__Method, _Test, getHTTPresponseCode, __CamelCase et _Camel_Case.
freegnu
6
que diriez-vous l'inverse? Convertir un not_camel_casevers notCamelCaseet / ou NotCamelCase?
john2x
9
Pour éviter les doubles soulignements lors de la conversion, par exemple camel_Case, ajoutez cette ligne:s2.replace('__', '_')
Marcus Ahlberg
2
Notez que ce n'est pas très réversible. getHTTPResponseCode doit être converti en get_h_t_t_p_response_code. getHttpResponseCode devrait se convertir en get_http_response_code
K2xL
4
@AnmolSinghJaggi La première expression régulière gère le cas de bord d'un acronyme suivi d'un autre mot (par exemple "HTTPResponse" -> "HTTP_Response") OU le cas plus normal d'un mot initial en minuscule suivi d'un mot en majuscule (par exemple "getResponse" -> " get_Response ". Le deuxième regex gère le cas normal de deux non-acronymes (par exemple" ResponseCode "->" Response_Code ") suivi d'un dernier appel à tout en minuscules. Ainsi" getHTTPResponseCode "->" getHTTP_ResponseCode "->" get_HTTP_Response_Code "- > "get_http_response_code"
Jeff Moser
188

Il y a une bibliothèque d'inflexion dans l'index du package qui peut gérer ces choses pour vous. Dans ce cas, vous recherchez inflection.underscore():

>>> inflection.underscore('CamelCase')
'camel_case'
Brad Koch
la source
44
Je ne comprends pas pourquoi les gens votent pour l'utilisation de fonctions personnalisées alors qu'il existe une excellente bibliothèque qui effectue cette tâche. Nous ne devons pas réinventer la roue.
Oden
88
@oden Peut-être parce que l'ajout d'une toute nouvelle dépendance pour faire le travail d'une fonction sur une seule ligne est une destruction excessive fragile?
Cecil Curry
11
Par exemple, bien sûr, c'est exagéré. Dans une application plus large, pas besoin de réinventer et d'obscurcir la roue.
Brad Koch
11
Regexes remonte beaucoup dans une "ligne unique", c'est pourquoi c'est beaucoup plus d'une ligne avec des tests appropriés.
studgeek
12
@CecilCurry: Je suis sûr que vous êtes un grand programmeur, mais je ne suis pas sûr qu'il n'y ait pas de cas que vous n'ayez pas pris en compte - regardez d'autres réponses ici pour des exemples. C'est pourquoi je choisirai toujours une bibliothèque, car c'est la somme d'expérience de beaucoup plus de développeurs que moi.
Michael Scheper
105

Je ne sais pas pourquoi tout cela est si compliqué.

pour la plupart des cas, la simple expression ([A-Z]+)fera l'affaire

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

Pour ignorer le premier caractère, ajoutez simplement regarder derrière (?!^)

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

Si vous souhaitez séparer ALLCaps en all_caps et attendre des nombres dans votre chaîne, vous n'avez toujours pas besoin de faire deux exécutions distinctes. Utilisez simplement |cette expression qui ((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))peut gérer à peu près tous les scénarios du livre.

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

Tout dépend de ce que vous voulez, alors utilisez la solution qui convient le mieux à vos besoins car elle ne devrait pas être trop compliquée.

nJoy!

nickl-
la source
1
La dernière itération est la plus intelligente, l'OMI. Il m'a fallu un peu de temps pour comprendre que cela ne faisait que remplacer le caractère unique au début de chaque mot - et c'était uniquement parce que l'approche était différente de celle que j'avais inventée moi-même. Bien fait.
Justin Miller
2
J'ai été intrigué par l' (?!^)expression qu'on appelle un regard derrière. À moins que je manque quelque chose, ce que nous voulons vraiment ici, c'est un regard négatif qui devrait être exprimé comme suit (?<!^). Pour des raisons que je ne peux pas comprendre, votre anticipation négative (?!^)semble également fonctionner ...
Apteryx
7
Cela ne gère pas bien les traits de soulignement préexistants: "Camel2WARNING_Case_CASE"devient "camel2_warning_case__case". Vous pouvez ajouter un (?<!_)lookbehind négatif, pour le résoudre: re.sub('((?<=[a-z0-9])[A-Z]|(?!^)(?<!_)[A-Z](?=[a-z]))', r'_\1', "Camel2WARNING_Case_CASE").lower() retours 'camel2_warning_case_case'
luckydonald
@Apteryx Vous avez raison, (?!^) été incorrectement appelé un "regard derrière" et aurait plutôt dû être appelé affirmation d'anticipation négative . Comme le montre cette belle explication , les lookaheads négatifs viennent généralement après l'expression que vous recherchez. Vous pouvez donc penser (?!^)à "trouver ''<start of string>ne suit pas". En effet, un lookbehind négatif fonctionne également: vous pouvez penser (?<!^)à "trouver ''<start of string>ne précède pas".
Nathaniel Jones
18

stringcase est ma bibliothèque de référence pour cela; par exemple:

>>> from stringcase import pascalcase, snakecase
>>> snakecase('FooBarBaz')
'foo_bar_baz'
>>> pascalcase('foo_bar_baz')
'FooBarBaz'
Beau
la source
11

Je préfère éviter re si possible:

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'
colidyre
la source
1
C'est la plus compacte qui évite d'utiliser la rebibliothèque et de faire les choses uniquement sur une seule ligne en utilisant uniquement les str.methods intégrés! Il est similaire à cette réponse , mais évite d'utiliser le découpage et les ajouts if ... elseen supprimant simplement "_" potentiellement ajouté comme premier caractère. J'aime le plus.
colidyre
Pour une réponse acceptée 6.81 µs ± 22.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)mais pour cette réponse 2.51 µs ± 25.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)qui est 2,5 fois plus rapide! Aime ça!
WBAR
10

Personnellement, je ne sais pas comment quelque chose utilisant des expressions régulières en python peut être décrit comme élégant. La plupart des réponses ici ne font que des tours de type RE de «golf de code». Le codage élégant est censé être facilement compris.

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
la source
1
+=sur les cordes est presque toujours une mauvaise idée. Ajouter à une liste et ''.join()à la fin. Ou dans ce cas, il suffit de le joindre avec un trait de soulignement ...
ThiefMaster
22
Comment une expression régulière sur une seule ligne n'est-elle pas intrinsèquement supérieure à peu près de toutes les manières pratiques (y compris la lisibilité) à l'itération de caractères multiligne inefficace et à la fusion de chaînes par force brute? Python fournit une prise en charge des expressions régulières prête à l'emploi pour une raison.
Cecil Curry
1
@CecilCurry - Les expressions régulières sont TRÈS complexes. Voir le compilateur et l'analyseur utilisé par Python: svn.python.org/projects/python/trunk/Lib/sre_compile.py & svn.python.org/projects/python/trunk/Lib/sre_parse.py - manipulation de chaîne simple comme cela est probablement beaucoup plus rapide qu'un RE faisant de même.
Evan Borgstrom
1
+1. Les regex peuvent être un véritable puits de CPU et, lors de calculs intensifs, vos performances diminueront considérablement. Pour les tâches simples, préférez toujours les fonctions simples.
Fabien
4
«Pour les tâches simples, préférez toujours les fonctions simples» est certainement un bon conseil, mais cette réponse n'est ni simple ni élégante. Regex peut être plus lent, mais la mise en défaut d'une fonction compliquée comme celle-ci (qui est AUSSI non testée et a de nombreux points d'erreur potentiels) est une optimisation complètement prématurée
kevlarr
8
''.join('_'+c.lower() if c.isupper() else c for c in "DeathToCamelCase").strip('_')
re.sub("(.)([A-Z])", r'\1_\2', 'DeathToCamelCase').lower()
Jimmy
la source
7

Je pense que cette solution est plus simple que les réponses précédentes:

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

Quelles sorties:

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'expression régulière correspond à trois modèles:

  1. [A-Z]?[a-z]+: Lettres minuscules consécutives commençant éventuellement par une lettre majuscule.
  2. [A-Z]{2,}(?=[A-Z][a-z]|\d|\W|$): Deux lettres majuscules consécutives ou plus. Il utilise un lookahead pour exclure la dernière lettre majuscule si elle est suivie d'une lettre minuscule.
  3. \d+: Nombres consécutifs.

En utilisant, re.findallnous obtenons une liste de "mots" individuels qui peuvent être convertis en minuscules et joints avec des traits de soulignement.

vitesse
la source
1
Il y a un bon exemple ici pour obtenir le jeton Numerics indépendamment.
math_law
1
Cassé: convertir ("aB") -> 'a'
adw
5

Je ne sais pas pourquoi utiliser les deux appels .sub ()? :) Je ne suis pas un gourou des regex, mais j'ai simplifié la fonction pour celui-ci, qui convient à mes besoins, j'avais juste besoin d'une solution pour convertir camelCasedVars de la requête POST en vars_with_underscore:

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

Cela ne fonctionne pas avec des noms tels que getHTTPResponse, car j'ai entendu dire que c'est une mauvaise convention de dénomination (devrait être comme getHttpResponse, c'est évidemment, qu'il est beaucoup plus facile de mémoriser ce formulaire).

desper4do
la source
J'ai oublié de mentionner que '{1}' n'est pas nécessaire, mais cela aide parfois à clarifier certains brouillards.
desper4do
2
-1: cela ne fonctionne tout simplement pas. Essayez avec par exemple avec 'HTTPConnectionFactory', votre code produit 'h_tt_pconnection_factory', le code de la réponse acceptée produit'http_connection_factory'
vartec
4

Voici ma solution:

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)

Il prend en charge les cas d'angle discutés dans les commentaires. Par exemple, il va convertir getHTTPResponseCodeà get_http_response_codecomme il devrait.

Evan Fosmark
la source
7
-1 car c'est très compliqué par rapport à l'utilisation d'expressions rationnelles.
Eric O Lebigot
7
EOL, je suis sûr que beaucoup de gens non-regexp penseraient le contraire.
Evan Fosmark
Cette solution échoue dans les cas suivants: _Method, _test_Method , __test__Method, getHTTPrespnseCode, __get_HTTPresponseCode, _Camel_Case, _Test et _test_Method.
freegnu
3
@Evan, ces gens seraient de mauvais programmeurs.
Jesse Dhillon
3

Pour le plaisir:

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

Ou, plus pour le plaisir:

>>> 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'
gahooa
la source
3
c.isupper () plutôt que c dans ABCEF ... Z
Jimmy
1
Python n'a pas d'expressions régulières? Un rapide / s / [az] \ K ([AZ] [az]) / _ \ L $ 1 / g; lc $ _ 'en Perl fait le travail (bien qu'il ne gère pas bien getHTTPResponseCode; mais cela est attendu, cela devrait être nommé getHttpResponseCode)
jrockway
5
str.joinest obsolète depuis des lustres . Utilisez ''.join(..)plutôt.
John Fouhy
jrockway: Il a des expressions régulières, via le module "re". Il ne devrait pas être trop difficile de faire ce travail en utilisant l'expression régulière plutôt que les approches affichées ici.
Matthew Iselin
Python noob ici, mais pourquoi retourner str.join ('', output)? Juste pour créer une copie?
Tarks
3

L'utilisation d'expressions régulières peut être la plus courte, mais cette solution est beaucoup plus lisible:

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-
la source
@blueyed qui est complètement indépendant, cette question n'a rien à voir avec django.
3
Ce n'est qu'un exemple, comme HTTPResponseCode, qui est géré par stackoverflow.com/a/23561109/15690 .
blueyed
3

Tant de méthodes compliquées ... Il suffit de trouver tout le groupe "Titled" et de rejoindre sa variante avec un trait de soulignement inférieur.

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

Si vous ne voulez pas faire de nombres comme le premier caractère du groupe ou un groupe séparé - vous pouvez utiliser le ([A-z][a-z0-9]*)masque.

unitto
la source
2

Pas dans la bibliothèque standard, mais j'ai trouvé ce script qui semble contenir les fonctionnalités dont vous avez besoin.

Stefano Borini
la source
2

Ce n'est pas une méthode élégante, c'est une implémentation de très bas niveau d'une machine à états simple (machine à états à champ de bits), probablement le mode le plus anti-pythonique pour résoudre ce problème, mais le module implémente également une machine à états trop complexe pour résoudre ce problème simple tâche, donc je pense que c'est une bonne solution.

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 peut analyser tous les types de cas: UpperSEQUENCEInterleaved, under_score, BIG_SYMBOLS et cammelCasedMethods

J'espère que cela est utile

jdavidls
la source
1
Hideux, mais il fonctionne environ 3 fois plus vite que la méthode regex sur ma machine. :)
jdiaz5513
1

Légèrement adapté de https://stackoverflow.com/users/267781/matth qui utilise des générateurs.

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()
Salvatore
la source
1

Jetez un œil à l'excellente bibliothèque Schematics

https://github.com/schematics/schematics

Il vous permet de créer des structures de données typées qui peuvent sérialiser / désérialiser de python en saveur Javascript, par exemple:

class MapPrice(Model):
    price_before_vat = DecimalType(serialized_name='priceBeforeVat')
    vat_rate = DecimalType(serialized_name='vatRate')
    vat = DecimalType()
    total_price = DecimalType(serialized_name='totalPrice')
Iain Hunter
la source
1

Cette méthode simple devrait faire le travail:

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()
  • Nous recherchons des majuscules précédées d'un nombre quelconque (ou nul) de majuscules et suivies d'un nombre quelconque de caractères minuscules.
  • Un trait de soulignement est placé juste avant l'occurrence de la dernière lettre majuscule trouvée dans le groupe, et un peut être placé avant cette lettre majuscule au cas où il serait précédé d'autres lettres majuscules.
  • S'il y a des tirets bas, supprimez-les.
  • Enfin, la chaîne de résultat entière est modifiée en minuscules.

(extrait d' ici , voir l' exemple de travail en ligne )

Mathieu Rodic
la source
Ceci est une réponse à la question opposée (comment convertir en cas de chameau).
Justin
1

Wow, je viens de voler ça aux extraits de django. ref http://djangosnippets.org/snippets/585/

Assez élégant

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

Exemple:

camelcase_to_underscore('ThisUser')

Retour:

'this_user'

REGEX DEMO

brianray
la source
1
Forme incorrecte utilisant str comme nom de variable locale.
freegnu
Cela échoue lamentablement s'il y a des traits de soulignement au début ou à la fin d'une chaîne et s'il y a des traits de soulignement avant une majuscule.
freegnu
ne prend pas en compte les chiffres 😬
villy393
0

Un exemple horrible utilisant des expressions régulières (vous pouvez facilement nettoyer ça :)):

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

Fonctionne pour getHTTPResponseCode!

Alternativement, en utilisant 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: Il devrait également être assez facile de voir qu'il y a place à amélioration pour des cas comme "Test", car le trait de soulignement est inséré sans condition.

Matthew Iselin
la source
0

Voici quelque chose que j'ai fait pour modifier les en-têtes d'un fichier délimité par des tabulations. J'omets la partie où je n'ai édité que la première ligne du fichier. Vous pouvez l'adapter assez facilement à Python avec la bibliothèque re. Cela comprend également la séparation des nombres (mais conserve les chiffres ensemble). Je l'ai fait en deux étapes, car c'était plus facile que de lui dire de ne pas mettre de soulignement au début d'une ligne ou d'un onglet.

Première étape ... rechercher des lettres majuscules ou des entiers précédés de lettres minuscules et les faire précéder d'un trait de soulignement:

Chercher:

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

Remplacement:

\1_\l\2/

Deuxième étape ... prenez ce qui précède et exécutez-le à nouveau pour convertir toutes les majuscules en minuscules:

Chercher:

([A-Z])

Remplacement (c'est-à-dire barre oblique inverse, L minuscule, barre oblique inverse, un):

\l\1
Joe Tricarico
la source
0

Je cherchais une solution au même problème, sauf que j'avais besoin d'une chaîne; par exemple

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

À partir des belles solutions en deux mots ici, j'ai trouvé ce qui suit:

"-".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 plupart de la logique compliquée consiste à éviter de mettre en minuscule le premier mot. Voici une version plus simple si cela ne vous dérange pas de modifier le premier mot:

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

Bien sûr, vous pouvez précompiler les expressions régulières ou joindre avec un trait de soulignement au lieu d'un trait d'union, comme indiqué dans les autres solutions.

Jim Pivarski
la source
0

Concis sans expressions régulières, mais 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())])
Dantalion
la source
0

Sans aucune bibliothèque:

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 peu lourd, mais

CamelCamelCamelCase ->  camel_camel_camel_case
HTTPRequest         ->  http_request
GetHTTPRequest      ->  get_http_request
getHTTPRequest      ->  get_http_request
bibmartin
la source
0

Très beau RegEx proposé sur ce site :

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

Si python a une méthode String Split, cela devrait fonctionner ...

En Java:

String s = "loremIpsum";
words = s.split("(?&#60;!^)(?=[A-Z])");
Jmini
la source
Malheureusement, le module d'expression régulière Python ne prend pas en charge (à partir de la version 3.6) le fractionnement sur les correspondances de longueur nulle.
vitesse
0
def convert(name):
    return reduce(
        lambda x, y: x + ('_' if y.isupper() else '') + y, 
        name
    ).lower()

Et si nous devons couvrir un cas avec une entrée déjà non camelée:

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

Juste au cas où quelqu'un aurait besoin de transformer un fichier source complet, voici un script qui le fera.

# 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))
Pascal T.
la source
-1

J'ai eu pas mal de chance avec celui-ci:

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)

Cela pourrait évidemment être optimisé pour la vitesse d' un petit peu si vous voulez.

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
la source
-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
la source
-3

Utilisez: str.capitalize()pour convertir la première lettre de la chaîne (contenue dans la variable str) en une lettre majuscule et renvoie la chaîne entière.

Exemple: Commande: "bonjour" .capitalize () Sortie: Bonjour

Arshin
la source
Ce n'est pas lié à la question - l'OP veut CamelCase -> snake_case, pas Capitalisation.
Brad Koch