Converta sublinhado em PascalCase, ou seja, UpperCamelCase

28

Se eu tiver uma string que se parece com isso:

"this_is_the_string"

Dentro de um script bash, eu gostaria de convertê-lo para PascalCase, ou seja, UpperCamelCase para ficar assim:

"ThisIsTheString"

Descobri que a conversão para lowerCamelCase pode ser feita assim:

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

Infelizmente, eu não estou familiarizado o suficiente com expressões regulares para modificar isso.

user1135541
fonte
(1) Isso realmente não importa, no que diz respeito a esta pergunta (e às respostas apresentadas até agora), mas, para sua informação, \U\2insere o texto encontrado do segundo grupo, convertido em ALL CAPS. Compare com \u\2, que insere o texto em maiúsculas e minúsculas, com apenas o primeiro caractere em maiúscula. (2) Todos os exemplos fornecidos abaixo traduzirão “this_is_a_string” para “ThisIsAString” - que é o que você solicitou, mas é um pouco difícil de ler. Você pode revisar seus requisitos para o caso especial de uma palavra de uma letra (substring). ... (continua)
Scott
(Continua)… (3) Você tem apenas uma dessas strings por linha? E é sempre o primeiro (ou o único ) texto da linha? Se você tiver uma string que não esteja no início da linha, as respostas abaixo a converterão em lowerCamelCase. Para corrigir, pegue a resposta de Janis e mude (^|_)para (\<|_).
Scott
11
Inverse: stackoverflow.com/questions/28795479/…
Ciro Santilli deve ser o primeiro de

Respostas:

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

Substitua o padrão
(^|_)no início da sequência ou depois de um sublinhado - primeiro grupo em
([a-z])letra minúscula única - segundo grupo colocando o segundo grupo
em
\U\2maiúsculas
gglobalmente.

Janis
fonte
4
Nota: \Ué uma extensão GNU para POSIX.
Ciro Santilli)
11
Apenas uma nota, você deve capturar números também sed -r 's/(^|[-_ ]+)([0-9a-z])/\U\2/g'. Então, strings como "this_is_2nd_string" também funcionam.
pinkeen 01/07
9

Desde que você está usando bash, se você armazenou sua string em uma variável, também pode fazê-lo apenas com shell:

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

${uscore//_/ }substitui tudo _pelo espaço, (....)divide a string em uma matriz, ${arr[@]^}converte a primeira letra de cada elemento em maiúscula e depois printf %s ..imprime todos os elementos um após o outro.
Você pode armazenar a sequência com camelo em outra variável:

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

e use / reutilize mais tarde, por exemplo:

printf %s\\n $ccase
ThisIsTheStringToBeConverted

Ou, com zsh:

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

(${(s:_:)uscore})divide a string _em uma matriz, coloca em (C)maiúscula a primeira letra de cada elemento e printf %s ...imprime todos os elementos um após o outro.
Para armazená-lo em outra variável, você pode usar (j::)para unir os elementos:

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

e use / reutilize mais tarde:

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

Aqui está uma maneira Perl:

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

Pode lidar com cadeias de comprimento arbitrário:

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

Ele corresponderá a qualquer caractere ( .) que vem após o início da string ou a um sublinhado ( (^|_)) e o substituirá pela versão em maiúscula de si mesmo ( uc($&)). O $&é uma variável especial que contém o que foi correspondido. O eno final de s///gepermite o uso de expressões (a uc()função neste caso) dentro da substituição e o gfaz substituir todas as ocorrências na linha. A segunda substituição remove os sublinhados.

terdon
fonte
Falando em perl, também existe um módulo perl String :: CamelCase que "cameliza" o texto sublinhado.
don_crissti
@don_crissti ooh, parece perfeito para isso. Obrigado.
terdon
Perl mais curto:perl -pe 's/(^|_)([a-z])/uc($2)/ge'
Isaac
6

Não é necessário representar a string inteira em uma correspondência de expressão regular - sed possui o /gmodificador que permite percorrer várias correspondências e substituir cada uma delas:

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

O primeiro regex é _\([a-z]\)- cada letra após sublinhado; o segundo corresponde à primeira letra de uma string.

myaut
fonte
3

Eu apenas coloquei nesta resposta porque é mais curta e mais simples do que qualquer outra até agora.

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

Diz: maiúscula, o personagem após um _ou o início. As letras que não sejam não serão alteradas, pois não têm maiúsculas.

ctrl-alt-delor
fonte
11
"Tudo deve ser feito o mais simples possível, mas não mais simples." - Albert Einstein. Isso não é equivalente às outras respostas; sua resposta converterá "FOO_BAR" para "FOOBAR", enquanto as outras respostas o deixarão em paz.
Scott Scott
@ Scott Ah, sim, eu não pensei nisso.
ctrl-alt-Delor
11
@ Scott Não é esse o comportamento desejado? Eu acho que, idealmente, ele deve se tornar, FooBarmas o sublinhado deve ser removido conforme as instruções. Pelo que entendi as instruções de qualquer maneira.
terdon
2
(Continua) ... (3) Eu acho que é um pouco claro que o espírito da pergunta é transformar uma string para que as quebras de palavras indicadas por sublinhados ( _) sejam indicadas por transições de maiúsculas e minúsculas. Dado que, “FOO_BAR” → “FOOBAR” está claramente errado (pois descarta as informações de quebra de palavra), embora “FOO_BAR” → “FooBar” possa estar correto. (4) Da mesma forma, um mapeamento que causa colisões parece ser contrário ao espírito da questão. Por exemplo, acredito que uma resposta que converte "DO_SPORTS" e "DOS_PORTS" para o mesmo destino está errada.
Scott Scott
11
(Continua novamente)… (5) No espírito de não causar colisões, parece-me que “foo_bar” e “FOO_BAR” não devem ser mapeados para a mesma coisa; portanto, eu me oponho a “FOO_BAR” → “FooBar” . (6) Acho que o problema maior são os espaços para nome. Não programo em Pascal desde que Blaise estava vivo, mas em C / C ++, por convenção, os identificadores que estão principalmente em letras minúsculas (para incluir snake_case e CamelCase) geralmente são o domínio do compilador, enquanto os identificadores em letras maiúsculas são os domínio do pré-processador. É por isso que acho que o OP não queria que os identificadores ALL_CAPS fossem considerados.
Scott
1

Em perl:

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

Isso também é capaz de i18n:

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

Eu fiz assim:

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

e obteve este resultado:

ThisIsTheString
Fábio Roberto Teodoro
fonte