我有一个文件需要转换为自定义代码页。该文件具有以下内容:
foo bar baz \bazfoo \barfoo foo bar \foobar
我想用bar替换foo,除非foo作为LaTeX宏的一部分出现,例如\ bazfoo,\ barfoo和\ foobar
换句话说,s/foo/bar/
,但是\ bazfoo必须保留\ bazfoo。有没有办法使用lookead运算符来做到这一点?
答案 0 :(得分:4)
可以使用否定的字符类,要求单词的模式不能以\
开头
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
。我在上面加了
答案 1 :(得分:2)
尽管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#^\\|^\{\\.*\}$#) {
。
答案 2 :(得分:2)
如果每个单词只需要处理一个foo
:
s/ (?: ^ | \s++ ) (?: [^\\\s]\S* )? \K foo /bar/gx
如果您只需要每个单词处理多个foo
:
s{ (?: ^ | \s++ ) \K ( [^\\\s]\S* ) }{ $1 =~ s/foo/bar/rg }egx
这些是先前解答中解决方案的固定版本和优化版本。 (也对先前的答案进行了修正,但并非优化。)
答案 3 :(得分:0)
如果我们确定任何Latex令牌字符都不会成为字符char。并将您的数据存储在“ d”中,只需:
sed -E 's/(^|\s)(\w*)foo/\1\2bar/g' d
perl -pe 's/(^|\s)(?:\w*)foo/$1bar/g' d