如何更改字符串实例的范围

如何更改字符串实例的范围

我需要更改文本文件每一行上子字符串的一些实例。这些实例都是连续的,例如 3 到 6 或 2 到 5 等。此操作需要什么命令?我得到的最接近的是:

sed 's/this/that/3' file1

这只改变了第三次出现的情况。我希望有类似的东西

sed 's/this/that/3,6' file1

可能是答案,但sed没有出现范围。

输入示例:

I want to change all letters "a" to "w" starting from the word "all" until the second "all" (inclusive)

预期输出:

I want to change wll letters "w" to "w" stwrting from the word "wll" until the second "all" (inclusive)

答案1

perl

将第三到第六as改为bs:

$ echo aaaaaaaaa | perl -pe '$n=0; s{a}{++$n;$n==3..$n==6?"b":$&}ge'
aabbbbaaa

这使用了运算符e的标志s{regex}{replacement}flags,因此替换被评估为代码,"b"如果递增的计数器在 3 到 6 之间,则给出该代码,$&否则将给出匹配的值 ( )。或者:

$ echo aaaaaaaa | perl -pe '$n=0; s/a(?(?{++$n; $n == 3 .. $n == 6})|(*FAIL))/b/g'
aabbbbaa

使用(?(condition)yes|no)正则表达式运算符,该运算符给出(*FAIL)递增计数器是否不在 3 .. 6 范围内的信息。

GNUsed支持s/foo/bar/3g替换第三次之后出现的foo.

因此,对于这样的固定字符串,您可以这样做:

$ echo aaaaaaaaa | sed 's/a/\n/3g;s/\n/a/5g;s/\n/b/g'
aabbbbaaa

也就是说,将倒数第 3a换行符替换为换行符(保证在模式空间中不会出现其他情况),然后将倒数5 个换行符恢复回s (6 - 3 + 1 = 4 是我们想要的 sa数量)a替换,因此恢复后面的那些),然后用bs 替换所有剩余的换行符。

与任何sed

sed 's/a/\
/g
s/\n/b/3
s/\n/b/3
s/\n/b/3
s/\n/b/3
s/\n/a/g'

要将的前两次出现之间的s更改a为s :wall

$ echo aaallaaallaaa | perl -pe 's{all.*?all}{$& =~ s/a/w/gr}e'
aawllwwwllaaa

\b如果all必须分隔,请使用字边界运算符

$ echo 'alloy (all-hands aaa ball all) fall' | perl -pe 's{all.*?all}{$& =~ s/a/w/gr}e'
wlloy (wll-hands aaa ball all) fall
$ echo 'alloy (all-hands aaa ball all) fall' | perl -pe 's{\ball\b.*?\ball\b}{$& =~ s/a/w/gr}e'
alloy (wll-hwnds www bwll wll) fall

(添加-Mopen=locale要根据区域设置字符映射进行解码的字符,而不是假设它们只是 ASCII,例如将 UTF-8 编码allée为一个法语单词,并且all后面不跟一些非单词字符)。

答案2

这对于 来说可能是不可能的sed,但是下面的 GNUawk程序可以工作:

awk -v frst=2 -v lst=5 '{for (i=1; i<=(lst-frst+1);i++) $0=gensub(/a/,"w",frst)}1'
  • 这会将第一次和最后一次出现的情况传递为变量frstlst来替换awk
  • 然后它将用于gensub()替换frst行上第 -th 次出现的搜索模式,并将结果分配回当前行缓冲区。
  • 这将总共执行 ( lst-frst+1) 次,以替换所有所需的事件。然后,打印当前行(包括所有修改)。
  • 请注意,要替换的字符串中出现的次数始终保持不变,因为已替换的出现次数不再计入下一次循环迭代中。

应用示例:

$ echo "a1a2a3a4a5a6" | awk -v frst=2 -v lst=5 '{for (i=1; i<=(lst-frst+1);i++) $0=gensub(/a/,"w",frst)}1'
a1w2w3w4w5a6

或你原来的:

$ echo 'I want to change all letters "a" to "w" starting from the word "all" until the second "all" (inclusive)' | awk -v frst=3 -v lst=6 '{for (i=1; i<=(lst-frst+1);i++) $0=gensub(/a/,"w",frst)}1'
I want to change wll letters "w" to "w" stwrting from the word "wll" until the second "all" (inclusive)

与往常一样,如果搜索模式可以重叠,则这将无法按预期工作。

答案3

对于任何 awk:

awk -v FS='a' -v start=3 -v end=6 -v replace="w" '
{
  for(i=1; i<NF; i++)
      printf("%s", $i (start<=i && i<=end? replace: FS))
  print $NF
}' infile

在这里,FS=a我们告诉 awk 根据字符分割记录a;我们还将其他 awk 变量定义为开始,结尾代替分别为目标字符“a”的开始和结束位置,我们将其替换为“w”字符。

然后我们循环遍历字段并打印字段本身,如果字段编号在起始点和结束点之间,则打印字段本身,如果不在该范围内,则打印字符“a”。最后我们也输出最后一个字段。

运行代码片段

答案4

使用(以前称为 Perl_6)

~$ echo aaaaaaaa | perl6 -pe 's:nth(3..6)/a/b/;'
aabbbbaa

Raku(Perl 系列中的一种编程语言)有一个新的nth正则表达式修饰符(“位置副词”),它支持1st2nd3rd等同义词。对于一般替换用途nth(),插入数字或范围参数。

以上是快速版本。下面只是为了演示 Raku 正则表达式修饰符在这方面的直观性(6th5th4th3rd匹配项被连续替换,每次用于andthen重新加载$_主题变量):

~$ echo aaaaaaaa | perl6 -ne 'S:6th/a/b/ andthen S:5th/a/b/ andthen S:4th/a/b/ andthen S:3rd/a/b/ andthen .put;'
aabbbbaa

https://docs.raku.org/language/regexes#Positional_adverbs
https://raku.org

相关内容