Exemple:
>>> convert('CamelCase')
'camel_case'
NotCamelCase
maisthisIs
Exemple:
>>> convert('CamelCase')
'camel_case'
NotCamelCase
maisthisIs
Réponses:
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
name = 'snake_case_name'
name = ''.join(word.title() for word in name.split('_'))
print(name) # SnakeCaseName
not_camel_case
vers notCamelCase
et / ou NotCamelCase
?
s2.replace('__', '_')
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'
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!
(?!^)
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 ...
"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'
(?!^)
é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 ''
où <start of string>
ne suit pas". En effet, un lookbehind négatif fonctionne également: vous pouvez penser (?<!^)
à "trouver ''
où <start of string>
ne précède pas".
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'
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'
re
bibliothè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 ... else
en supprimant simplement "_" potentiellement ajouté comme premier caractère. J'aime le plus.
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!
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'
+=
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 ...
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:
[A-Z]?[a-z]+
: Lettres minuscules consécutives commençant éventuellement par une lettre majuscule.[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.\d+
: Nombres consécutifs.En utilisant, re.findall
nous obtenons une liste de "mots" individuels qui peuvent être convertis en minuscules et joints avec des traits de soulignement.
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).
'HTTPConnectionFactory'
, votre code produit 'h_tt_pconnection_factory'
, le code de la réponse acceptée produit'http_connection_factory'
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_code
comme il devrait.
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'
str.join
est obsolète depuis des lustres . Utilisez ''.join(..)
plutôt.
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
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.
Pas dans la bibliothèque standard, mais j'ai trouvé ce script qui semble contenir les fonctionnalités dont vous avez besoin.
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
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()
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')
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()
(extrait d' ici , voir l' exemple de travail en ligne )
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'
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.
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
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.
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())])
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
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("(?<!^)(?=[A-Z])");
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()
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))
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)
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