首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用sed GnuWin32删除一行中的重复单词

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

Stack Overflow用户
提问于 2019-12-09 08:53:07
回答 3查看 239关注 0票数 0

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

例如,我需要什么样的结果:

输入

代码语言:javascript
复制
One two three bird animal two bird

输出

代码语言:javascript
复制
One two three bird animal
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2019-12-22 16:15:42

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

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

但既然你在windows机器上工作,这也意味着你有CRLF的终端机。这可能会给最后一个条目带来轻微的问题。如果这一行是:

代码语言:javascript
复制
foo bar foo

awk会把它解读为

代码语言:javascript
复制
foo bar foo\r

因此,由于CR,最后一个foo将与第一个foo不匹配。

现在更正如下:

代码语言:javascript
复制
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[$i]替换s[tolower($i)]

像这样的句子还是有问题的

代码语言:javascript
复制
"There was a horse in the bar, it ran out of the bar."

单词bar在这里可以匹配,但是,.使得它不匹配。解决这一问题的办法是:

代码语言:javascript
复制
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中列出

票数 2
EN

Stack Overflow用户

发布于 2019-12-18 18:07:12

我认为这在awk中会快得多。

这应该适用于任何平台,但我还没有在Windows上验证它:

代码语言:javascript
复制
awk '{
  sp = "";
  delete seen;
  for (i=1; i<=NF; i++) if (!seen[$i]++) { printf "%s%s", sp, $i; sp = " "; }
  printf "\n";
}' file

(你可以把它压缩到一条线上,这样就行了。)

AWK在柱状数据方面非常出色。默认情况下,它将每一行的文本划分为由连续空格分隔的字段(因此,给定hello world,我们得到了$1 = "hello"$2 = "world")。特殊的NF变量是它找到的字段数,因此for (i=1; i<=NF; i++)在每个字段(word)上迭代为值为$ii

这里我使用的是关联数组(也称为字典或散列)。索引seen (当前单词)处的$i数组开始为零(未初始化)。我们增加它,但是就像C一样,awk使用x++来增量x,但返回它的原始值(与++x不同,++x增加并返回增量的值)。因此,当我们还没有在这个词中增加数组时,!seen[$i]++就是真(!0) --这对我们来说是新的。seen在每一行都被清除,所以我们每一行都有唯一的单词,而不是整个文件。

我们知道我们还没看过,所以我们得把它打印出来。注意,单词之间的原始空格丢失了(它没有存储在任何地方)。我们只打印一个空格(但不是在新行的开头,因此是sp变量),然后再打印新单词。

在for循环之后,我们完成行。永远不会有任何尾随的空间。(另外,实际的行尾也丢失了,所以我们假设它是\n。如果您想要DOS行的结尾,请使用\r\n。)

票数 3
EN

Stack Overflow用户

发布于 2019-12-09 12:04:31

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

代码语言:javascript
复制
sed -E ':a;s/\<((\S+)\>.*)\s\<\2\>/\1/gi;ta' file

匹配任何单词并删除前面的空白及其副本。重复一遍。

注:雷杰普不考虑情况而移除重复项。如果要将One单独处理为one,请使用:

代码语言:javascript
复制
sed -E ':a;s/\<((\S+)\>.*)\s\<\2\>/\1/g;ta' file
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59245359

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档