Konwertuj znak podkreślenia na PascalCase, tj. UpperCamelCase

28

Jeśli mam ciąg, który wygląda tak:

"this_is_the_string"

Wewnątrz skryptu bash chciałbym przekonwertować go na PascalCase, tj. UpperCamelCase, aby wyglądał następująco:

"ThisIsTheString"

Przekonałem się, że konwersję do lowerCamelCase można wykonać w następujący sposób:

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

Niestety nie znam wystarczająco dobrze wyrażeń regularnych, aby to zmienić.

użytkownik1135541
źródło
(1) Nie ma to większego znaczenia, jeśli chodzi o to pytanie (i przedstawione dotychczas odpowiedzi), ale FYI \U\2wstawia znaleziony tekst z drugiej grupy, przekonwertowany na WSZYSTKIE CAPS. Porównaj z \u\2, która wstawia tekst w przypadku Zdania, z tylko pierwszym znakiem pisanym wielkimi literami. (2) Wszystkie poniższe przykłady przetłumaczą „this_is_a_string” na „ThisIsAString” - o to prosiłeś, ale jest nieco trudny do odczytania. Możesz zrewidować swoje wymagania dotyczące specjalnego przypadku pojedynczego słowa (podłańcucha). … (Ciąg dalszy)
Scott,
(Ciąg dalszy)… (3) Czy masz tylko jeden taki ciąg w wierszu? I czy zawsze jest to pierwszy (lub jedyny ) tekst w wierszu? Jeśli masz ciąg znaków, który nie znajduje się na początku wiersza, poniższe odpowiedzi przekonwertują go na lowerCamelCase. Aby to naprawić, weź odpowiedź Janis i zmień (^|_)na (\<|_).
Scott
1
odwrotność: stackoverflow.com/questions/28795479/...
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Odpowiedzi:

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

Zamień wzór
(^|_)na początku łańcucha lub po podkreśleniu - pierwsza grupa
([a-z])mała mała litera - druga grupa
przez
\U\2wielkie litery drugiej grupy
gglobalnie.

Janis
źródło
4
Uwaga: \Ujest rozszerzeniem GNU do POSIX.
Ciro Santilli 19 改造 中心 法轮功 六四 事件
1
Tylko uwaga, powinieneś również przechwytywać liczby sed -r 's/(^|[-_ ]+)([0-9a-z])/\U\2/g'. Więc łańcuchy takie jak „this_is_2nd_string” też działają.
pinkeen
9

Ponieważ używasz bash, jeśli zapisałeś ciąg w zmiennej, możesz to zrobić tylko w powłoce:

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

${uscore//_/ }zastępuje wszystko _spacją, (....)dzieli ciąg na tablicę, ${arr[@]^}konwertuje pierwszą literę każdego elementu na wielkie litery, a następnie printf %s ..drukuje wszystkie elementy jeden po drugim.
Możesz przechowywać ciąg wielbłąda w innej zmiennej:

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

i użyj / użyj go później, np .:

printf %s\\n $ccase
ThisIsTheStringToBeConverted

Lub z zsh:

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

(${(s:_:)uscore})dzieli ciąg na _tablicę, drukuje (C)wielką literę każdego elementu i printf %s ...drukuje wszystkie elementy jeden po drugim ..
Aby zapisać go w innej zmiennej, można użyć (j::)do łączenia elementów:

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

i użyj / użyj go później:

printf %s\\n $ccase
ThisIsTheStringToBeConverted
don_crissti
źródło
8

Oto sposób na Perla:

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

Może radzić sobie z ciągami o dowolnej długości:

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

Będzie pasować do dowolnego znaku ( .), który pojawia się po początku łańcucha lub znaku podkreślenia ( (^|_)) i zastąpi go samą wersją wielkich liter ( uc($&)). $&Jest specjalną zmienną, która zawiera co właśnie dopasowane. Na ekońcu s///gezezwala na użycie wyrażeń ( uc()w tym przypadku funkcji) w ramach podstawienia i gpowoduje, że zastępuje ono wszystkie wystąpienia w wierszu. Drugie podstawienie usuwa podkreślenia.

terdon
źródło
Mówiąc o perlu, istnieje również moduł perla String :: CamelCase, który „camelizes” podkreślony tekst.
don_crissti
@don_crissti ooh, brzmi idealnie do tego. Dzięki.
terdon
Krótszy Perl:perl -pe 's/(^|_)([a-z])/uc($2)/ge'
Izaak
6

Nie jest konieczne reprezentowanie całego łańcucha w dopasowaniu wyrażenia regularnego - sed ma /gmodyfikator, który pozwala na przejście wielu dopasowań i zastąpienie każdego z nich:

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

Pierwszym wyrażeniem regularnym jest _\([a-z]\)- każda litera po podkreśleniu; drugi pasuje do pierwszej litery w ciągu.

myaut
źródło
3

Podaję tylko tę odpowiedź, ponieważ jest ona krótsza i prostsza niż jakakolwiek inna do tej pory.

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

Mówi: upcase, znak następujący po a _lub na początku. Nie litery nie zostaną zmienione, ponieważ nie mają wielkości liter.

ctrl-alt-delor
źródło
1
„Wszystko powinno być tak proste, jak to możliwe, ale nie prostsze”. - Albert Einstein. Nie jest to równoważne z innymi odpowiedziami; twoja odpowiedź zamieni „FOO_BAR” na „FOOBAR”, podczas gdy inne odpowiedzi pozostawią ją samą.
Scott
@ scott Ach tak, nie myślałem o tym.
ctrl-alt-delor
1
@Scott Czy to nie jest pożądane zachowanie? Myślę, że idealnie, powinno być, FooBarale podkreślenie powinno zostać usunięte zgodnie z instrukcjami. W każdym razie rozumiem instrukcje.
terdon
2
(Ciąg dalszy)… (3) Myślę, że jest dość jasne, że duchem pytania jest przekształcenie łańcucha, tak aby podziały wyrazów wskazane przez podkreślniki ( _) były zamiast tego wskazywane przez przejścia wielkości liter. Biorąc pod uwagę, że „FOO_BAR” → „FOOBAR” jest wyraźnie niepoprawny (ponieważ odrzuca informacje o podziale słów), chociaż „FOO_BAR” → „FooBar” może być poprawny. (4) Podobnie mapowanie, które powoduje kolizje, wydaje się być sprzeczne z duchem pytania. Na przykład uważam, że odpowiedź, która konwertuje „DO_SPORTS” i „DOS_PORTS” na ten sam cel, jest błędna.
Scott
1
(Ciąg dalszy)… (5) W duchu nie powodowania kolizji wydaje mi się, że „foo_bar” i „FOO_BAR” nie powinny być odwzorowane na to samo, dlatego sprzeciwiam się „FOO_BAR” → „FooBar” . (6) Myślę, że większym problemem są przestrzenie nazw. Nie programowałem w Pascalu odkąd Blaise żył, ale w C / C ++, zgodnie z konwencją, identyfikatory, które są przede wszystkim pisane małymi literami (w tym snake_case i CamelCase) są ogólnie domeną kompilatora, podczas gdy identyfikatory pisane wielkimi literami to domena procesora wstępnego. Dlatego myślę, że OP nie chciał, aby identyfikatory ALL_CAPS były brane pod uwagę.
Scott,
1

W perlu:

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

Jest to również w stanie obsługiwać i18n:

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

Zrobiłem to w ten sposób:

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

i uzyskałem ten wynik:

ThisIsTheString
Fábio Roberto Teodoro
źródło