Преобразование подчеркивания в PascalCase, т.е. UpperCamelCase

28

Если у меня есть строка, которая выглядит так:

"this_is_the_string"

Внутри bash-скрипта я хотел бы преобразовать его в PascalCase, т.е. UpperCamelCase, чтобы он выглядел так:

"ThisIsTheString"

Я обнаружил, что преобразование в lowerCamelCase может быть сделано следующим образом:

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

К сожалению, я недостаточно знаком с регулярными выражениями, чтобы изменить это.

user1135541
источник
(1) Это не имеет большого значения, поскольку этот вопрос (и ответы, представленные до сих пор) касаются этого, но, к вашему сведению, \U\2вставляет найденный текст из второй группы, преобразованный во ВСЕ КАПСЫ. Сравните с \u\2, который вставляет текст в случае предложения, только с заглавной буквы. (2) Все приведенные ниже примеры переведут «this_is_a_string» в «ThisIsAString» - это то, что вы просили, но его немного сложно прочитать. Возможно, вы захотите пересмотреть ваши требования для особого случая однобуквенного слова (подстрока). … (Продолжение)
Скотт
(Продолжение)… (3) У вас есть только одна такая строка в строке? И всегда ли это первый (или единственный ) текст в строке? Если у вас есть строка, которая не находится в начале строки, приведенные ниже ответы преобразуют ее в lowerCamelCase. Чтобы исправить, примите ответ Яниса и измените (^|_)на (\<|_).
Скотт
1
обратное: stackoverflow.com/questions/28795479/…
Сиро Сантилли 新疆 改造 中心 法轮功 六四 事件

Ответы:

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

Замените шаблон
(^|_)в начале строки или после подчеркивания - первая группа,
([a-z])одна строчная буква - вторая группа
,
\U\2заглавная вторая группа в
gглобальном масштабе.

Janis
источник
4
Примечание: \Uэто расширение GNU для POSIX.
Сиро Сантилли 新疆 改造 中心 法轮功 六四 事件
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
don_crissti
источник
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 String :: CamelCase, который «верблюжит» подчеркнутый текст.
don_crissti
@don_crissti ооо, звучит идеально для этого. Спасибо.
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'

Первое регулярное выражение _\([a-z]\)- каждая буква после подчеркивания; второй соответствует первой букве в строке.

myaut
источник
3

Я только вставил этот ответ, потому что он короче и проще, чем любой другой.

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

Там написано: upcase, символ, следующий за a _или начало. Номера букв не будут изменены, так как они не имеют регистра.

Ctrl-Alt-Делор
источник
1
«Все должно быть сделано как можно проще, но не проще». - Альберт Эйнштейн. Это не эквивалентно другим ответам; ваш ответ преобразует "FOO_BAR" в "FOOBAR", в то время как другие ответы оставят его в покое.
Скотт
@ Scott Ах да, я не думал об этом.
Ctrl-Alt-Delor
1
@ Скотт Разве это не желаемое поведение? Я думаю, что в идеале это должно стать, FooBarно подчеркивание должно быть удалено в соответствии с инструкциями. Как я понимаю инструкции в любом случае.
Тердон
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'
АлертБеерКореХемп
mosvy
источник
0

Я сделал это так:

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

и получил этот результат:

ThisIsTheString
Фабио Роберто Теодоро
источник