Convertir le trait de soulignement en PascalCase, c'est-à-dire UpperCamelCase

28

Si j'ai une chaîne qui ressemble à ceci:

"this_is_the_string"

Dans un script bash, je voudrais le convertir en PascalCase, c'est-à-dire UpperCamelCase pour ressembler à ceci:

"ThisIsTheString"

J'ai trouvé que la conversion vers lowerCamelCase peut se faire comme ceci:

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

Malheureusement, je ne connais pas assez les expressions rationnelles pour modifier cela.

user1135541
la source
(1) Cela n'a pas vraiment d'importance, en ce qui concerne cette question (et les réponses présentées jusqu'à présent), mais, pour info, \U\2insère le texte trouvé du deuxième groupe, converti en TOUTES MAJUSCULES. Comparer à \u\2, qui insère le texte en cas de phrase, avec uniquement le premier caractère en majuscule. (2) Tous les exemples ci-dessous traduiront «this_is_a_string» en «ThisIsAString» - c'est ce que vous avez demandé, mais il est légèrement difficile à lire. Vous voudrez peut-être réviser vos exigences pour le cas spécial d'un mot à une lettre (sous-chaîne). … (Suite)
Scott
(Suite)… (3) Avez-vous une seule chaîne de ce type par ligne? Et est-ce toujours le premier (ou le seul ) texte sur la ligne? Si vous avez une chaîne qui n'est pas au début de la ligne, les réponses ci-dessous la convertiront en boîtierCamelCase inférieur. Pour corriger, prenez la réponse de Janis et passez (^|_)à (\<|_).
Scott
1
inverse: stackoverflow.com/questions/28795479/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Réponses:

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

Remplacez le motif
(^|_)au début de la chaîne ou après un trait de soulignement - premier groupe
([a-z]), lettre minuscule unique - deuxième groupe
en
\U\2mettant le deuxième groupe en majuscule
g.

Janis
la source
4
Remarque: \Uest une extension GNU de POSIX.
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
1
Juste une note, vous devez également capturer des nombres sed -r 's/(^|[-_ ]+)([0-9a-z])/\U\2/g'. Les chaînes comme "this_is_2nd_string" fonctionnent également.
pinkeen
9

Puisque vous utilisez bash, si vous avez stocké votre chaîne dans une variable, vous pouvez également le faire uniquement en shell:

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

${uscore//_/ }remplace tout _par un espace, (....)divise la chaîne en un tableau, ${arr[@]^}convertit la première lettre de chaque élément en majuscule, puis printf %s ..imprime tous les éléments les uns après les autres.
Vous pouvez stocker la chaîne en chameau dans une autre variable:

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

et l'utiliser / réutiliser plus tard, par exemple:

printf %s\\n $ccase
ThisIsTheStringToBeConverted

Ou, avec zsh:

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

(${(s:_:)uscore})divise la chaîne _en un tableau, met en (C)majuscule la première lettre de chaque élément et printf %s ...imprime tous les éléments les uns après les autres.
Pour la stocker dans une autre variable, vous pouvez utiliser (j::)pour joindre les éléments:

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

et l'utiliser / réutiliser plus tard:

printf %s\\n $ccase
ThisIsTheStringToBeConverted
don_crissti
la source
8

Voici un moyen Perl:

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

Il peut traiter des chaînes de longueur arbitraire:

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

Il correspondra à tout caractère ( .) qui vient après le début de la chaîne ou un trait de soulignement ( (^|_)) et le remplacera par la version majuscule de lui-même ( uc($&)). La $&est une variable spéciale qui contient tout ce qui vient d'être mis en correspondance. Le eà la fin de s///gepermet l'utilisation d'expressions (la uc()fonction dans ce cas) dans la substitution et le gfait remplacer toutes les occurrences de la ligne. La deuxième substitution supprime les traits de soulignement.

terdon
la source
En parlant de perl, il y a aussi un module perl String :: CamelCase qui "camélise" le texte souligné.
don_crissti
@don_crissti ooh, semble parfait pour cela. Merci.
terdon
Perl plus court:perl -pe 's/(^|_)([a-z])/uc($2)/ge'
Isaac
6

Il n'est pas nécessaire de représenter la chaîne entière dans une correspondance d'expression régulière - sed a le /gmodificateur qui vous permet de parcourir plusieurs correspondances et de remplacer chacune d'elles:

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

Le premier regex est _\([a-z]\)- chaque lettre après le trait de soulignement; la seconde correspond à la première lettre d'une chaîne.

myaut
la source
3

Je ne mets cette réponse que parce qu'elle est plus courte et plus simple que toute autre jusqu'à présent.

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

Il dit: upcase, le caractère suivant un _ou le début. Les lettres non ne seront pas modifiées, car elles n'ont pas de casse.

ctrl-alt-delor
la source
1
"Tout devrait être aussi simple que possible, mais pas plus simple." - Albert Einstein. Ce n'est pas équivalent aux autres réponses; votre réponse convertira "FOO_BAR" en "FOOBAR", tandis que les autres réponses le laisseront tranquille.
Scott
@scott Ah oui, je n'y ai pas pensé.
ctrl-alt-delor
1
@Scott N'est-ce pas le comportement souhaité? Je suppose que dans l'idéal, cela devrait devenir, FooBarmais le trait de soulignement devrait être supprimé conformément aux instructions. Si je comprends bien les instructions.
terdon
2
(Suite)… (3) Je pense qu'il est quelque peu clair que l'esprit de la question est de transformer une chaîne de sorte que les sauts de mots indiqués par des traits de soulignement ( _) soient plutôt indiqués par des transitions de casse. Étant donné que «FOO_BAR» → «FOOBAR» est clairement faux (car il supprime les informations de rupture de mot), bien que «FOO_BAR» → «FooBar» puisse être correct. (4) De même, une cartographie provoquant des collisions semble contraire à l'esprit de la question. Par exemple, je pense qu'une réponse qui convertit «DO_SPORTS» et «DOS_PORTS» en la même cible est fausse.
Scott
1
(Suite)… (5) Dans l'esprit de ne pas provoquer de collisions, il me semble que «foo_bar» et «FOO_BAR» ne devraient pas correspondre à la même chose, donc je m'oppose à «FOO_BAR» → «FooBar» . (6) Je pense que le plus gros problème concerne les espaces de noms. Je n'ai pas programmé en Pascal depuis que Blaise était vivant, mais en C / C ++, par convention, les identifiants qui sont principalement en minuscules (pour inclure snake_case et CamelCase) sont généralement le domaine du compilateur, tandis que les identifiants en majuscules sont les domaine du pré-processeur. C'est pourquoi je pense que l'OP ne voulait pas que les identifiants ALL_CAPS soient pris en compte.
Scott
1

En perl:

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

C'est également compatible i18n:

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

Je l'ai fait de cette façon:

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

et a obtenu ce résultat:

ThisIsTheString
Fábio Roberto Teodoro
la source