用正则表达式替换字符时忽略乳胶宏

问题描述 投票:2回答:4

我有一个文件,我需要转换出自定义代码页。该文件包含以下内容:

foo bar baz \bazfoo \barfoo foo bar \foobar

我想用bar替换foo,除非foo作为LaTeX宏的一部分出现,例如\ bazfoo,\ barfoo和\ foobar

换句话说,s/foo/bar/,但\ bazfoo必须保持\ bazfoo。有没有办法使用lookead运算符?

regex perl latex
4个回答
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。我在上面添加了它


2
投票

如果你只需要为每个单词处理一个foo

s/ (?: ^ | \s++ ) (?: [^\\\s]\S* )? \K foo /bar/gx

如果你只需要处理每个单词的多个foo

s{ (?: ^ | \s++ ) \K ( [^\\\s]\S* ) }{ $1 =~ s/foo/bar/rg }egx

这些是早期答案中解决方案的固定和优化版本。 (修复但未对前面的答案进行优化。)


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#^\\|^\{\\.*\}$#) {


0
投票

如果我们确定任何Latex令牌字符永远不会是字char。以及'd'中的数据,简单地说:

sed -E 's/(^|\s)(\w*)foo/\1\2bar/g' d
perl -pe 's/(^|\s)(?:\w*)foo/$1bar/g' d
© www.soinside.com 2019 - 2024. All rights reserved.