使用sed在字符串中插入下划线

时间:2017-01-14 00:44:41

标签: regex sed

我尝试使用sed在一串非空白字符的每个大写字母前面插入_,除非它在开头。 (我想转换在camelcase中的字符串,偶尔包含几个相邻的大写字母甚至是标点符号。)

期望的行为:

输入:

AaAaAa AAA AAA

输出:

Aa_Aa_Aa A_A_A A_A_A

我尝试使用以下命令:

sed -e "s/\(\S\)\([[:upper:]]\)/\1_\2/g"

但它在上面输入的最后两个字符串失败了,产生了这个:

Aa_Aa_Aa A_AA A_AA

我不明白为什么。

我使用的是GNU sed 4.2.2。

3 个答案:

答案 0 :(得分:2)

我假设您的示例输入错误,因为您提供的替换Aa Aa Aa没有做任何事情。它也不是驼峰案例标识符。它应该是AaAaAa,对吗?

如果是这样,那么你可以让sed通过使它循环直到不再发生替换来做你需要的事情:

echo "AaAaAa AAA AAA" | sed -e ':x;s/\([^[:space:]_]\)\([[:upper:]]\)/\1_\2/g;tx' 

产生

Aa_Aa_Aa A_A_A A_A_A 

答案 1 :(得分:2)

这可能适合你(GNU sed):

sed -r 'y/_/\n/;s/[[:upper:]]/_&/g;s/\b_//g;y/\n/_/' file

将所有_转换为唯一替代。插入_的大写字符前面。删除所有前导_。重新转换原始_

如果您首先没有任何前导_,那么这就足够了:

sed -r 's/[[:upper:]]/_&/g;s/\b_//g' file

答案 2 :(得分:1)

问题在于,使用单个s///g时,正则表达式匹配不能重叠(并且未考虑更早匹配的结果)。

使用AAA,第一场比赛是

AAA
^^
| \
\1 \2

更换后,我们有A_AA,其中包含"当前位置"在最右边的两个A之间:

A _ A A
     ^
     next match attempt starts here

然后我们再次尝试匹配,但我们已经没有人物了。 \S与上一个A匹配,但那就是:之后没有大写字符。

为了完成这项工作,我们必须以某种方式将中间A与第一次替换的\2和第二次替换的\1进行匹配,并且我不会# 39;不知道如何用sed做到这一点。

(使用perl会很容易,因为你可以使用后视/前瞻,不会在匹配中包含周围的文字:perl -pe 's/(?<=\S)(?=[[:upper:]])/_/g'