【问题标题】:Elegant Python function to convert CamelCase to snake_case?优雅的 Python 函数将 CamelCase 转换为 snake_case?
【发布时间】:2010-11-13 14:19:29
【问题描述】:

例子:

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

【问题讨论】:

  • 要向另一个方向转换,请参阅这个other stackoverflow 问题。
  • n.b.那是NotCamelCasethisIs
  • @MattRichards 这是一个有争议的问题。 wiki
  • @MattRichards 例如在 Java 中两者都使用,CamelCase 用于命名类定义,而 camelCase 用于命名初始化变量。

标签: python camelcasing


【解决方案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()

专门处理更高级的情况(这不再可逆):

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

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

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

蛇皮套到骆驼皮套

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

【讨论】:

  • 此解决方案在以下情况下失败:_test_Method、__test__Method、_Test、getHTTPresponseCode、__CamelCase 和 _Camel_Case。
  • 反过来呢?将not_camel_case 转换为notCamelCase 和/或NotCamelCase?
  • 为了避免在转换时使用双下划线,例如camel_Case,添加这一行:s2.replace('__', '_')
  • 注意这不是很可逆的。 getHTTPResponseCode 应转换为 get_h_t_t_p_response_code。 getHttpResponseCode 应该转换为 get_http_response_code
  • @AnmolSinghJaggi 第一个正则表达式处理首字母缩略词后跟另一个单词的边缘情况(例如“HTTPResponse”->“HTTP_Response”)或者更常见的初始小写单词后跟大写单词的情况(例如“getResponse”->“get_Response”。第二个正则表达式处理两个非首字母缩写词的正常情况(例如“ResponseCode”->“Response_Code”),最后调用小写所有内容。因此“getHTTPResponseCode”->“ getHTTP_ResponseCode" -> "get_HTTP_Response_Code" -> "get_http_response_code"
【解决方案2】:

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

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

【讨论】:

  • 我不明白当有一个很棒的库可以执行此任务时,为什么人们会投票赞成使用自定义函数。我们不应该重新发明轮子。
  • @oden 可能是因为添加整个新依赖项来完成单行函数的工作是脆弱的过度杀伤?
  • 举个例子,肯定是矫枉过正。在更大的应用程序中,无需重新发明和混淆轮子。
  • 正则表达式很多都回到了“单行”中,这就是为什么经过适当的测试它不仅仅是一行。
  • @CecilCurry:我确定您是一位出色的程序员,但我不确定您是否没有考虑过案例——请查看此处的其他答案以获取示例。这就是我总是选择图书馆的原因,因为它是许多开发人员的总和,而不仅仅是我。
【解决方案3】:

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

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

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

要忽略第一个字符,只需在 (?!^) 后面添加外观

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

如果您想将 ALLCaps 与 all_caps 分开并期望字符串中有数字,您仍然不需要执行两次单独运行,只需使用 | 这个表达式 ((?&lt;=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z])) 可以处理本书中的几乎所有场景

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

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

开心!

【讨论】:

  • 最后一次迭代是最聪明的,IMO。我花了一点时间才明白它只是替换了每个单词开头的单个字符——那只是因为这种方法与我自己想出的方法不同。做得很好。
  • 我对 (?!^) 表达式被称为后视感到困惑。除非我遗漏了什么,否则我们在这里真正想要的是消极的后视,应该表示为(?&lt;!^)。由于某些原因,我无法理解您的负面预测 (?!^) 似乎也有效......
  • 这不能很好地处理预先存在的下划线:"Camel2WARNING_Case_CASE" 变为 "camel2_warning_case__case"。你可以添加一个(?&lt;!_) 否定的lookbehind 来解决它:re.sub('((?&lt;=[a-z0-9])[A-Z]|(?!^)(?&lt;!_)[A-Z](?=[a-z]))', r'_\1', "Camel2WARNING_Case_CASE").lower() 返回'camel2_warning_case_case'
  • @Apteryx 你说得对,(?!^) 被错误地称为“后视”,而应该被称为否定前瞻断言。正如this nice explanation 所示,否定前瞻通常出现在您要搜索的表达式之后。因此,您可以将(?!^) 视为“查找'',其中&lt;start of string&gt; 不跟随”。事实上,消极的后视也有效:您可以将(?&lt;!^) 视为“找到'',其中&lt;start of string&gt; 不在之前”。
【解决方案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'

【讨论】:

  • 这是最紧凑的一个,它避免了使用re 库并且只使用内置的str.methods 只在一行中做这些事情!它类似于this answer,但通过简单地剥离可能添加的“_”作为第一个字符来避免使用切片和附加if ... else。我最喜欢这个。
  • 对于已接受的答案 6.81 µs ± 22.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) 但对于此响应 2.51 µs ± 25.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each),速度要快 2.5 倍!喜欢这个!
  • 这会将诸如“MultinomialNB”之类的首字母缩略词转换为“multinomal_n_b”而不是“multinomial_nb”。
  • URL -> u_r_l, HTTP -> h_t_t_p (我意识到我在这里有点堆积但是......)
  • @WBAR 它可能会更快,但它无法处理大量案例......示例:HIThereHOWIsItGoing, how_are_YoU_TeST。虽然我同意它确实可以很好地完成普通的骆驼案到蛇案
【解决方案5】:

stringcase 是我的首选库;例如:

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

【讨论】:

  • 对于hello world,它会将_ 添加为hello__world 这不好
  • @GonzaloGarcia 这是不正确的:stringcase.snakecase('hello world') 返回'hello_world'(一个下划线)
  • 对不起,如果字符串是hello world,它将添加双_
  • 由用户将正确的参数传递给库:)
  • 双下划线在Python中是常用的;你的“更好”是别人的“更坏”;如果你想要一个具有不同语义的库,你可以使用一个;您的批评不是关于 CamelCase 的原始问题的一部分,而不是空格
【解决方案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,我们得到一个单独的“单词”列表,这些单词可以转换为小写并用下划线连接。

【讨论】:

  • 这里有一个很好的例子,可以独立地对数字进行标记。
  • 破碎:convert("aB") -> 'a'
  • @adw 这个正则表达式涵盖了这种情况:r"[A-Z]?[a-z]+|[A-Z]{2,}(?=[A-Z][a-z]|\d|\W|$)|\d+|[A-Z]{2,}|[A-Z]$"
【解决方案7】:

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

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'

【讨论】:

  • += 在字符串上几乎总是一个坏主意。附加到一个列表并在最后''.join() 它。或者在这种情况下,只需用下划线连接它...
  • 单行正则表达式 not 如何在几乎所有实用方式(包括可读性)上天生优于低效的多行字符迭代和暴力字符串处理? Python 提供开箱即用的正则表达式支持是有原因的。
  • @CecilCurry - 正则表达式非常复杂。查看 Python 使用的编译器和解析器:svn.python.org/projects/python/trunk/Lib/sre_compile.py & svn.python.org/projects/python/trunk/Lib/sre_parse.py -- 像这样的简单字符串操作可能比 RE 执行相同操作要快得多。
  • +1。正则表达式可能是一个真正的 CPU 接收器,并且在密集计算时会大大降低您的性能。对于简单的任务,总是更喜欢简单的功能。
  • “对于简单的任务,总是喜欢简单的函数”绝对是个好建议,但这个答案既不是一个简单的函数也不是一个优雅的答案。正则表达式可能会更慢,但默认使用这样的复杂函数(也未经测试并且有许多潜在的错误点)是完全过早的优化
【解决方案8】:
''.join('_'+c.lower() if c.isupper() else c for c in "DeathToCamelCase").strip('_')
re.sub("(.)([A-Z])", r'\1_\2', 'DeathToCamelCase').lower()

【讨论】:

    【解决方案9】:

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

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

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

    【讨论】:

    • 我忘了说,'{1}' 不是必需的,但有时它有助于澄清一些迷雾。
    • -1:这行不通。例如尝试使用'HTTPConnectionFactory',您的代码会产生'h_tt_pconnection_factory',接受答案的代码会产生'http_connection_factory'
    【解决方案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 \
                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)
    

    它支持 cmets 中讨论的那些极端情况。例如,它会将getHTTPResponseCode 转换为get_http_response_code

    【讨论】:

    • -1 因为这与使用正则表达式相比非常复杂。
    • EOL,我敢肯定很多非正则表达式的人会不这么认为。
    • 此解决方案在以下情况下失败:_Method、_test_Method、__test__Method、getHTTPrespnseCode、__get_HTTPresponseCode、_Camel_Case、_Test 和 _test_Method。
    • @Evan,那些人会是糟糕的程序员。
    【解决方案11】:

    为了好玩:

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

    【讨论】:

    • c.isupper() 而不是 ABCEF 中的 c...Z
    • Python 没有正则表达式?快速 's/[a-z]\K([A-Z][a-z])/_\L$1/g; Perl 中的 lc $_' 完成了这项工作(虽然它不能很好地处理 getHTTPResponseCode;但这是意料之中的,应该命名为 getHttpResponseCode)
    • str.join 已被弃用 ages。请改用''.join(..)
    • jrockway:它确实有正则表达式,通过“re”模块。使用正则表达式而不是此处发布的方法来完成这项工作应该不会太难。
    • 这里是 Python 菜鸟,但为什么要返回 str.join('', output)?只是为了创建一个副本?
    【解决方案12】:

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

    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
    

    【讨论】:

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

    希望有用

    【讨论】:

    • 可怕,但它的运行速度比我机器上的正则表达式方法快 3 倍。 :)
    【解决方案14】:

    这么多复杂的方法... 只需找到所有“Titled”组并使用下划线加入其小写变体。

    >>> 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】:

          看看优秀的 Schematics 库

          https://github.com/schematics/schematics

          它允许您创建可以从 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')
          

          【讨论】:

            【解决方案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()
            
            • 我们查找前面有任意数量(或零个)大写字母,后跟任意数量小写字符的大写字母。
            • 下划线放置在组中最后一个大写字母的出现之前,并且可以在该大写字母之前放置一个下划线,以防它前面有其他大写字母。
            • 如果有尾随下划线,请将其删除。
            • 最后,将整个结果字符串改为小写。

            (取自here,见working example online

            【讨论】:

            • 这是对相反问题的答案(如何将 转换为 驼峰大小写)。
            【解决方案19】:

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

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

            搜索:

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

            替换:

            \1_\l\2/
            

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

            搜索:

            ([A-Z])
            

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

            \l\1
            

            【讨论】:

              【解决方案20】:

              我一直在寻找解决同一问题的方法,但我需要一条链条;例如

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

              从这里不错的两字解决方案开始,我想出了以下内容:

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

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

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

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

              【讨论】:

                【解决方案21】:

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

                【讨论】:

                  【解决方案22】:

                  没有任何库:

                  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
                  

                  【讨论】:

                    【解决方案23】:

                    this site 提出的非常好的 RegEx:

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

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

                    在 Java 中:

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

                    【讨论】:

                    • 不幸的是,Python 正则表达式模块(从 3.6 版开始)不支持在零长度匹配上进行拆分。
                    【解决方案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'\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))
                    

                    【讨论】:

                      【解决方案25】:

                      哇,我刚刚从 django sn-ps 偷了这个。参考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'
                      

                      REGEX DEMO

                      【讨论】:

                      • 使用 str 作为局部变量名的错误形式。
                      • 如果字符串的开头或结尾有任何下划线并且大写字母之前有任何下划线,这将非常失败。
                      • 不考虑数字?
                      【解决方案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】:

                        不在标准库中,但我发现 this module 似乎包含您需要的功能。

                        【讨论】:

                        • 不,这是一个什么都不做的空包。我希望你能用一个代码示例来证明我错了。
                        • 这个 repo 看起来多年来完全没有维护。它也是一个似乎对文件进行操作的整个脚本,甚至不使用 argparse。
                        【解决方案28】:

                        如果您使用Google's (nearly) deterministic Camel case algorithm,则不需要处理HTMLDocument 之类的东西,因为它应该是HtmlDocument,那么这种基于正则表达式的方法很简单。它将所有大写字母或数字替换为下划线。注意不处理多位数字。

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

                        【讨论】:

                          【解决方案29】:
                          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()
                          

                          【讨论】:

                            【解决方案30】:

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

                            示例: 命令:“你好”.capitalize() 输出:你好

                            【讨论】:

                            • 这与问题无关 - OP 想要 CamelCase -> snake_case,而不是大写。
                            猜你喜欢
                            • 1970-01-01
                            • 1970-01-01
                            • 2022-01-25
                            • 1970-01-01
                            • 2017-11-28
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            相关资源
                            最近更新 更多