แปลงขีดล่างเป็น PascalCase เช่น UpperCamelCase


28

หากฉันมีสตริงที่มีลักษณะเช่นนี้:

"this_is_the_string"

ภายในสคริปต์ทุบตีฉันต้องการแปลงเป็น PascalCase เช่น UpperCamelCase ให้มีลักษณะดังนี้:

"ThisIsTheString"

ฉันพบว่าการแปลงเป็น lowerCamelCase สามารถทำได้ดังนี้:

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

น่าเสียดายที่ฉันไม่คุ้นเคยกับ regexes ที่จะแก้ไขสิ่งนี้


(1) สิ่งนี้ไม่สำคัญเท่าที่คำถามนี้ (และคำตอบที่นำเสนอ) เกี่ยวข้อง แต่ FYI \U\2แทรกข้อความที่พบจากกลุ่มที่สองซึ่งแปลงเป็น CAPS ทั้งหมด เปรียบเทียบกับ\u\2ซึ่งแทรกข้อความในกรณีประโยคโดยมีเพียงอักขระตัวแรกที่เป็นตัวพิมพ์ใหญ่ (2) ตัวอย่างทั้งหมดที่ระบุด้านล่างจะแปล“ this_is_a_string” เป็น“ ThisIsAString” - ซึ่งเป็นสิ่งที่คุณขอ แต่เป็นการยากที่จะอ่าน คุณอาจต้องการแก้ไขข้อกำหนดของคุณสำหรับกรณีพิเศษของคำหนึ่งตัวอักษร (ซับสตริง) … (ต่อ)
สกอตต์

(ต่อ) ... (3) คุณมีสตริงดังกล่าวเพียงหนึ่งรายการต่อบรรทัดหรือไม่? และเป็นข้อความแรก (หรือข้อความเดียว ) เสมอในบรรทัดหรือไม่ หากคุณมีสตริงที่ไม่ใช่จุดเริ่มต้นของบรรทัดคำตอบด้านล่างจะแปลงเป็น lowerCamelCase ในการแก้ไขปัญหาใช้คำตอบของเจนิสและการเปลี่ยนแปลงไป(^|_) (\<|_)
สกอตต์

คำตอบ:


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

รูปแบบการทดแทน
(^|_)ที่จุดเริ่มต้นของสตริงหรือหลังขีดล่าง - กลุ่มแรก
([a-z])ตัวอักษรตัวพิมพ์เล็กเดี่ยว - กลุ่มที่สอง
โดย
\U\2พิมพ์ตัวพิมพ์ใหญ่กลุ่มที่สอง
gทั่วโลก


4
หมายเหตุ: \Uเป็นส่วนขยาย GNU เป็น POSIX
Ciro Santilli 新疆改造中心法轮功六四事件

1
sed -r 's/(^|[-_ ]+)([0-9a-z])/\U\2/g'เพียงแค่ทราบคุณควรจับหมายเลขเกินไป ดังนั้นสตริงเช่น"this_is_2nd_string"ก็ทำงานเช่นกัน
pinkeen

9

เนื่องจากคุณใช้อยู่bashหากคุณเก็บสตริงไว้ในตัวแปรคุณสามารถทำได้โดยใช้เชลล์เท่านั้น:

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

${uscore//_/ }แทนที่ทั้งหมด_ด้วยช่องว่าง(....)แยกสตริงเป็นอาร์เรย์${arr[@]^}แปลงอักษรตัวแรกของแต่ละองค์ประกอบเป็นตัวprintf %s ..พิมพ์ใหญ่แล้วพิมพ์องค์ประกอบทั้งหมดทีละตัว
คุณสามารถจัดเก็บสตริงอูฐใส่ในตัวแปรอื่น:

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

และใช้ / นำมาใช้ซ้ำในภายหลังเช่น:

printf %s\\n $ccase
ThisIsTheStringToBeConverted

หรือด้วยzsh:

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

(${(s:_:)uscore})แยกสตริง_ให้เป็นอาร์เรย์(C)ใช้อักษรตัวแรกของแต่ละองค์ประกอบและprintf %s ...พิมพ์องค์ประกอบทั้งหมดทีละตัว
เพื่อเก็บไว้ในตัวแปรอื่นคุณสามารถใช้(j::)เพื่อรวมองค์ประกอบ:

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

และใช้ / นำมาใช้ซ้ำในภายหลัง:

printf %s\\n $ccase
ThisIsTheStringToBeConverted

8

นี่เป็นวิธี Perl:

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

มันสามารถจัดการกับสายยาวโดยพลการ:

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

มันจะตรงกับตัวละครใด ๆ ( .) ที่มาหลังจากการเริ่มต้นของสตริงหรือขีดล่าง ( (^|_)) และแทนที่ด้วยรุ่นตัวพิมพ์ใหญ่ของตัวเอง ( uc($&)) $&เป็นตัวแปรพิเศษที่มีสิ่งที่ถูกจับคู่เพียง eในตอนท้ายของs///geช่วยให้การใช้สำนวนนี้ ( uc()ฟังก์ชั่นในกรณีนี้) ภายในทดแทนและgทำให้มันเปลี่ยนทั้งหมดที่เกิดขึ้นในสาย การทดแทนที่สองจะเอาขีดล่างออก


การพูดของ Perl ยังมีโมดูล Perl :: CamelCaseที่ "camelizes" ข้อความขีดเส้นใต้
don_crissti

@don_crissti ooh ฟังดูสมบูรณ์แบบสำหรับสิ่งนี้ ขอบคุณ
terdon

สั้น Perl:perl -pe 's/(^|_)([a-z])/uc($2)/ge'
ไอแซค

6

ไม่จำเป็นต้องแสดงสตริงทั้งหมดในการจับคู่นิพจน์ทั่วไป - sed มี/gตัวปรับแต่งที่อนุญาตให้คุณเดินผ่านการแข่งขันหลายรายการและแทนที่แต่ละรายการ:

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

regex แรกคือ_\([a-z]\)- ตัวอักษรแต่ละตัวหลังขีดล่าง; ตัวที่สองที่ตรงกับตัวอักษรตัวแรกในสตริง


3

ฉันใส่คำตอบนี้เพียงเพราะมันสั้นและง่ายกว่าคำตอบอื่น ๆ

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

มันบอกว่า: upcase ตัวละครดังต่อไปนี้_หรือการเริ่มต้น ตัวอักษรที่ไม่ใช่จะไม่สามารถเปลี่ยนแปลงได้เนื่องจากไม่มีกรณี


1
"ทุกสิ่งควรทำอย่างง่ายที่สุด แต่ไม่ง่ายกว่า" - Albert Einstein. นี่ไม่เท่ากับคำตอบอื่น ๆ คำตอบของคุณจะแปลง "FOO_BAR" เป็น "FOOBAR" ในขณะที่คำตอบอื่น ๆ จะปล่อยให้อยู่คนเดียว
สกอตต์

@ กอตต์อาใช่ฉันไม่ได้คิดอย่างนั้น
ctrl-alt-delor

1
@Scott นั่นไม่ใช่พฤติกรรมที่ต้องการใช่ไหม ฉันเดาว่ามันควรจะเป็นFooBarแต่ขีดเส้นใต้ควรถูกลบตามคำแนะนำ เท่าที่ฉันเข้าใจคำแนะนำต่อไป
terdon

2
(ต่อ) ... (3) ฉันคิดว่ามันค่อนข้างชัดเจนว่าวิญญาณของคำถามคือการแปลงสตริงเพื่อให้การแบ่งคำที่ระบุโดยขีดล่าง ( _) แทนการเปลี่ยนเคส ระบุว่า“ FOO_BAR” →“ FOOBAR” เป็นสิ่งที่ผิดอย่างชัดเจน (เพราะมันเป็นการทิ้งข้อมูลการแบ่งคำ) แม้ว่า“ FOO_BAR” →“ FooBar” อาจถูกต้อง (4) ในทำนองเดียวกันการทำแผนที่ที่ทำให้เกิดการชนดูเหมือนจะตรงกันข้ามกับวิญญาณของคำถาม ตัวอย่างเช่นฉันเชื่อว่าคำตอบที่แปลง“ DO_SPORTS” และ“ DOS_PORTS” เป็นเป้าหมายเดียวกันนั้นผิด
สกอตต์

1
(ต่อไปอีกครั้ง) ... (5) ด้วยจิตวิญญาณที่ไม่ทำให้เกิดการชนดูเหมือนว่า“ foo_bar” และ“ FOO_BAR” ไม่ควรแมปกับสิ่งเดียวกันดังนั้นฉันจึงคัดค้าน“ FOO_BAR” →“ FooBar” . (6) ฉันคิดว่าปัญหาที่ใหญ่กว่าคือเนมสเปซ ฉันยังไม่ได้ตั้งโปรแกรมใน Pascal ตั้งแต่ Blaise ยังมีชีวิตอยู่ แต่ใน C / C ++ โดยการประชุมตัวบ่งชี้ที่เป็นตัวพิมพ์เล็ก (รวมถึง snake_case และ CamelCase) โดยทั่วไปจะเป็นโดเมนของคอมไพเลอร์ในขณะที่ตัวระบุในกรณีส่วนใหญ่ โดเมนของตัวประมวลผลล่วงหน้า นั่นเป็นเหตุผลที่ฉันคิดว่า OP ไม่ต้องการให้ตัวระบุ ALL_CAPS พิจารณา
สกอตต์

1

ใน Perl:

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

นี่คือ i18n สามารถ:

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.