Fungsi Python elegan untuk mengonversi CamelCase ke snake_case?

333

Contoh:

>>> convert('CamelCase')
'camel_case'
Sridhar Ratnakumar
sumber
28
Untuk mengkonversi ke arah lain, melihat ini lain pertanyaan stackoverflow.
Nathan
10
nb itu NotCamelCasetapithisIs
Matt Richards
5
@ MatRichards Ini masalah perselisihan. wiki
NO_NAME
@ MatRichards Sebagai contoh di Jawa mereka menggunakan keduanya, CamelCase digunakan untuk menamai definisi Kelas, sementara camelCase digunakan untuk menamai variabel yang diinisialisasi.
darkless

Jawaban:

798

Kasing unta untuk kasing ular

import re

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

Jika Anda melakukan ini berulang kali dan yang di atas lambat, kompilasi regex sebelumnya:

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

Untuk menangani kasus yang lebih canggih secara khusus (ini tidak dapat dibalik lagi):

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

Case ular ke case unta

name = 'snake_case_name'
name = ''.join(word.title() for word in name.split('_'))
print(name)  # SnakeCaseName
epost
sumber
1
Solusi ini gagal dalam kasus ini: _test_Method, __test__Method, _Test, getHTTPresponseCode, __CamelCase, dan _Camel_Case.
freegnu
6
bagaimana dengan sebaliknya? Konversi not_camel_caseke notCamelCasedan / atau NotCamelCase?
john2x
9
Untuk menghindari garis bawah dua kali lipat saat mengonversi mis. Camel_Case, tambahkan baris ini:s2.replace('__', '_')
Marcus Ahlberg
2
Perhatikan ini tidak terlalu reversibel. getHTTPResponseCode harus dikonversi menjadi get_h_t_t_p_response_code. getHttpResponseCode harus dikonversi ke get_http_response_code
K2xL
4
@AnmolSinghJaggi Regex pertama menangani kasus tepi akronim diikuti oleh kata lain (misalnya "HTTPResponse" -> "HTTP_Response") ATAU kasus yang lebih normal dari kata kecil awal diikuti dengan huruf besar (misalnya "getResponse" -> " get_Response ". Regex kedua menangani kasus normal dua non-akronim (mis." ResponseCode "->" Response_Code ") diikuti dengan panggilan terakhir untuk merender semuanya. Dengan demikian" getHTTPResponseCode "->" getHTHT__ResponseCode "->" get_HTTP_ResponseCode "->" get_HTTP_ResponseCode "-> > "get_http_response_code"
Jeff Moser
188

Ada perpustakaan infleksi dalam indeks paket yang dapat menangani hal-hal ini untuk Anda. Dalam hal ini, Anda akan mencari inflection.underscore():

>>> inflection.underscore('CamelCase')
'camel_case'
Brad Koch
sumber
44
Saya tidak mengerti mengapa orang-orang memilih menggunakan fungsi-fungsi khusus ketika ada perpustakaan hebat yang melakukan tugas ini. Kita seharusnya tidak menciptakan kembali roda.
oden
88
@oden Mungkin karena menambahkan seluruh ketergantungan baru untuk melakukan pekerjaan fungsi single-line rapuh over-kill?
Cecil Curry
11
Untuk satu contoh, yakin itu berlebihan. Di aplikasi yang lebih besar, tidak perlu menemukan kembali dan mengaburkan roda.
Brad Koch
11
Regex kembali banyak menjadi "baris tunggal", itulah sebabnya mengapa lebih dari satu baris dengan pengujian yang tepat.
studgeek
12
@CecilCurry: Saya yakin Anda seorang programmer yang hebat, tetapi saya tidak yakin tidak ada kasus yang belum Anda pertimbangkan — lihat saja jawaban lain di sini sebagai contoh. Itu sebabnya saya akan selalu memilih perpustakaan, karena ini adalah pengalaman terbaik dari lebih banyak devs daripada hanya saya.
Michael Scheper
105

Saya tidak tahu mengapa ini semua sangat menyulitkan.

untuk sebagian besar kasus, ekspresi sederhana ([A-Z]+)akan berhasil

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

Untuk mengabaikan karakter pertama cukup tambahkan tampilan di belakang (?!^)

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

Jika Anda ingin memisahkan ALLCaps ke all_caps dan mengharapkan angka dalam string Anda, Anda masih tidak perlu melakukan dua run terpisah gunakan saja. |Ungkapan ini ((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))dapat menangani hampir setiap skenario di buku

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

Itu semua tergantung pada apa yang Anda inginkan jadi gunakan solusi yang paling sesuai dengan kebutuhan Anda karena tidak boleh terlalu rumit.

nJoy!

nickl-
sumber
1
Iterasi terakhir adalah yang paling pintar, IMO. Saya butuh sedikit untuk memahami bahwa itu hanya mengganti karakter tunggal pada awal setiap kata - dan itu hanya karena pendekatannya berbeda dari yang saya buat sendiri. Bagus sekali.
Justin Miller
2
Saya bingung dengan (?!^)ekspresi yang disebut melihat-belakang. Kecuali jika saya melewatkan sesuatu, yang benar-benar kita inginkan di sini adalah pandangan negatif yang harus diungkapkan (?<!^). Untuk alasan yang saya tidak bisa mengerti pandangan negatif Anda (?!^)tampaknya bekerja juga ...
Apteryx
7
Ini tidak menangani garis bawah yang sudah ada sebelumnya dengan baik: "Camel2WARNING_Case_CASE"menjadi "camel2_warning_case__case". Anda dapat menambahkan tampilan (?<!_)negatif di belakang, untuk menyelesaikannya: re.sub('((?<=[a-z0-9])[A-Z]|(?!^)(?<!_)[A-Z](?=[a-z]))', r'_\1', "Camel2WARNING_Case_CASE").lower() pengembalian 'camel2_warning_case_case'
luckydonald
@Apteryx Anda benar, (?!^)secara keliru disebut "lihat di belakang" dan seharusnya disebut pernyataan pencarian yang negatif . Seperti yang ditunjukkan penjelasan yang bagus ini , lookaheads negatif biasanya muncul setelah ekspresi yang Anda cari. Jadi Anda dapat menganggapnya (?!^)sebagai "menemukan di ''mana <start of string>tidak mengikuti". Memang, tampilan negatif di belakang juga berfungsi: Anda dapat menganggapnya (?<!^)sebagai "menemukan di ''mana <start of string>tidak mendahului".
Nathaniel Jones
18

stringcase adalah perpustakaan pilihan saya untuk ini; misalnya:

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

Saya lebih suka menghindari rejika mungkin:

def to_camelcase(s):
    return ''.join(['_' + c.lower() if c.isupper() else c for c in s]).lstrip('_')
>>> to_camelcase("ThisStringIsCamelCase")
'this_string_is_camel_case'
colidyre
sumber
1
Ini adalah yang paling ringkas yang menghindari menggunakan reperpustakaan dan melakukan hal-hal hanya dalam satu baris hanya menggunakan str.methods built-in! Ini mirip dengan jawaban ini , tetapi hindari menggunakan pengirisan dan tambahan if ... elsedengan hanya mengupas yang berpotensi ditambahkan "_" sebagai karakter pertama. Saya sangat suka ini.
colidyre
Untuk jawaban yang diterima 6.81 µs ± 22.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)tetapi untuk respons ini 2.51 µs ± 25.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)yang 2,5 kali lebih cepat! Suka ini!
WBAR
10

Secara pribadi saya tidak yakin bagaimana pun menggunakan ekspresi reguler dalam python dapat digambarkan sebagai elegan. Sebagian besar jawaban di sini hanya melakukan trik "kode golf" jenis RE. Pengkodean yang elegan seharusnya mudah dipahami.

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'
TehTris
sumber
1
+=pada string hampir selalu merupakan ide yang buruk. Tambahkan ke daftar dan ''.join()pada akhirnya. Atau dalam hal ini, cukup bergabung dengan garis bawah ...
ThiefMaster
22
Bagaimana ekspresi reguler satu-baris tidak secara bawaan unggul dalam hampir setiap cara praktis (termasuk keterbacaan) untuk iterasi karakter multi-garis yang tidak efisien dan tali hijauing brute-force? Python menyediakan dukungan ekspresi reguler di luar kotak karena suatu alasan.
Cecil Curry
1
@CecilCurry - Ekspresi reguler SANGAT kompleks. Lihat kompilator dan parser yang digunakan Python: svn.python.org/projects/python/trunk/Lib/sre_compile.py & svn.python.org/projects/python/trunk/Lib/sre_parse.py - Manipulasi string sederhana seperti ini kemungkinan jauh lebih cepat daripada RE melakukan hal yang sama.
Evan Borgstrom
1
+1. Regex dapat menjadi CPU sink nyata, dan pada perhitungan intensif akan secara dramatis menurunkan kinerja Anda. Untuk tugas-tugas sederhana, selalu lebih suka fungsi-fungsi sederhana.
Fabien
4
"Untuk tugas-tugas sederhana, selalu lebih suka fungsi-fungsi sederhana" jelas merupakan nasihat yang baik, tetapi jawaban ini bukan fungsi yang sederhana atau elegan. Regex mungkin lebih lambat, tetapi default ke fungsi yang rumit seperti ini (yang JUGA belum teruji dan memiliki banyak titik kesalahan) adalah optimasi yang sepenuhnya prematur
kevlarr
8
''.join('_'+c.lower() if c.isupper() else c for c in "DeathToCamelCase").strip('_')
re.sub("(.)([A-Z])", r'\1_\2', 'DeathToCamelCase').lower()
Jimmy
sumber
7

Saya pikir solusi ini lebih mudah daripada jawaban sebelumnya:

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

Output yang mana:

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

Ekspresi reguler cocok dengan tiga pola:

  1. [A-Z]?[a-z]+: Huruf kecil berurutan yang secara opsional dimulai dengan huruf besar.
  2. [A-Z]{2,}(?=[A-Z][a-z]|\d|\W|$): Dua atau lebih huruf besar berturut-turut. Ini menggunakan lookahead untuk mengecualikan huruf besar terakhir jika diikuti oleh huruf kecil.
  3. \d+: Nomor yang berurutan.

Dengan menggunakan re.findallkita mendapatkan daftar "kata-kata" individual yang dapat dikonversi menjadi huruf kecil dan digabungkan dengan garis bawah.

rspeed
sumber
1
Ada contoh yang bagus di sini untuk membuat Token Numerik dipatenkan secara independen.
math_law
1
Rusak: convert ("aB") -> 'a'
adw
5

Saya tidak mengerti mengapa menggunakan keduanya .sub () panggilan? :) Saya bukan guru regex, tapi saya menyederhanakan fungsi yang satu ini, yang cocok untuk kebutuhan tertentu saya, saya hanya perlu solusi untuk mengkonversi camelCasedVars dari permintaan POST ke vars_with_underscore:

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

Itu tidak bekerja dengan nama-nama seperti getHTTPResponse, karena saya dengar itu penamaan yang buruk (harus seperti getHttpResponse, itu jelas, bahwa lebih mudah menghafal formulir ini).

putus asa
sumber
Saya lupa menyebutkan, bahwa '{1}' tidak diperlukan, tetapi kadang-kadang membantu menjelaskan beberapa kabut.
desper4do
2
-1: ini tidak berhasil. Coba dengan misalnya dengan 'HTTPConnectionFactory', kode Anda menghasilkan 'h_tt_pconnection_factory', kode dari jawaban yang diterima menghasilkan'http_connection_factory'
vartec
4

Inilah solusi saya:

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)

Ini mendukung kasus sudut yang dibahas dalam komentar. Misalnya, itu akan dikonversi getHTTPResponseCodemenjadi get_http_response_codeseperti seharusnya.

Evan Fosmark
sumber
7
-1 karena ini sangat rumit dibandingkan dengan menggunakan regexps.
Eric O Lebigot
7
EOL, saya yakin banyak orang non-regexp akan berpikir sebaliknya.
Evan Fosmark
Solusi ini gagal dalam kasus-kasus ini: _Method, _test_Method , __test__Method, getHTTPrespnseCode, __get_HTTPresponseCode, _Camel_Case, _Test, dan _test_Method.
freegnu
3
@ Evan, orang-orang itu akan menjadi programmer yang buruk.
Jesse Dhillon
3

Untuk bersenang-senang:

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

Atau, lebih untuk bersenang-senang:

>>> 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'
gahooa
sumber
3
c.isupper () daripada c di ABCEF ... Z
Jimmy
1
Python tidak memiliki regex? Quick's / [az] \ K ([AZ] [az]) / _ \ L $ 1 / g; lc $ _' di Perl melakukan pekerjaan (meskipun tidak menangani getHTTPResponseCode baik, tetapi yang diharapkan, yang harus dinamai getHttpResponseCode)
jrockway
5
str.jointelah usang sejak lama . Gunakan ''.join(..)sebagai gantinya.
John Fouhy
jrockway: Itu memang memiliki ekspresi reguler, melalui modul "re". Seharusnya tidak terlalu sulit untuk membuat pekerjaan ini menggunakan regex daripada pendekatan yang diposting di sini.
Matthew Iselin
Python noob di sini, tetapi mengapa mengembalikan str.join ('', output)? Hanya untuk membuat salinan?
Tarks
3

Menggunakan regex mungkin yang terpendek, tetapi solusi ini jauh lebih mudah dibaca:

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
3k-
sumber
@blueyed itu sama sekali tidak berhubungan, pertanyaan ini tidak ada hubungannya dengan Django.
3k-
Itu hanya sebuah contoh, seperti HTTPResponseCode, yang ditangani oleh stackoverflow.com/a/23561109/15690 .
biru
3

Begitu banyak metode yang rumit ... Cukup temukan semua grup "Berjudul" dan gabungkan varian kase bawahnya dengan garis bawah.

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

Jika Anda tidak ingin membuat angka seperti karakter pertama grup atau grup terpisah - Anda dapat menggunakan ([A-z][a-z0-9]*)mask.

bersatu
sumber
2

Tidak di perpustakaan standar, tetapi saya menemukan skrip ini yang tampaknya berisi fungsionalitas yang Anda butuhkan.

Stefano Borini
sumber
2

Ini bukan metode yang elegan, implementasi yang sangat 'rendah' ​​dari mesin keadaan sederhana (mesin bitfield state), mungkin mode yang paling anti pythonic untuk menyelesaikan ini, namun modul ini juga menerapkan mesin keadaan terlalu rumit untuk menyelesaikan mesin sederhana ini. tugas, jadi saya pikir ini adalah solusi yang bagus.

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 dapat mem-parsing semua jenis kasus: UpperSEQUENCEDisatukan, under_score, BIG_SYMBOLS, dan cammelCasedMethods

Semoga bermanfaat

jdavidl
sumber
1
Hideous, tetapi ini berjalan sekitar 3x lebih cepat dari metode regex di komputer saya. :)
jdiaz5513
1

Diadaptasi secara ringan dari https://stackoverflow.com/users/267781/matth yang menggunakan generator.

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()
Salvatore
sumber
1

Lihatlah Schibatics lib yang luar biasa

https://github.com/schematics/schematics

Hal ini memungkinkan Anda untuk membuat struktur data yang diketik yang dapat membuat cerita bersambung / deserialize dari python ke Javascript, misalnya:

class MapPrice(Model):
    price_before_vat = DecimalType(serialized_name='priceBeforeVat')
    vat_rate = DecimalType(serialized_name='vatRate')
    vat = DecimalType()
    total_price = DecimalType(serialized_name='totalPrice')
Iain Hunter
sumber
1

Metode sederhana ini harus melakukan pekerjaan:

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()
  • Kami mencari huruf kapital yang didahului oleh sejumlah (atau nol) huruf kapital, dan diikuti oleh sejumlah huruf kecil.
  • Garis bawah diletakkan tepat sebelum huruf kapital terakhir ditemukan dalam grup, dan seseorang dapat ditempatkan sebelum huruf kapital tersebut jika diawali dengan huruf kapital lain.
  • Jika ada garis bawah yang tertinggal, hapus itu.
  • Akhirnya, seluruh string hasil diubah menjadi huruf kecil.

(diambil dari sini , lihat contoh kerja online )

Mathieu Rodic
sumber
Ini adalah jawaban untuk pertanyaan sebaliknya (bagaimana mengkonversi ke case unta).
Justin
1

Wow saya baru saja mencuri ini dari potongan Django. ref http://djangosnippets.org/snippets/585/

Cukup elegan

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

Contoh:

camelcase_to_underscore('ThisUser')

Pengembalian:

'this_user'

REGEX DEMO

brianray
sumber
1
Formulir salah menggunakan str sebagai nama variabel lokal.
freegnu
Ini gagal total jika ada garis bawah pada awal atau akhir string dan jika ada garis bawah sebelum huruf kapital.
freegnu
tidak memperhitungkan nomor rekening 😬
villy393
0

Contoh menghebohkan menggunakan ekspresi reguler (Anda dapat dengan mudah membersihkan ini :)):

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

Bekerja untuk getHTTPResponseCode!

Atau, gunakan 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")

EDIT: Seharusnya juga cukup mudah untuk melihat bahwa ada ruang untuk perbaikan untuk kasus-kasus seperti "Tes", karena garis bawah dimasukkan tanpa syarat.

Matthew Iselin
sumber
0

Berikut adalah sesuatu yang saya lakukan untuk mengubah header pada file yang dibatasi-tab. Saya menghilangkan bagian di mana saya hanya mengedit baris pertama file. Anda dapat mengadaptasinya ke Python dengan cukup mudah dengan pustaka ulang. Ini juga termasuk memisahkan angka-angka (tetapi menjaga angka bersama-sama). Saya melakukannya dalam dua langkah karena itu lebih mudah daripada mengatakannya untuk tidak meletakkan garis bawah pada awal baris atau tab.

Langkah Satu ... cari huruf besar atau bilangan bulat didahului dengan huruf kecil, dan awali dengan garis bawah:

Cari:

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

Penggantian:

\1_\l\2/

Langkah Dua ... ambil yang di atas dan jalankan lagi untuk mengonversi semua huruf besar menjadi huruf kecil:

Cari:

([A-Z])

Penggantian (yaitu backslash, huruf kecil L, backslash, satu):

\l\1
Joe Tricarico
sumber
0

Saya mencari solusi untuk masalah yang sama, kecuali bahwa saya membutuhkan rantai; misalnya

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

Mulai dari solusi dua kata yang bagus di sini, saya datang dengan yang berikut:

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

Sebagian besar logika yang rumit adalah untuk menghindari mengurangi kata pertama. Ini versi yang lebih sederhana jika Anda tidak keberatan mengubah kata pertama:

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

Tentu saja, Anda dapat melakukan pre-kompilasi ekspresi reguler atau bergabung dengan garis bawah alih-alih tanda hubung, seperti yang dibahas dalam solusi lain.

Jim Pivarski
sumber
0

Ringkas tanpa ekspresi reguler, tetapi 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())])
Dantalion
sumber
0

Tanpa perpustakaan apa pun:

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('__','_')

Agak berat, tapi

CamelCamelCamelCase ->  camel_camel_camel_case
HTTPRequest         ->  http_request
GetHTTPRequest      ->  get_http_request
getHTTPRequest      ->  get_http_request
bibmartin
sumber
0

RegEx yang sangat bagus diusulkan di situs ini :

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

Jika python memiliki metode String Split, itu harus berfungsi ...

Di Jawa:

String s = "loremIpsum";
words = s.split("(?&#60;!^)(?=[A-Z])");
Jmini
sumber
Sayangnya, modul ekspresi reguler Python tidak (pada versi 3.6) mendukung pemisahan pada kecocokan dengan panjang nol.
rspeed
0
def convert(name):
    return reduce(
        lambda x, y: x + ('_' if y.isupper() else '') + y, 
        name
    ).lower()

Dan jika kita perlu membahas kasus dengan input yang sudah di-un-camel:

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

Untuk berjaga-jaga jika seseorang perlu mengubah file sumber yang lengkap, berikut adalah skrip yang akan melakukannya.

# 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))
Pascal T.
sumber
-1

Saya cukup beruntung dengan yang ini:

import re
def camelcase_to_underscore(s):
    return re.sub(r'(^|[a-z])([A-Z])',
                  lambda m: '_'.join([i.lower() for i in m.groups() if i]),
                  s)

Ini jelas bisa dioptimalkan untuk kecepatan kecil sedikit jika Anda ingin.

import re

CC2US_RE = re.compile(r'(^|[a-z])([A-Z])')

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)
codekoala
sumber
-1
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()
JohnBoy
sumber
-3

Menggunakan: str.capitalize() untuk mengkonversi huruf pertama dari string (terkandung dalam variabel str) ke huruf kapital dan mengembalikan seluruh string.

Contoh: Perintah: "halo" .capitalize () Output: Halo

Arshin
sumber
Ini tidak terkait dengan pertanyaan - OP menginginkan CamelCase -> snake_case, bukan Kapitalisasi.
Brad Koch