小智 734

这非常彻底:

def convert(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()
Run Code Online (Sandbox Code Playgroud)

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

>>> 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'
Run Code Online (Sandbox Code Playgroud)

或者,如果你打算多次调用它,你可以预编译正则表达式:

first_cap_re = re.compile('(.)([A-Z][a-z]+)')
all_cap_re = re.compile('([a-z0-9])([A-Z])')
def convert(name):
    s1 = first_cap_re.sub(r'\1_\2', name)
    return all_cap_re.sub(r'\1_\2', s1).lower()
Run Code Online (Sandbox Code Playgroud)

不要忘记导入正则表达式模块

import re
Run Code Online (Sandbox Code Playgroud)

  • github对此进行了额外的测试:https://gist.github.com/3660565 (9认同)
  • 为了避免在转换例如camel_Case时出现双下划线,请添加以下行:`s2.replace('__','_')` (8认同)
  • 反过来怎么样?将`not_camel_case`转换为`notCamelCase`和/或`NotCamelCase`? (6认同)
  • @AnmolSinghJaggi第一个正则表达式处理首字母缩略词的边缘情况,后跟另一个单词(例如"HTTPResponse" - >"HTTP_Response")或初始小写单词后跟大写单词的更正常情况(例如"getResponse" - >" get_Response".第二个正则表达式处理两个非首字母缩略词的正常情况(例如"ResponseCode" - >"Response_Code"),然后是最后一次调用小写的所有内容.因此"getHTTPResponseCode" - >"getHTTP_ResponseCode" - >"get_HTTP_Response_Code" - >"get_http_response_code" (4认同)
  • 此解决方案在以下情况下失败:_test_Method、__test__Method、_Test、getHTTPresponseCode、__CamelCase 和 _Camel_Case。 (3认同)
  • `convert = lambda 名称:re.sub('((?!^)(?<!_)[AZ][az]+|(?<=[a-z0-9])[AZ])', r '_\1', name).lower()` - 这可以正确处理 freegnu 的情况。 (3认同)
  • 请注意,这不是很可逆.getHTTPResponseCode应转换为get_h_t_t_p_response_code.getHttpResponseCode应转换为get_http_response_code (2认同)
  • `APIKey` => `a_p_i_key` (2认同)

Bra*_*och 166

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

>>> inflection.underscore('CamelCase')
'camel_case'
Run Code Online (Sandbox Code Playgroud)

  • @oden也许因为添加一个**全新的依赖**来完成单行函数的工作是脆弱的过度杀戮? (72认同)
  • 我不明白为什么当有一个很棒的库执行这个任务时,人们会投票使用自定义函数.我们不应该重新发明轮子. (37认同)
  • Regexes重新回到了"单行",这就是为什么它通过适当的测试不止一条线. (10认同)
  • 举个例子,确定它太过分了.在更大的应用程序中,无需重新发明和混淆轮子. (9认同)
  • @CecilCurry:我确定你是一个优秀的程序员,但我不确定你没有考虑过的案例 - 只是看看其他答案的例子.这就是为什么我总是选择一个图书馆,因为它是更多开发人员的总体经验,而不仅仅是我. (9认同)
  • @oden,您可以将整个库(一个文件)(https://github.com/jpvanhal/inflection/blob/master/inflection.py)复制粘贴到您的代码库中,这比复制稍微好一些粘贴堆栈溢出的答案并且从不更新。如果这让您感觉好一点,该库自 2015 年以来也没有更新过。 (3认同)
  • @MichaelScheper你总是可以复制`inflection`库代码:`re.sub(r"([az\d])([AZ])", r'\1_\2', re.sub(r"( [AZ]+)([AZ][az])", r'\1_\2', word)).replace("-", "_").lower()` https://inflection.readthedocs。 io/en/latest/_modules/inflection.html#underscore (2认同)

nic*_*kl- 93

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

对于大多数情况,简单的表达式([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'
Run Code Online (Sandbox Code Playgroud)

忽略第一个charachter只需添加后面的外观 (?!^)

>>> 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'
Run Code Online (Sandbox Code Playgroud)

如果你想将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'_\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'
Run Code Online (Sandbox Code Playgroud)

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

的nJoy!

  • 这不能很好地处理预先存在的下划线:`"Camel2WARNING_Case_CASE"`变成`"camel2_warning_case__case"`.你可以添加一个`(?<!_)`负向lookbehind来解决它:`re.sub('((?<= [a-z0-9])[AZ] |(?!^)(?< !_)[AZ](?= [az]))',r'_ 1',"Camel2WARNING_Case_CASE").lower()`返回''camel2_warning_case_case'` (5认同)
  • @Apteryx你是对的,`(?!^)`被错误地称为“向后看”,而应该被称为_负向前看断言_。正如[这个很好的解释](/sf/answers/208144681/)所示,否定先行通常出现在您正在搜索的表达式之后。因此,您可以将 `(?!^)` 视为“查找 `&lt;start of string&gt;` 不跟在后面的位置”。事实上,负向后查找也有效:您可以将 `(?&lt;!^)` 视为“在 `&lt;start of string&gt;` 不位于前面的位置查找 `''`”。 (3认同)
  • 我被`(?!^)`表达式称为后视混淆了.除非我遗漏了什么,否则我们真正想要的是负面的后视,应该表示为`(?<!^)`.由于我无法理解你的负面预测`(?!^)`似乎也有效... (2认同)

oto*_*can 29

避免库和正则表达式:

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

  • 这是最紧凑的一个,它避免使用 `re` 库,并且只使用内置的 str.methods 在一行中完成这些工作!它类似于[这个答案](/sf/answers/2014233231/),但通过简单地删除可能添加的“_”作为第一个字符来避免使用切片和附加的“if ... else”。我最喜欢这个了。 (3认同)
  • 这会将“MultinomialNB”等首字母缩略词转换为“multinomial_n_b”而不是“multinomial_nb”。 (3认同)
  • 对于接受的答案“每个循环 6.81 µs ± 22.5 ns(7 次运行的平均值 ± 标准偏差,每次 100000 个循环)”,但对于此响应“每个循环 2.51 µs ± 25.5 ns(7 次运行的平均值 ± 标准偏差,每个循环 100000 次)`,速度提高了 2.5 倍!喜欢这个! (2认同)
  • URL -&gt; u_r_l,HTTP -&gt; h_t_t_p (我意识到我在这里堆积了一点,但是......) (2认同)

Teh*_*ris 13

就个人而言,我不确定在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'
Run Code Online (Sandbox Code Playgroud)

  • 单行正则表达式如何在几乎所有实用方式(包括可读性)中无效地优于低效的多行字符迭代和强力字符串变换?由于某种原因,Python提供了开箱即用的正则表达式支持. (14认同)
  • "对于简单的任务,总是喜欢简单的功能"绝对是一个很好的建议,但这个答案既不是简单的功能,也不是优雅的功能.正则表达式可能会更慢,但默认为这样一个复杂的函数(这是未测试的,并且有很多潜在的错误点)是完全不成熟的优化 (3认同)
  • 字符串上的“+=”几乎总是一个坏主意。追加到列表中并在最后添加`''.join()`。或者在这种情况下,只需用下划线将其连接起来...... (2认同)
  • 抱歉,与简单的正则表达式相比,这个函数很难阅读。 (2认同)

Bea*_*eau 12

stringcase是我的首选库; 例如:

>>> from stringcase import pascalcase, snakecase
>>> snakecase('FooBarBaz')
'foo_bar_baz'
>>> pascalcase('foo_bar_baz')
'FooBarBaz'
Run Code Online (Sandbox Code Playgroud)

  • 双下划线在Python中常用;你的“更好”是别人的“更差”;如果您想要一个具有不同语义的库,您可以使用一个;您的批评不是关于 CamelCase 的原始问题的一部分,而不是空格 (3认同)
  • 由用户将正确的参数传递给库:) (2认同)

Jim*_*mmy 8

''.join('_'+c.lower() if c.isupper() else c for c in "DeathToCamelCase").strip('_')
re.sub("(.)([A-Z])", r'\1_\2', 'DeathToCamelCase').lower()
Run Code Online (Sandbox Code Playgroud)


rsp*_*eed 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))
Run Code Online (Sandbox Code Playgroud)

哪个输出:

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
Run Code Online (Sandbox Code Playgroud)

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

  1. [A-Z]?[a-z]+:连续的小写字母,可以选择以大写字母开头。
  2. [A-Z]{2,}(?=[A-Z][a-z]|\d|\W|$):两个或多个连续的大写字母。如果在前一个大写字母后跟一个小写字母,它将使用前瞻性排除。
  3. \d+:连续数字。

通过使用,re.findall我们获得了单个“单词”的列表,这些单词可以转换为小写并带有下划线。

  • 损坏:convert("aB") -&gt; 'a' (2认同)

小智 5

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

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

它不适用于像getHTTPResponse这样的名称,因为我听说它是​​错误的命名约定(应该像getHttpResponse,显然,它更容易记住这个表单).

  • -1:这是行不通的。例如,尝试使用“ HTTPConnectionFactory”,您的代码生成“ h_tt_pconnection_factory”,接受的答案中的代码生成“ http_connection_factory”。 (2认同)