优雅的Python函数将CamelCase转换为snake_case?

例:

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

这是相当彻底的:

 def convert(name): s1 = re.sub('(.)([AZ][az]+)', r'\1_\2', name) return re.sub('([a-z0-9])([AZ])', r'\1_\2', s1).lower() 

适用于所有这些(并且不会损害已有的版本):

 >>> convert('CamelCase') 'camel_case' >>> convert('CamelCamelCase') 'camel_camel_case' >>> convert('Camel2Camel2Case') 'camel2_camel2_case' >>> convert('getHTTPResponseCode') 'get_http_response_code' >>> convert('get2HTTPResponseCode') 'get2_http_response_code' >>> convert('HTTPResponseCode') 'http_response_code' >>> convert('HTTPResponseCodeXYZ') 'http_response_code_xyz' 

或者,如果您打算将其称为数十亿次,则可以预编译正则expression式:

 first_cap_re = re.compile('(.)([AZ][az]+)') all_cap_re = re.compile('([a-z0-9])([AZ])') def convert(name): s1 = first_cap_re.sub(r'\1_\2', name) return all_cap_re.sub(r'\1_\2', s1).lower() 

不要忘记导入正则expression式模块

 import re 

包索引中有一个变形库 ,可以为你处理这些事情。 在这种情况下,你会寻找inflection.underscore()

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

我不知道为什么这些都是如此复杂。

对于大多数情况下,简单的expression式([AZ]+)将做的伎俩

 >>> re.sub('([AZ]+)', r'_\1','CamelCase').lower() '_camel_case' >>> re.sub('([AZ]+)', r'_\1','camelCase').lower() 'camel_case' >>> re.sub('([AZ]+)', r'_\1','camel2Case2').lower() 'camel2_case2' >>> re.sub('([AZ]+)', r'_\1','camelCamelCase').lower() 'camel_camel_case' >>> re.sub('([AZ]+)', r'_\1','getHTTPResponseCode').lower() 'get_httpresponse_code' 

忽略第一个字符只是添加后面(?!^)

 >>> re.sub('(?!^)([AZ]+)', r'_\1','CamelCase').lower() 'camel_case' >>> re.sub('(?!^)([AZ]+)', r'_\1','CamelCamelCase').lower() 'camel_camel_case' >>> re.sub('(?!^)([AZ]+)', r'_\1','Camel2Camel2Case').lower() 'camel2_camel2_case' >>> re.sub('(?!^)([AZ]+)', r'_\1','getHTTPResponseCode').lower() 'get_httpresponse_code' 

如果你想分开ALLCaps到all_caps,并期望你的string中的数字,你仍然不需要做两个单独的运行,只需使用| 这个expression式((?<=[a-z0-9])[AZ]|(?!^)[AZ](?=[az]))可以处理书中的每个场景

 >>> a = re.compile('((?<=[a-z0-9])[AZ]|(?!^)[AZ](?=[az]))') >>> 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' 

这一切都取决于你想要什么,所以使用最适合你的需求的解决scheme,因为它不应该过于复杂。

的nJoy!

就个人而言,我不确定在python中如何使用正则expression式可以被描述为优雅。 这里的大多数答案只是做“代码高尔夫”types的RE技巧。 优雅的编码应该很容易理解。

 def un_camel(x): final = '' for item in x: if item.isupper(): final += "_"+item.lower() else: final += item if final[0] == "_": final = final[1:] return final >>> un_camel("RegularExpressionsAreFunky") 'regular_expressions_are_funky' 

我不明白为什么使用两个.sub()调用? :)我不是正则expression式的大师,但我简化了这个function,这是适合我的某些需求,我只需要一个解决scheme,将camelCasedVars从POST请求转换为vars_with_underscore:

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

它不适用于像getHTTPResponse这样的名字,因为我听说这是不好的命名约定(应该像getHttpResponse,很显然,这是更容易记住这种forms)。

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

stringcase是我的前往图书馆; 例如:

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

为了它的乐趣:

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

或者,更为它的乐趣:

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

这是我的解决scheme:

 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) 

它支持评论中讨论的那些angular落案例。 例如,它会将getHTTPResponseCode转换为get_http_response_code

不在标准库中,但我发现这个脚本似乎包含您需要的function。

这不是一个很好的方法,是一个简单的状态机(位域状态机)的一个非常“低级”的实现,可能是解决这个问题的最好的反pythonic模式,然而,re模块也实现了一个太复杂的状态机来解决这个简单任务,所以我认为这是一个很好的解决scheme。

 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可以parsing所有的病例types:UpperSEQUENCEInterleaved,under_score,BIG_SYMBOLS和cammelCasedMethods

我希望这是有用的

哇,我只是从django片段偷了这个。 ref http://djangosnippets.org/snippets/585/

相当优雅

 camelcase_to_underscore = lambda str: re.sub('(((?<=[az])[AZ])|([AZ](?![AZ]|$)))', '_\\1', str).lower().strip('_') 

例:

 camelcase_to_underscore('ThisUser') 

返回:

 'this_user' 

使用正则expression式可能是最短的,但是这个解决scheme更可读:

 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 

https://stackoverflow.com/users/267781/matth改用了发电机。;

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

看看优秀的原理图库

https://github.com/schematics/schematics

它允许你创buildtypes化的数据结构,可以从python序列化/反序列化为Javascript风格,例如:

 class MapPrice(Model): price_before_vat = DecimalType(serialized_name='priceBeforeVat') vat_rate = DecimalType(serialized_name='vatRate') vat = DecimalType() total_price = DecimalType(serialized_name='totalPrice') 

一个可怕的例子使用正则expression式(你可以很容易地清理:) :))

 def f(s): return s.group(1).lower() + "_" + s.group(2).lower() p = re.compile("([AZ]+[az]+)([AZ]?)") print p.sub(f, "CamelCase") print p.sub(f, "getHTTPResponseCode") 

尽pipegetHTTPResponseCode工程!

或者,使用lambda:

 p = re.compile("([AZ]+[az]+)([AZ]?)") 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") 

编辑:它也应该很容易看到,有像“testing”的情况下有改进的余地,因为下划线是无条件插入。

这是我做的改变标签分隔文件的标题。 我省略了只编辑文件第一行的部分。 你可以很容易地使用re库来适应Python。 这也包括分离出数字(但保持数字在一起)。 我分两步做,因为比起告诉它不要在行或标签的开头加上下划线更容易。

第一步…find以大写字母开头的大写字母或整数,并在其前面加下划线:

search:

 ([az]+)([AZ]|[0-9]+) 

替代:

 \1_\l\2/ 

第二步…采取上述并再次运行它将所有上限转换为小写:

search:

 ([AZ]) 

replace(反斜杠,小写L,反斜杠,一个):

 \l\1 

我正在寻找解决同样的问题,除了我需要一个链条; 例如

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

从这里出色的双字解决scheme开始,我提出了以下内容:

 "-".join(x.group(1).lower() if x.group(2) is None else x.group(1) \ for x in re.finditer("((^.[^AZ]+)|([AZ][^AZ]+))", "stringToSplit")) 

大多数复杂的逻辑是避免降低第一个词。 如果你不介意改变第一个单词,这是一个更简单的版本:

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

当然,正如其他解决scheme所讨论的那样,您可以预编译正则expression式,也可以使用下划线而不是连字符连接。

简洁没有正则expression式,但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())]) 

没有任何图书馆:

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

有点沉重,但是

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

本网站提出的非常好的RegEx:

 (?<!^)(?=[AZ]) 

如果python有一个string拆分方法,它应该工作…

在Java中:

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

这个简单的方法应该做的工作:

 import re def convert(name): return re.sub(r'([AZ]*)([AZ][az]+)', lambda x: (x.group(1) + '_' if x.group(1) else '') + x.group(2) + '_', name).rstrip('_').lower() 
  • 我们查找以大写字母(或零)为前缀的大写字母,后跟任意数量的小写字母。
  • 下一个下划线位于组中最后一个大写字母出现之前,如果前面有其他大写字母,则可以在该大写字母之前放置下划线。
  • 如果尾部有下划线,请删除它们。
  • 最后,整个结果string变为小写。

(从这里取得 , 在线查看实例 )

如果可能,我宁愿避免

myString="ThisStringIsCamelCase" ''.join(['_'+i.lower() if i.isupper() else i for i in myString]).lstrip('_') 'this_string_is_camel_case'

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

如果我们需要用一个已经没有input的input来覆盖一个案例:

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

我认为这个解决scheme比以前的答案更直接:

 import re def convert (camel_input): words = re.findall(r'[AZ]?[az]+|[AZ]{2,}(?=[AZ][az]|\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)) 

哪些产出:

 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 

正则expression式匹配三种模式:

  1. [AZ]?[az]+ :连续的小写字母,可选以大写字母开头。
  2. [AZ]{2,}(?=[AZ][az]|\d|\W|$) :两个或多个连续的大写字母。 如果后面跟着一个小写字母,它将使用一个前视来排除最后一个大写字母。
  3. \d+ :连续的数字。

通过使用re.findall我们得到一个可以转换为小写re.findall单词列表。

我有这个运气相当好:

 import re def camelcase_to_underscore(s): return re.sub(r'(^|[az])([AZ])', lambda m: '_'.join([i.lower() for i in m.groups() if i]), s) 

如果你愿意,这显然可以优化一点速度。

 import re CC2US_RE = re.compile(r'(^|[az])([AZ])') 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) 

使用: str.capitalize()将string(包含在variablesstr中)的第一个字母转换为大写字母,并返回整个string。

示例:命令:“hello”.capitalize()输出:Hello