Convierta el guión bajo a PascalCase, es decir, UpperCamelCase

28

Si tengo una cadena que se ve así:

"this_is_the_string"

Dentro de un script bash, me gustaría convertirlo a PascalCase, es decir, UpperCamelCase para que se vea así:

"ThisIsTheString"

Descubrí que la conversión a lowerCamelCase se puede hacer así:

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

Lamentablemente, no estoy lo suficientemente familiarizado con las expresiones regulares para modificar esto.

usuario1135541
fuente
(1) Esto realmente no importa, en lo que respecta a esta pregunta (y las respuestas presentadas hasta ahora), pero, para su información, \U\2inserta el texto encontrado del segundo grupo, convertido a TODAS LAS MAYÚSCULAS. Compare con \u\2, que inserta el texto en el caso Sentencia, con solo el primer carácter en mayúscula. (2) Todos los ejemplos que figuran a continuación traducirán "this_is_a_string" a "ThisIsAString", que es lo que solicitó, pero es un poco difícil de leer. Es posible que desee revisar sus requisitos para el caso especial de una palabra de una letra (subcadena). ... (Continúa)
Scott
(Continúa) ... (3) ¿Tiene solo una cadena de este tipo por línea? ¿Y es siempre el primer (o el único ) texto en la línea? Si tiene una cadena que no está al comienzo de la línea, las respuestas a continuación la convertirán en lowerCamelCase. Para solucionarlo, tome la respuesta de Janis y cambie (^|_)a (\<|_).
Scott
1
inverso: stackoverflow.com/questions/28795479/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Respuestas:

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

Sustituya el patrón
(^|_)al comienzo de la cadena o después de un guión bajo - primer grupo
([a-z])letra minúscula individual - segundo grupo
en
\U\2mayúscula segundo grupo
gglobalmente.

Janis
fuente
44
Nota: \Ues una extensión de GNU para POSIX.
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
1
Solo una nota, también debes capturar números sed -r 's/(^|[-_ ]+)([0-9a-z])/\U\2/g'. Así que cadenas como "this_is_2nd_string" también funcionan.
pinkeen
9

Como está utilizando bash, si almacenó su cadena en una variable, también podría hacerlo solo con shell:

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

${uscore//_/ }reemplaza todo _con espacio, (....)divide la cadena en una matriz, ${arr[@]^}convierte la primera letra de cada elemento en mayúsculas y luego printf %s ..imprime todos los elementos uno tras otro.
Puede almacenar la cadena en camello en otra variable:

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

y usarlo / reutilizarlo más tarde, por ejemplo:

printf %s\\n $ccase
ThisIsTheStringToBeConverted

O con zsh:

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

(${(s:_:)uscore})divide la cadena _en una matriz, (C)escribe en mayúscula la primera letra de cada elemento e printf %s ...imprime todos los elementos uno tras otro.
Para almacenarlo en otra variable que podría usar (j::)para unir los elementos:

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

y úsalo / reutilízalo más tarde:

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

Aquí hay una manera de Perl:

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

Puede tratar cadenas de longitud arbitraria:

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

Coincidirá con cualquier carácter ( .) que viene después del inicio de la cadena o un guión bajo ( (^|_)) y lo reemplazará con la versión en mayúscula de sí mismo ( uc($&)). La $&es una variable especial que contiene lo que se acaba de comparar. Al efinal de s///gepermite el uso de expresiones (la uc()función en este caso) dentro de la sustitución y ghace que reemplace todas las ocurrencias en la línea. La segunda sustitución elimina los guiones bajos.

terdon
fuente
Hablando de perl, también hay un módulo de perl String :: CamelCase que "cameliza" el texto subrayado.
don_crissti
@don_crissti ooh, suena perfecto para esto. Gracias.
terdon
Más corto Perl:perl -pe 's/(^|_)([a-z])/uc($2)/ge'
Isaac
6

No es necesario representar la cadena completa en una coincidencia de expresión regular: sed tiene el /gmodificador que le permite caminar sobre varias coincidencias y reemplazar cada una de ellas:

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

La primera expresión regular es _\([a-z]\): cada letra después del guión bajo; la segunda coincide con la primera letra de una cadena.

myaut
fuente
3

Solo pongo esta respuesta porque es más corta y más simple que cualquier otra hasta ahora.

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

Dice: en mayúsculas, el personaje que sigue a _o el comienzo. No se cambiarán las letras, ya que no tienen caso.

ctrl-alt-delor
fuente
1
"Todo debe hacerse lo más simple posible, pero no más simple". - Albert Einstein. Esto no es equivalente a las otras respuestas; su respuesta convertirá "FOO_BAR" en "FOOBAR", mientras que las otras respuestas lo dejarán en paz.
Scott
@ Scott Ah, sí, no pensé en eso.
ctrl-alt-delor
1
@ Scott ¿No es ese el comportamiento deseado? Supongo que, idealmente, debería convertirse, FooBarpero el guión bajo debería eliminarse según las instrucciones. Como entiendo las instrucciones de todos modos.
terdon
2
(Cont.) ... (3) Creo que está algo claro que el espíritu de la pregunta es transformar una cadena para que los saltos de palabras indicados por guiones bajos ( _) se indiquen en cambio por las transiciones de mayúsculas y minúsculas. Dado que, "FOO_BAR" → "FOOBAR" está claramente equivocado (ya que descarta la información de salto de palabra), aunque "FOO_BAR" → "FooBar" puede ser correcto. (4) Del mismo modo, un mapeo que causa colisiones parece ser contrario al espíritu de la pregunta. Por ejemplo, creo que una respuesta que convierte "DO_SPORTS" y "DOS_PORTS" al mismo objetivo es incorrecta.
Scott
1
(Continúa de nuevo) ... (5) En el espíritu de no causar colisiones, me parece que "foo_bar" y "FOO_BAR" no deberían asignarse a la misma cosa, por lo que me opongo a "FOO_BAR" → "FooBar" . (6) Creo que el problema más grande son los espacios de nombres. No he programado en Pascal desde que Blaise estaba vivo, pero en C / C ++, por convención, los identificadores que están principalmente en minúsculas (para incluir snake_case y CamelCase) son generalmente el dominio del compilador, mientras que los identificadores en mayúsculas son dominio del preprocesador. Por eso creo que el OP no quería que se consideraran los identificadores ALL_CAPS.
Scott
1

En perl:

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

Esto también es compatible con i18n:

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

Lo hice de esta manera:

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

y obtuve este resultado:

ThisIsTheString
Fábio Roberto Teodoro
fuente