优雅的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("(?<!^)(?=[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式匹配三种模式:
-
[AZ]?[az]+
:连续的小写字母,可选以大写字母开头。 -
[AZ]{2,}(?=[AZ][az]|\d|\W|$)
:两个或多个连续的大写字母。 如果后面跟着一个小写字母,它将使用一个前视来排除最后一个大写字母。 -
\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