regex 使用sed GnuWin32删除一行中的重复单词

g2ieeal7  于 2023-06-30  发布在  其他
关注(0)|答案(4)|浏览(120)

我在试着删除文本中的重复单词。这些文章中描述的相同问题:Remove duplicate words in a line with sed和:Removing duplicate strings with SED但是这些变体对我不起作用。可能是因为我使用GnuWin32
例如我需要的结果:

输入

One two three bird animal two bird

输出

One two three bird animal
nr9pn0ug

nr9pn0ug1#

我认为这将是 * 远 * 更快的awk。
这应该可以在任何平台上工作,但我没有在Windows上验证过:

awk '{
  sp = "";
  delete seen;
  for (i=1; i<=NF; i++) if (!seen[$i]++) { printf "%s%s", sp, $i; sp = " "; }
  printf "\n";
}' file

(Feel自由地将其压缩到一条线上,它会工作得很好。)
AWK在列式数据方面非常出色。默认情况下,它将每行的文本划分为由白色分隔的字段(因此给定hello world,我们得到$1 = "hello"$2 = "world")。特殊的NF变量是它找到的字段的数量,所以for (i=1; i<=NF; i++)迭代每个字段(单词),作为值为$ii
我在这里使用了一个关联数组(也称为字典或哈希)。索引$i(当前字)处的seen数组从零开始(未初始化)。我们递增它,但就像C一样,awk使用x++递增x,但返回其原始值(与++x相反,它递增并返回递增后的值)。因此,!seen[$i]++是true(!0),当我们还没有在这个字处递增数组时,它对我们来说是新的。seen在每一行都被清除,所以我们每行都有唯一的字,而不是整个文件。
既然我们还没看过,我们就得印出来。注意,单词之间的原始白色将丢失(它不会存储在任何地方)。我们只打印一个空格(但不是在新行的开头,因此是sp变量),然后是新单词。
在for循环之后,我们完成了行。永远不会有任何尾随空格。(另外,实际的行尾丢失了,所以我们假设它是\n。如果你想要DOS行结束符,使用\r\n

nbewdwxp

nbewdwxp2#

工具sed并不是为这项工作而设计的。sed只有两种形式的内存,模式空间和保持空间,它们只不过是它可以记住的两个简单字符串。每次你在这样的内存块上做一个操作,你必须重写整个内存块并重新分析它。另一方面,Awk在这里有更大的灵活性,可以更容易地操作有问题的行。

awk '{delete s}
     {for(i=1;i<=NF;++i) if(!(s[$i]++)) printf (i==1?"":OFS)"%s",$i}
     {printf ORS}' file

但由于你在windows机器上工作,这也意味着你有CRLF行结束符。这可能会对最后一个条目产生轻微的问题。如果该行为:

foo bar foo

awk会把它读成

foo bar foo\r

因此由于CR,最后的foo将不匹配第一个foo。
更正如下:

awk 'BEGIN{RS=ORS="\r\n"}
     {delete s}
     {for(i=1;i<=NF;++i) if(!(s[$i]++)) printf (i==1?"":OFS)"%s",$i}
     {printf ORS}' file

这可以使用,因为你使用CygWin,它在最后的GNU中,所以我们可以使用RS的扩展名作为正则表达式或多字符值。
如果你想区分大小写,你可以用s[tolower($i)]代替s[$i]
还有一些句子

"There was a horse in the bar, it ran out of the bar."

单词bar可以在这里匹配,但是,.使它不匹配。这可以通过以下方式解决:

awk 'BEGIN{RS=ORS="\r\n"; ere="[,.?:;\042\047]"}
     {delete s}
     {for(i=1;i<=NF;++i) {
        key=tolower($i); sub("^" ere,"",key); sub(ere "$","",key)
        if(!(s[key]++)) printf (i==1?"":OFS)"%s",$i
      } 
     }
     {printf ORS}' file

这基本上是相同的,但删除了单词开头和结尾的标点符号。标点符号列在ere

efzxgjgh

efzxgjgh3#

这可能对你有用(GNU sed):

sed -E ':a;s/\<((\S+)\>.*)\s\<\2\>/\1/gi;ta' file

匹配任何单词并删除前面白色及其重复项。重复。
注意:正则表达式删除重复项时不考虑大小写。如果要将Oneone分开处理,请用途:

sed -E ':a;s/\<((\S+)\>.*)\s\<\2\>/\1/g;ta' file
y1aodyip

y1aodyip4#

对于可能包含- / '等的唯一单词(其中\<\>将打断' word ',例如内核命令行中的选项):
1.在输入字符串前后填充空格," $string "如下

  1. string=$(sed -E ':a;s/(\s(\S+)\s.*)\2\s/\1/;ta' <<< " $string ")
    1.拆下衬垫string=${string# }; string=${string% }

相关问题