将下划线转换为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/'

不幸的是,我对正则表达式不够熟悉,无法对其进行修改。


(1)就此问题(和到目前为止给出的答案)而言,这并不重要,但是,仅供参考,\U\2将第二组中找到的文本插入,转换为ALL CAPS。与相比\u\2,在句子大小写的情况下插入文本,只有第一个字符大写。(2)下面给出的所有示例都将“ this_is_a_string”转换为“ ThisIsAString”,这是您所要求的,但是有点难以理解。您可能需要修改对一个字母组成的单词(子字符串)的特殊情况的要求。…(续)
斯科特

(续)…(3)每行只有一个这样的字符串吗?它总是行中的第一个(或唯一一个)文本吗?如果您的字符串不在行首,则以下答案将其转换为lowerCamelCase。要解决此问题,请使用Janis的答案并将其更改(^|_)(\<|_)
斯科特

Answers:


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


(^|_)字符串开头或下划线后的替换模式-第一组
([a-z])单个小写字母-第二组
通过将
\U\2第二组
g全局大写。


4
注意:\U是POSIX的GNU扩展。
Ciro Santilli新疆改造中心法轮功六四事件

1
仅需注意,您也应该捕获数字sed -r 's/(^|[-_ ]+)([0-9a-z])/\U\2/g'。因此像“ this_is_2nd_string”这样的字符串也可以工作。
pinkeen

9

由于您正在使用bash,如果您将字符串存储在变量中,则也可以仅使用shell进行操作:

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

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 2015年

@don_crissti ooh,听起来很完美。谢谢。
terdon

更短的Perl:perl -pe 's/(^|_)([a-z])/uc($2)/ge'
Isaac

6

不必在正则表达式匹配项中表示整个字符串-sed具有/g修饰符,该修饰符使您可以遍历多个匹配项并替换每个匹配项:

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

第一个正则表达式是_\([a-z]\)-下划线后的每个字母;第二个匹配字符串中的第一个字母。


3

我之所以只提供这个答案,是因为它比迄今为止的任何一个都更短,更简单。

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

它说:大写,a后面的字符_或开始。非字母不会更改,因为它们没有大小写。


1
“一切都应该尽可能简单,但不要简单。” - 艾尔伯特爱因斯坦。这不等于其他答案。您的答案会将“ FOO_BAR”转换为“ FOOBAR”,而其他答案将不理会。
斯科特

@scott啊,是的,我没有想到。
ctrl-alt-delor

1
@Scott这不是理想的行为吗?我认为理想情况下应该变为,FooBar但应根据说明删除下划线。据我了解的指示。
terdon

2
(续)…(3)我认为问题的实质是转换字符串,以便用下划线(_)表示的单词中断由大小写转换表示,这一点很明显。鉴于此,尽管“ FOO_BAR”→“ FooBar”可能是正确的,但“ FOO_BAR”→“ FOOBAR”显然是错误的(因为它会丢弃分词信息)。(4)同样,导致冲突的映射似乎与问题的精神背道而驰。例如,我认为将“ DO_SPORTS”和“ DOS_PORTS”转换为相同目标的答案是错误的。
斯科特

1
(再次续)…(5)本着不引起碰撞的精神,“ foo_bar”和“ FOO_BAR”似乎不应该映射到同一事物,因此我反对“ FOO_BAR”→“ FooBar” 。(6)我认为更大的问题是名称空间。自从Blaise活着以来,我还没有在Pascal中编程,但是按照惯例,在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-able:

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.