programing

CamelCase를 snake_case로 변환하는 우아한 Python 함수?

bestcode 2022. 10. 27. 21:57
반응형

CamelCase를 snake_case로 변환하는 우아한 Python 함수?

예:

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

카멜 케이스에서 뱀 케이스로

import re

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

이 작업을 여러 번 수행하고 위의 작업이 느릴 경우 미리 regex를 컴파일하십시오.

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

패키지 인덱스에는 이러한 작업을 처리할 수 있는 변곡 라이브러리가 있습니다.이 경우, 다음과 같이 됩니다.

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

왜 이렇게 복잡한지 모르겠어요.

의 경우,은 "" " " " " " 입니다. " 입니다.([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'

.ALCaps all_caps _ alc alc alc alc 。|은 「」입니다.((?<=[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'

모든 것은 원하는 것에 따라 다르므로, 너무 복잡하지 않기 때문에 고객의 요구에 가장 적합한 솔루션을 사용하십시오.

njoy!

라이브러리 및 정규 표현 회피:

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'

stringcase는 이를 위한 사용 라이브러리입니다.예:

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

저는 이 솔루션이 이전 답변보다 더 간단하다고 생각합니다.

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

정규 표현은 다음 3가지 패턴과 일치합니다.

  1. [A-Z]?[a-z]+: 옵션으로 대문자로 시작하는 연속되는 소문자.
  2. [A-Z]{2,}(?=[A-Z][a-z]|\d|\W|$): 2번입니다.마지막 대문자 뒤에 소문자가 이어지는 경우 미리 보기를 사용하여 제외합니다.
  3. \d+: 연속번호.

「」를 사용해 .re.findall소문자로 변환하여 밑줄로 묶을 수 있는 개별 "단어" 목록을 얻을 수 있습니다.

개인적으로 파이썬에서 정규 표현을 사용하는 것이 어떻게 우아하게 묘사될 수 있을지 모르겠다.대부분의 답은 "코드 골프" 타입의 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('_'+c.lower() if c.isupper() else c for c in "DeathToCamelCase").strip('_')
re.sub("(.)([A-Z])", r'\1_\2', 'DeathToCamelCase').lower()

제 해결책은 다음과 같습니다.

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)

그것은 코멘트에서 논의된 코너 케이스를 지지한다.예를 들어, 변환됩니다.getHTTPResponseCode로로 합니다.get_http_response_code그래야 할 것처럼요

왜 둘 다 .sub() 콜을 사용하는지 모르겠어요.:) regex guru는 아니지만 특정 요구에 맞는 기능을 이 기능으로 단순화했습니다.post 요청에서 vars_with_underscore로 camelCasedVars를 변환하는 솔루션이 필요했을 뿐입니다.

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

get과 같은 이름에서는 동작하지 않습니다.HTTPResponse는 나쁜 명명규칙이라고 들었기 때문에 (getHttpResponse와 같아야 합니다.이 폼을 외우기 쉽다는 것은 명백합니다).

재미삼아:

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

정규식을 사용하는 것이 가장 짧을 수 있지만 이 솔루션은 훨씬 읽기 쉽습니다.

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

이것은 우아한 방법이 아니라 간단한 스테이트 머신(비트필드 스테이트 머신)의 매우 '저레벨' 구현이며, 아마도 이것을 해결하기 위한 가장 반피토닉 모드일 것입니다만, re module도 이 단순한 태스크를 해결하기 위해 너무 복잡한 스테이트 머신을 구현하고 있기 때문에 좋은 해결책이라고 생각합니다.

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 및 cammelCased 메서드

유용했으면 좋겠다

뛰어난 Schematics lib 보기

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

복잡한 방법들이 너무 많아서...모든 "제목" 그룹을 찾고 밑줄로 구분된 하위 버전을 결합하기만 하면 됩니다.

>>> 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]*)mask.mask.mask.mask.

정규 표현을 사용한 끔찍한 예(이것을 간단하게 정리할 수 있습니다:

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

취득에 효과적이다단, HTTPResponse Code!

또는 람다를 사용하여 다음을 수행합니다.

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

편집: 또, 「테스트」등의 경우는, 언더 스코어가 무조건 삽입되어 있기 때문에, 개선의 여지가 있는 것을 알 수 있습니다.

발전기를 사용하는 https://stackoverflow.com/users/267781/matth에서 Lightely를 채택했습니다.

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

다음과 같은 간단한 방법으로 작업을 수행할 수 있습니다.

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()
  • 임의의 수의 대문자(또는 제로) 앞에 임의의 수의 소문자가 이어지는 대문자를 찾습니다.
  • 그룹에서 발견된 마지막 대문자 발생 직전에 밑줄이 표시되며, 다른 대문자 뒤에 있는 경우 해당 대문자 앞에 밑줄을 넣을 수 있습니다.
  • 뒤에 밑줄이 있는 경우 제거합니다.
  • 마지막으로 결과 문자열 전체를 소문자로 변경합니다.

(온라인의 작업 예 참조)

다음은 탭 구분 파일의 헤더를 변경하기 위한 작업입니다.파일의 첫 줄만 편집한 부분은 생략하고 있습니다.re 라이브러리를 사용하면 Python에 쉽게 적응할 수 있습니다.여기에는 숫자 분리도 포함됩니다(단, 숫자를 함께 유지합니다).줄이나 탭의 시작 부분에 밑줄을 그리지 말라고 말하는 것보다 더 쉬웠기 때문에 두 단계로 진행했습니다.

스텝 1...소문자 앞에 대문자 또는 정수를 찾고 그 앞에 밑줄을 그립니다.

검색:

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

교환:

\1_\l\2/

스텝 2...위의 명령어를 다시 실행하여 모든 대소문자를 소문자로 변환합니다.

검색:

([A-Z])

대체(백슬래시, 소문자 L, 백슬래시, 1):

\l\1

같은 문제에 대한 해결책을 찾고 있었지만 체인이 필요했습니다.

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

물론 다른 솔루션에서 설명한 것처럼 정규 표현을 미리 컴파일하거나 하이픈 대신 밑줄로 결합할 수 있습니다.

정규 표현 없이 간결하지만 HTTP ResponseCode=> 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는 매우 훌륭합니다.

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

python이 String Split 메서드를 사용하는 경우 동작합니다.

자바어:

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

완전한 소스 파일을 변환해야 하는 경우에 대비하여 다음 스크립트가 있습니다.

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

장고 토막에서 이걸 훔쳤어참조: 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'

REGEGEX

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

표준 라이브러리에는 없지만 필요한 기능이 포함된 이 모듈을 찾았습니다.

Google의 (거의) 결정론적 Camel 케이스 알고리즘을 사용하면 다음과 같은 것을 처리할 필요가 없습니다.HTMLDocumentHtmlDocument정규식입니다.모든 대문자 또는 숫자를 밑줄로 바꿉니다.노트에서는 여러 자리 숫자는 처리되지 않습니다.

import re

def to_snake_case(camel_str):
    return re.sub('([A-Z0-9])', r'_\1', camel_str).lower().lstrip('_')
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()

용도:str.capitalize()문자열의 첫 글자(변수 스트링에 포함)를 대문자로 변환하고 문자열 전체를 반환합니다.

예: 명령어: "hello".capitalize() 출력:안녕하세요.

언급URL : https://stackoverflow.com/questions/1175208/elegant-python-function-to-convert-camelcase-to-snake-case

반응형