Konversi garis bawah ke PascalCase, yaitu UpperCamelCase

28

Jika saya memiliki string yang terlihat seperti ini:

"this_is_the_string"

Di dalam skrip bash, saya ingin mengonversinya menjadi PascalCase, yaitu UpperCamelCase agar terlihat seperti ini:

"ThisIsTheString"

Saya menemukan bahwa mengonversi ke lowerCamelCase dapat dilakukan seperti ini:

"this_is_the_string" | sed -r 's/([a-z]+)_([a-z])([a-z]+)/\1\U\2\L\3/'

Sayangnya saya tidak cukup akrab dengan regex untuk memodifikasi ini.

pengguna1135541
sumber
(1) Ini tidak terlalu penting, sejauh menyangkut pertanyaan ini (dan jawaban yang disajikan sejauh ini), tetapi, FYI, \U\2memasukkan teks yang ditemukan dari grup kedua, dikonversi ke ALL CAPS. Bandingkan dengan \u\2, yang menyisipkan teks dalam huruf Kalimat, dengan hanya karakter pertama yang ditulis dengan huruf kapital. (2) Semua contoh yang diberikan di bawah ini akan menerjemahkan "this_is_a_string" menjadi "ThisIsAString" - yang Anda minta, tetapi agak sulit dibaca. Anda mungkin ingin merevisi persyaratan Anda untuk huruf khusus satu kata (substring). … (Lanjutan)
Scott
(Lanjutan) ... (3) Apakah Anda hanya memiliki satu string per baris? Dan apakah selalu teks pertama (atau satu - satunya ) di telepon? Jika Anda memiliki string yang tidak ada di awal baris, jawaban di bawah ini akan mengubahnya menjadi lowerCamelCase. Untuk memperbaikinya, ambil jawaban Janis dan ubah (^|_)ke (\<|_).
Scott
1
inverse: stackoverflow.com/questions/28795479/…
Ciro Santilli 改造 改造 中心 法轮功 六四 事件

Jawaban:

44
$ echo "this_is_the_string" | sed -r 's/(^|_)([a-z])/\U\2/g'            
ThisIsTheString

Ganti pola
(^|_)pada awal string atau setelah underscore - grup pertama
([a-z])huruf kecil satu - grup kedua
dengan
\U\2huruf besar grup kedua
gsecara global.

Janis
sumber
4
Catatan: \Uadalah ekstensi GNU ke POSIX.
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
1
Sekadar catatan, Anda juga harus menangkap angka sed -r 's/(^|[-_ ]+)([0-9a-z])/\U\2/g'. Jadi string seperti "this_is_2nd_string" juga berfungsi.
pinkeen
9

Karena Anda menggunakan bash, jika Anda menyimpan string dalam variabel, Anda juga dapat melakukannya hanya shell:

uscore="this_is_the_string_to_be_converted"
arr=(${uscore//_/ })
printf %s "${arr[@]^}"
ThisIsTheStringToBeConverted

${uscore//_/ }mengganti semua _dengan spasi, (....)memisahkan string menjadi array, ${arr[@]^}mengubah huruf pertama dari setiap elemen menjadi huruf besar dan kemudian printf %s ..mencetak semua elemen satu demi satu.
Anda dapat menyimpan string yang dikunci unta ke variabel lain:

printf -v ccase %s "${arr[@]^}"

dan gunakan / gunakan kembali nanti, misalnya:

printf %s\\n $ccase
ThisIsTheStringToBeConverted

Atau, dengan zsh:

uscore="this_is_the_string_to_be_converted"
arr=(${(s:_:)uscore})
printf %s "${(C)arr}"
ThisIsTheStringToBeConverted

(${(s:_:)uscore})memisahkan string _menjadi sebuah array, (C)huruf besar huruf pertama dari setiap elemen dan printf %s ...mencetak semua elemen satu demi satu ..
Untuk menyimpannya dalam variabel lain yang bisa Anda gunakan (j::)untuk bergabung dengan elemen:

ccase=${(j::)${(C)arr}}

dan gunakan / gunakan kembali nanti:

printf %s\\n $ccase
ThisIsTheStringToBeConverted
don_crissti
sumber
8

Inilah cara Perl:

$ echo "this_is_the_string" | perl -pe 's/(^|_)./uc($&)/ge;s/_//g'
ThisIsTheString

Itu dapat menangani string yang panjangnya berubah-ubah:

$ echo "here_is_another_larger_string_with_more_parts" | 
    perl -pe 's/(^|_)./uc($&)/ge;s/_//g'
HereIsAnotherLargerStringWithMoreParts

Ini akan cocok dengan karakter apa pun ( .) yang muncul setelah dimulainya string atau garis bawah ( (^|_)) dan menggantinya dengan versi huruf besar dari dirinya sendiri ( uc($&)). Ini $&adalah variabel khusus yang berisi apa pun yang baru saja cocok. Pada eakhir s///gememungkinkan penggunaan ekspresi ( uc()fungsi dalam kasus ini) dalam substitusi dan gmembuatnya menggantikan semua kemunculan dalam baris. Substitusi kedua menghilangkan garis bawah.

terdon
sumber
Berbicara tentang perl, ada juga modul perl String :: CamelCase yang "camelizes" menggarisbawahi teks.
don_crissti
@don_crissti ooh, kedengarannya cocok untuk ini. Terima kasih.
terdon
Shorter Perl:perl -pe 's/(^|_)([a-z])/uc($2)/ge'
Isaac
6

Tidak perlu untuk mewakili seluruh string dalam kecocokan ekspresi reguler - sed memiliki /gpengubah yang memungkinkan Anda untuk melewati beberapa pertandingan dan mengganti masing-masing:

echo "this_is_the_string" | sed 's/_\([a-z]\)/\U\1/g;s/^\([a-z]\)/\U\1/g'

Regex pertama adalah _\([a-z]\)- setiap huruf setelah garis bawah; yang kedua cocok dengan huruf pertama dalam sebuah string.

myaut
sumber
3

Saya hanya memasukkan jawaban ini karena lebih pendek dan lebih sederhana daripada yang lain sejauh ini.

sed -re "s~(^|_)(.)~\U\2~g"

Dikatakan: huruf besar, karakter mengikuti _atau awal. Non huruf tidak akan diubah, karena tidak ada huruf besar.

ctrl-alt-delor
sumber
1
"Semuanya harus dibuat sesederhana mungkin, tetapi tidak sederhana." - Albert Einstein. Ini tidak setara dengan jawaban lain; jawaban Anda akan mengonversi "FOO_BAR" menjadi "FOOBAR", sedangkan jawaban lainnya akan membiarkannya.
Scott
@scott Ah ya, saya tidak memikirkan itu.
ctrl-alt-delor
1
@ Esc bukankah itu perilaku yang diinginkan? Saya kira idealnya, itu harus menjadi FooBartetapi garis bawah harus dihapus sesuai instruksi. Seperti yang saya pahami instruksinya.
terdon
2
(Lanjutkan) ... (3) Saya pikir agak jelas bahwa semangat pertanyaan adalah untuk mengubah string sehingga kata istirahat ditunjukkan oleh garis bawah ( _) alih-alih ditunjukkan oleh transisi kasus. Mengingat bahwa, "FOO_BAR" → "FOOBAR" jelas salah (karena membuang informasi pemecah kata), meskipun "FOO_BAR" → "FooBar" mungkin benar. (4) Demikian pula, pemetaan yang menyebabkan tabrakan tampaknya bertentangan dengan semangat pertanyaan. Sebagai contoh, saya percaya bahwa jawaban yang mengubah "DO_SPORTS" dan "DOS_PORTS" ke target yang sama salah.
Scott
1
(Lanjutkan lagi) ... (5) Dengan semangat tidak menyebabkan tabrakan, menurut saya “foo_bar” dan “FOO_BAR” tidak boleh dipetakan ke hal yang sama, jadi oleh karena itu saya keberatan dengan “FOO_BAR” → “FooBar” . (6) Saya pikir masalah yang lebih besar adalah ruang nama. Saya belum memprogram dalam Pascal sejak Blaise masih hidup, tetapi dalam C / C ++, berdasarkan konvensi, pengidentifikasi yang terutama dalam huruf kecil (untuk menyertakan snake_case dan CamelCase) umumnya adalah domain dari kompiler, sedangkan pengidentifikasi dalam huruf besar adalah domain pra-prosesor. Jadi itu sebabnya saya berpikir bahwa OP tidak ingin pengidentifikasi ALL_CAPS dipertimbangkan.
Scott
1

Dalam perl:

$ echo 'alert_beer_core_hemp' | perl -pe 's/(?:\b|_)(\p{Ll})/\u$1/g'
AlertBeerCoreHemp

Ini juga bisa i18n:

$ echo 'алерт_беер_коре_хемп' | perl -CIO -pe 's/(?:\b|_)(\p{Ll})/\u$1/g'
АлертБеерКореХемп
mosvy
sumber
0

Saya melakukannya dengan cara ini:

echo "this_is_the_string" | sed -r 's/(\<|_)([[:alnum:]])/\U\2/g'

dan dapatkan hasil ini:

ThisIsTheString
Fábio Roberto Teodoro
sumber