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

2025-01-15 08:45:00
admin
原创
98
摘要:问题描述:例子:>>> convert('CamelCase') 'camel_case' 解决方案 1:驼峰式命名法转为蛇形命名法import re name = 'CamelCaseName' name = re.sub(r'(?<!^)(?=[A-Z])', '_', name...

问题描述:

例子:

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

解决方案 1:

驼峰式命名法转为蛇形命名法

import re

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

如果您多次执行此操作并且上述操作很慢,请预先编译正则表达式:

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

请注意,此正则表达式及其后紧随的正则表达式使用零宽度匹配,这在 Python 3.6 或更早版本中无法正确处理。如果您需要支持较旧的 EOL Python,请参阅下文了解不使用前瞻/后瞻的替代方案。

如果您想避免转换"HTTPHeader""h_t_t_p_header",可以使用带有正则表达式交替的变体:

pattern = re.compile(r"(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])")
name = pattern.sub('_', name).lower()

请参阅Regex101.com了解测试用例(不包括最终的小写字母)。

?x您可以使用或来提高可读性re.X

pattern = re.compile(
    r"""
        (?<=[a-z])      # preceded by lowercase
        (?=[A-Z])       # followed by uppercase
        |               #   OR
        (?<[A-Z])       # preceded by lowercase
        (?=[A-Z][a-z])  # followed by uppercase, then lowercase
    """,
    re.X,
)

如果使用regex模块而不是re,则可以使用更易读的 POSIX 字符类(不仅限于 ASCII)。

pattern = re.compile(
    r"""
        (?<=[[:lower:]])            # preceded by lowercase
        (?=[[:upper:]])             # followed by uppercase
        |                           #   OR
        (?<[[:upper:]])             # preceded by lower
        (?=[[:upper:]][[:lower:]])  # followed by upper then lower
    """,
    re.X,
)

处理更高级情况的另一种方法是不依赖于前瞻/后瞻,使用两次替换过程:

def camel_to_snake(name):
    name = re.sub('(.)([A-Z][a-z]+)', r'_', name)
    return re.sub('([a-z0-9])([A-Z])', r'_', 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

还添加带有两个或更多下划线的情况:

def to_snake_case(name):
    name = re.sub('(.)([A-Z][a-z]+)', r'_', name)
    name = re.sub('__([A-Z])', r'_', name)
    name = re.sub('([a-z0-9])([A-Z])', r'_', name)
    return name.lower()

蛇形命名法转为帕斯卡命名法

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

解决方案 2:

软件包索引中有一个词形变化库,可以帮你处理这些事情。在本例中,你需要查找inflection.underscore()

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

解决方案 3:

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

大多数情况下,简单的表达([A-Z]+)就可以解决问题

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

要忽略第一个字符,只需添加 look behind(?!^)

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

如果你想将 ALLCaps 分离为 all_caps 并期望字符串中出现数字,你仍然不需要进行两次单独的运行,只需使用|这个表达式((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))就可以处理书中的每一个场景

>>> a = re.compile('((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))')
>>> a.sub(r'_', 'getHTTPResponseCode').lower()
'get_http_response_code'
>>> a.sub(r'_', 'get2HTTPResponseCode').lower()
'get2_http_response_code'
>>> a.sub(r'_', 'get2HTTPResponse123Code').lower()
'get2_http_response123_code'
>>> a.sub(r'_', 'HTTPResponseCode').lower()
'http_response_code'
>>> a.sub(r'_', 'HTTPResponseCodeXYZ').lower()
'http_response_code_xyz'

这完全取决于您想要什么,因此请使用最适合您需求的解决方案,因为它不应该过于复杂。

好开心!

解决方案 4:

避免使用库和正则表达式:

def camel_to_snake(s):
    return ''.join(['_'+c.lower() if c.isupper() else c for c in s]).lstrip('_')
>>> camel_to_snake('ThisIsMyString')
'this_is_my_string'

解决方案 5:

stringcase是我为此使用的库;例如:

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

解决方案 6:

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

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

输出:

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

正则表达式匹配三种模式:

  1. [A-Z]?[a-z]+:连续的小写字母,可以以大写字母开头。

  2. [A-Z]{2,}(?=[A-Z][a-z]|d|W|$):两个或多个连续的大写字母。如果最后一个大写字母后面跟着一个小写字母,则使用前瞻排除最后一个大写字母。

  3. d+:连续的数字。

通过使用,re.findall我们得到一个可以转换为小写并用下划线连接的单个“单词”列表。

解决方案 7:

就我个人而言,我不确定在 Python 中使用正则表达式的任何东西如何能被描述为优雅。这里的大多数答案只是在玩“代码高尔夫”类型的正则技巧。优雅的编码应该很容易理解。

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'

解决方案 8:

''.join('_'+c.lower() if c.isupper() else c for c in "DeathToCamelCase").strip('_')
re.sub("(.)([A-Z])", r'_', 'DeathToCamelCase').lower()

解决方案 9:

使用正则表达式可能是最短的,但这种解决方案更具可读性:

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

解决方案 10:

这是我的解决方案:

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

它支持评论中讨论的那些极端情况。例如,它会像应该的getHTTPResponseCode那样进行转换get_http_response_code

解决方案 11:

我不明白为什么要使用两个 .sub() 调用?:) 我不是正则表达式专家,但我将函数简化为这个,它适合我的某些需求,我只需要一个解决方案将 camelCasedVars 从 POST 请求转换为 vars_with_underscore:

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

它不适用于像 getHTTPResponse 这样的名称,因为我听说它是​​不好的命名约定(应该像 getHttpResponse,显然,记住这种形式要容易得多)。

解决方案 12:

为了好玩:

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

解决方案 13:

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

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 可以解析所有案例类型:UpperSEQUENCEInterleaved、under_score、BIG_SYMBOLS 和 cammelCasedMethods

我希望它有用

解决方案 14:

这么多复杂的方法......只需找到所有“标题”组并将其小写变体与下划线连接起来。

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

如果您不想让数字成为组的第一个字符或单独的组 - 您可以使用([A-z][a-z0-9]*)掩码。

解决方案 15:

使用正则表达式的一个可怕的例子(你可以轻松地清理它:)):

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

尽管适用于 getHTTPResponseCode!

或者,使用 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")

编辑:还应该很容易看出,对于“测试”这样的情况,还有改进的空间,因为下划线是无条件插入的。

解决方案 16:

轻微改编自
使用生成器的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()

解决方案 17:

这个网站上提出了非常好的 RegEx :

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

如果 python 有一个 String Split 方法,它应该可以工作......

在 Java 中:

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

解决方案 18:

这个简单的方法应该可以完成这项工作:

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()
  • 我们寻找前面有任意数量(或零个)大写字母,后面有任意数量的小写字符的大写字母。

  • 在组中找到的最后一个大写字母之前放置一个下划线,并且如果该大写字母前面有其他大写字母,则可以在该大写字母之前放置一个下划线。

  • 如果有尾随下划线,请将其删除。

  • 最后,整个结果字符串变为小写。

(取自此处,请参阅在线工作示例)

解决方案 19:

如果您使用Google 的(几乎)确定性 Camel 大小写算法,则无需处理诸如 之类的事情,HTMLDocument因为应该是HtmlDocument,那么这种基于正则表达式的方法很简单。它用下划线替换所有大写字母或数字。注意不处理多位数字。

import re

def to_snake_case(camel_str):
    return re.sub('([A-Z0-9])', r'_', camel_str).lower().lstrip('_')

解决方案 20:

这是我为更改制表符分隔文件的标题所做的操作。我省略了只编辑文件第一行的部分。您可以使用 re 库轻松地将其改编为 Python。这还包括分离数字(但将数字保持在一起)。我分两步完成,因为这比告诉它不要在行或制表符的开头添加下划线更容易。

第一步...找到大写字母或前面有小写字母的整数,并在它们前面加上下划线:

搜索:

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

替代品:

_l/

第二步...采取上述操作并再次运行,将所有大写字母转换为小写字母:

搜索:

([A-Z])

替换(即反斜杠,小写 L,反斜杠,一):

l

解决方案 21:

我正在寻找同一问题的解决方案,只是我需要一个链;例如

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

从这里的两个字的解决方案开始,我得出了以下结论:

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

大多数复杂的逻辑都是为了避免将第一个单词小写。如果您不介意更改第一个单词,这里有一个更简单的版本:

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

当然,您可以预编译正则表达式或用下划线而不是连字符连接,如其他解决方案中所讨论的那样。

解决方案 22:

简洁无需正则表达式,但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())])

解决方案 23:

没有任何库:

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

解决方案 24:

万一有人需要转换完整的源文件,这里有一个脚本可以完成此操作。

# 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'_', name)
    return re.sub('([a-z0-9])([A-Z])', r'_', s1).lower()

def lines(str):
    return str.split("
")

def unlines(lst):
    return "
".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))

解决方案 25:

哇,我刚刚从 django snippets 偷了这个。参考http://djangosnippets.org/snippets/585/

非常优雅

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

例子:

camelcase_to_underscore('ThisUser')

返回:

'this_user'

正则表达式演示

解决方案 26:

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

如果我们需要处理一个已经非驼峰式输入的情况:

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

解决方案 27:

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

解决方案 28:

用途:str.capitalize()将字符串(包含在变量str中)的首字母转换为大写字母并返回整个字符串。

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

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1590  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1361  
  信创产品在政府采购中的占比分析随着信息技术的飞速发展以及国家对信息安全重视程度的不断提高,信创产业应运而生并迅速崛起。信创,即信息技术应用创新,旨在实现信息技术领域的自主可控,减少对国外技术的依赖,保障国家信息安全。政府采购作为推动信创产业发展的重要力量,其对信创产品的采购占比情况备受关注。这不仅关系到信创产业的发展前...
信创和国产化的区别   18  
  信创,即信息技术应用创新产业,旨在实现信息技术领域的自主可控,摆脱对国外技术的依赖。近年来,国货国用信创发展势头迅猛,在诸多领域取得了显著成果。这一发展趋势对科技创新产生了深远的推动作用,不仅提升了我国在信息技术领域的自主创新能力,还为经济社会的数字化转型提供了坚实支撑。信创推动核心技术突破信创产业的发展促使企业和科研...
信创工作   18  
  信创技术,即信息技术应用创新产业,旨在实现信息技术领域的自主可控与安全可靠。近年来,信创技术发展迅猛,对中小企业产生了深远的影响,带来了诸多不可忽视的价值。在数字化转型的浪潮中,中小企业面临着激烈的市场竞争和复杂多变的环境,信创技术的出现为它们提供了新的发展机遇和支撑。信创技术对中小企业的影响技术架构变革信创技术促使中...
信创国产化   19  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用