替换两个字符串之间的指定字符?

替换两个字符串之间的指定字符?

我有事要完成。我需要将所有出现的 & 内部或之间替换<ex> </ex>为 #。实际例子如下:

a & b & c <ex> a & b & c </ex> a & b & c

再次,我需要替换所有出现的 & 内部<ex>和之前</ex>

预期输出:

a & b & c <ex> a # b # c </ex> a & b & c

请发布关于你们如何做到这一点的解释。

编辑#1

请只向我提供一个sed解决方案,因为我将在 AS400 系统上运行它,并且无法安装 Perl 或任何其他解释器。

答案1

<ex>...</ex>如果每行只出现一次:

sed -e :1 -e 's@\(<ex>.*\)&\(.*</ex>\)@\1#\2@;t1'

如果可能有多个出现并且它们不嵌套(或者它们嵌套并且您只想替换&最深的出现中的):

sed '
  s|_|_u|g        # replace all underscores with "_u"
  s|(|_o|g        # replace all open parentheses with "_o"
  s|)|_c|g        # replace all close parentheses with "_c"
  s|<ex>|(|g      # replace all open ex tags with "("
  s|</ex>|)|g     # replace all close ex tags with ")"

  :1              # a label

  s/\(([^()]*\)&\([^()]*)\)/\1#\2/g
                  # find:
                  #   an open parentheses, 
                  #   some non-parentheses chars (captured),
                  #   an ampersand, 
                  #   some non-parentheses chars (captured) and 
                  #   a close parentheses, 
                  # replace with
                  #   the first captured text, 
                  #   an octothorpe
                  #   the second captured text, 
                  # globally in the current record.

  t1              # if there was a successful replacement, goto label "1",
                  # else carry on

  s|(|<ex>|g      # restore open tags
  s|)|</ex>|g     # restore close tags
  s|_o|(|g        # restore open parentheses
  s|_c|)|g        # restore close parentheses
  s|_u|_|g        # restore underscores
'

如果它们可能嵌套并且您想将其替换为封闭的:

sed '
  s|_|_u|g;s|(|_o|g;s|)|_c|g
  s|<ex>|(|g;s|</ex>|)|g;:1
  s/\(([^()]*\)(\([^()]*\))\([^()]*)\)/\1_O\2_C\3/g;t1
  :2
  s/\(([^()]*\)&\([^()]*)\)/\1#\2/g;t2
  s|(|<ex>|g;s|)|</ex>|g
  s|_O|<ex>|g;s|_C|</ex>|g
  s|_o|(|g;s|_c|)|g;s|_u|_|g'

答案2

Perl(需要 5.14 版本)来救援:

perl -pe 's%(<ex>.*?</ex>)% $1 =~ s/&/#/gr %eg'

在旧版本中,您必须更详细:

perl -pe 's%(<ex>.*?</ex>)% ($_x = $1) =~ s/&/#/g; $_x %eg'

说明:将<ex>标签之间的所有内容放入$1, 内$1,将 & 替换为#。

答案3

另一个 perl 命令,

$ perl -pe 's/&(?=(?:(?!<ex>|<\/ex>).)*<\/ex>)/#/g' file
a & b & c <ex> a # b # c </ex> a & b & c

在解释上述命令之前,我将解释一下负前瞻和正前瞻实际上会做什么。

在正则表达式中(?=...)意味着积极的前瞻。环视(即;正向和负向前瞻、正向和负向向后查找)将进行零宽度匹配。也就是说,它不会匹配任何字符。通常正向和负向前瞻用于条件检查目的。也(?:...)称为非捕获组。也就是说,非捕获组内的模式只会进行匹配操作。它不会捕获任何字符。

  • (?!<ex>|<\/ex>)无法匹配字符串<ex></ex>
  • (?:(?!<ex>|<\/ex>).)它实际上的意思是,它首先查找后面的三个字符或四个字符,并确保后面的三个或四个字符不能是<ex></ex>.只有满足这个条件才匹配后面的字符。
  • (?:(?!<ex>|<\/ex>).)*它执行上述步骤零次或多次,直到检测到字符串<ex>或。</ex>一旦找到这两个字符串中的任何一个,它就会突然停止匹配以下字符。

  • (?:(?!<ex>|<\/ex>).)*<\/ex>也匹配以下</ex>字符串。这一切都被纳入了前瞻中。

  • &(?=(?:(?!<ex>|<\/ex>).)*<\/ex>)&最后,只有后面跟着满足上述条件的字符时,它才匹配该字符。即,&后面必须跟任何不属于的字符,<ex>或者后面必须</ex>跟有结束标记零次或多次</ex>

答案4

如果行中出现不止一次,而不是嵌套,也许这会有所帮助:

#cat plop
>a & b & c <ex> a & b & c </ex> a & b & c <ex> a & b & c </ex> a & b & c

#cat plop |sed -e :1 -e 's@\(<ex>[^(</ex>)]*\)&\(.*</ex>\)@\1+\2@;t1'     
>a & b & c <ex> a + b + c </ex> a & b & c <ex> a + b + c </ex> a & b & c

相关内容