我有一个文件,我需要转换出自定义代码页。该文件包含以下内容:
foo bar baz \bazfoo \barfoo foo bar \foobar
我想用bar替换foo,除非foo作为LaTeX宏的一部分出现,例如\ bazfoo,\ barfoo和\ foobar
换句话说,s/foo/bar/
,但\ bazfoo必须保持\ bazfoo。有没有办法使用lookead运算符?
可以要求带有模式的单词不以\
开头,使用否定的字符类
s{(?: ^|\s ) (?: [^\\\s]\S* )? \K foo}{XXX}gx
foo
也可能出现在字符串的开头或一个单词,因此交替^|\s
和[^\\\s]\S*
是可选的。 \
需要在角色类中进行转义,否则它本身就会逃离]
。
\K
将所有比赛都丢弃到那一点,所以我们不必抓住它们并把它们放回去。
负面的后视不允许可变长度模式,这里有什么问题。
测试,添加测试字符串
perl -wE'$_=q(foo bar somefoo \bazfoo \barfoo foo bar \foobar); say;
s{(?: ^|\s ) (?: [^\\\s]\S* )? \K foo}{XXX}gx; say'
版画
foo bar somefoo \bazfoo \barfoo foo bar \foobar
XXX bar someXXX \bazfoo \barfoo XXX bar \foobar
请注意,您的测试字符串不包括foo
在单词内但仍需要替换的情况,例如somefoo
。我在上面添加了它
如果你只需要为每个单词处理一个foo
:
s/ (?: ^ | \s++ ) (?: [^\\\s]\S* )? \K foo /bar/gx
如果你只需要处理每个单词的多个foo
:
s{ (?: ^ | \s++ ) \K ( [^\\\s]\S* ) }{ $1 =~ s/foo/bar/rg }egx
这些是早期答案中解决方案的固定和优化版本。 (修复但未对前面的答案进行优化。)
虽然zdim已经有了一个引人入胜的解决方案,但我仍想分享我的版本。
我也有问题,因为看起来可变长度。
所以我的解决方案是“标记化”字符串含义:选择每个“单词”并仅替换那些不以\开头的单词。
perl -e '
$_=q(foo bar baz \bazfoo \barfoo foo bar \foobar);
s/(\S+)/ # pick the word
$word=$1; # save it
if ($word!~m#^\\#) { # test for LaTeX
$word=~s#foo#bar#g; # otherwise replace
}
$word # the result
/gex; # globally, execute and eXtended for comments
print $_;
'
不幸的是,这需要使用“e”( - xecute)标志。
更新:根据@Alex(见下面的评论)»此解决方案将找不到{\ foo},这是有效的LaTeX语法。«
因此,如果需要,将上面的if语句行更改为if ($word!~m#^\\|^\{\\.*\}$#) {
。
如果我们确定任何Latex令牌字符永远不会是字char。以及'd'中的数据,简单地说:
sed -E 's/(^|\s)(\w*)foo/\1\2bar/g' d
perl -pe 's/(^|\s)(?:\w*)foo/$1bar/g' d