将字符串中的帕斯卡大小写更改为蛇大小写



我有以下文字:

SendNoticeMsg (api.post = "/test/SendNoticeMsg")
GenerateMsg (api.post = "/test/GenerateMsg")
GetUserLastAction (api.post = "/test/GetUserLastAction")

我想将文本更改为:

SendNoticeMsg (api.post = "/test/send_notice_msg")
GenerateMsg (api.post = "/test/generate_msg")
GetUserLastAction (api.post = "/test/get_user_last_action")

描述:我只想将 URL 路径更改为有效的下划线样式,因此解决方案不应更改任何其他不相关的字符。

我尝试使用 sed 脚本:

sed -E 's/(/test/.*)([A-Z]).*"/12_L/'

但它不起作用。

perl -wnE'
@p = m{(.*/)(.*)"};                       # break up into parts
@w = $p[-1] =~ /([A-Z][a-z0-9]*)/g;       # extract (PascalCase-ed) words
$p[-1] = join("_", map { lc } @w).q{")};  # low-case them, join with _ 
say @p
' input.txt

或者通过将开关更改为perl -i.bak -wnE'...'来"就地"覆盖输入

这假设单词,在首字母之后,只能有[a-z0-9];如果需要,可以调整。

另一个 perl 解决方案:

$ perl -pe '$i=0; s/(?=/test/)(S+)/$s=$1;$s=~s!([A-Z])!$i++?"_".lc($1):lc($1)!ge;$s/ge ' underscore2.txt
SendNoticeMsg (api.post = "/test/send_notice_msg")
GenerateMsg (api.post = "/test/generate_msg")
GetUserLastAction (api.post = "/test/get_user_last_action")
$

进一步增强。

$ perl -pe '$i=0; s/(?=/[^/]+/)(S+)/$s=$1;$s=~s!([A-Z])!$i++?"_".lc($1):lc($1)!ge;$s/ge ' underscore2.txt
SendNoticeMsg (api.post = "/test/send_notice_msg")
GenerateMsg (api.post = "/test/generate_msg")
GetUserLastAction (api.post = "/test/get_user_last_action")
$
perl -pe's{/test/K[^"]*}{ lc( $& =~ s/wK(?=p{Lu})/_/gr ) }e'

甚至

perl -pe's{/test/K[^"]*}{ lc( $& =~ s/B(?=p{Lu})/_/gr ) }e'

我们提取路径,并将其替换为由替换表达式构造的字符串。我们通过在适当的位置(在单词字符和大写字母之间)插入下划线来构造该字符串,然后将结果小写。

请参阅指定要处理的文件到 Perl 单行。

我将在组合中添加一个sed,通过使用#作为替代替换分隔符@tripleee答案略有不同,例如

sed -E ':a;s#^(.*/)([^A-Z]*)([A-Z])#1L2_3#;ta;s#/_#/#'

^(.*/)允许从每行的开头到最后'/'对所有字符进行贪婪匹配。

示例使用/输出

$ sed -E ':a;s#^(.*/)([^A-Z]*)([A-Z])#1L2_3#;ta;s#/_#/#' file
SendNoticeMsg (api.post = "/test/send_notice_msg")
GenerateMsg (api.post = "/test/generate_msg")
GetUserLastAction (api.post = "/test/get_user_last_action")

(注意:如果不使用 GNUsed则需要将每个';'替换为-e以标记单独的表达式)

如果由于某种原因您的sed不支持 ERE,那么像 BRE 这样的"纠察围栏"将是:

sed ':a;s#^(.*/)([^A-Z]*)([A-Z])#1L2_3#;ta;s#/_#/#'

下面是一个Perl单行:

perl -pe 'sub to_snake { my $v = $_[0]; $v =~ s/([a-z])([A-Z])/$1_$2/g; return lc $v; } s@(/test/)(w+)@$1.to_snake $2@e'

请注意,您可以在此处更改w以满足您的需求,但对于大多数目的来说应该足够了。

分解一下:

Perl 的-p参数读取输入并写入标准输出;-e允许在命令行上将程序指定为字符串。

这部分在每一行上执行替换;它使用e修饰符来执行代码:

s@(/test/)(w+)@$1.to_snake $2@e'

这部分定义了转换函数;它匹配大写字母前面的小写字母,在它们之间添加一个下划线,然后使用 Perl 的lc函数将所有内容映射到小写:

sub to_snake { my $v = $_[0]; $v =~ s/([a-z])([A-Z])/$1_$2/g; return lc $v; }

简单的sed脚本执行单个替换。您希望添加一个循环以使其匹配并根据需要多次替换。

sed -E -e ':a' -e 's/(/test/.*)([A-Z])(.*")/1_L23/' -e 'ta' -e 's%/_%/%'

:a将创建一个标签a,并在最新的替换成功时ta分支返回到该标签。(我盲目地假设脚本的其他部分对你来说是正确的;-E选项的可用性以及它是否为小写提供转义L绝不是通用的或可移植的,尽管我不得不重构它一点,并为/test/_whatever/添加修复。

似乎所有的答案都使用perl所以这里有一个使用awk

awk -F '"' '{
gsub(/([^/][A-Z])/," & ", $2);
n=split($2,c," ");
s=""
for (i=1; i<=n; i++) {
s = (i%2==1) ? (s c[i]) : (s substr(c[i],1,1) "_" substr(c[i], 2, 1));
}
$2=tolower(s);
print $1 """ $2 """ $3;
}' input.txt

它假设没有可用的捕获组或gensub,因此它用空格分隔以稍后插入_

假设每行有 0 或 2 个",并且只对更改它们之间的内容感兴趣,您可能会使用以下方式AWK。让file.txt内容成为

SendNoticeMsg (api.post = "/test/SendNoticeMsg")
GenerateMsg (api.post = "/test/GenerateMsg")
GetUserLastAction (api.post = "/test/GetUserLastAction")

然后

BEGIN{FS=OFS="""}{while(match($2,/[a-z][A-Z]/)){$2 = substr($2, 1, RSTART) "_" substr($2, RSTART+1)};$2=tolower($2);print}

输出

SendNoticeMsg (api.post = "/test/send_notice_msg")
GenerateMsg (api.post = "/test/generate_msg")
GetUserLastAction (api.post = "/test/get_user_last_action")

解释 我将字段分隔符和输出字段分隔符都设置为",然后我更改$2("内部的内容)如下:只要有小写字母后跟大写字母,它们之间就插入_。最后,我将整个$2更改为小写。(在Gawk 4.2.1中测试)

/in 在锁定之前

perl -pe '$_ = lc join "_", split /(?<=[^A-Z])(?=[A-Z])/;'
my $s = "Foo1BarXBazX";
print $s =~ s/^([A-Z])|(?<=[^A-Z])([A-Z]+)/defined $1 ? "L$1" : "_L$2"; /egr;
"foo1_bar_xbaz_x"

最新更新