使用 sed GnuWin32 删除一行中的重复单词
Remove duplicate words in a line with sed GnuWin32
我正在尝试删除文本中的重复字词。这些文章中描述的相同问题: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
这可能适合您 (GNU sed):
sed -E ':a;s/\<((\S+)\>.*)\s\<\>//gi;ta' file
匹配任何单词并删除前面的白色 space 及其重复项。重复。
N.B。正则表达式删除重复项而不考虑大小写。如果要将 One
与 one
分开处理,请使用:
sed -E ':a;s/\<((\S+)\>.*)\s\<\>//g;ta' file
我认为这在 awk 中 远 更快。
这应该适用于任何平台,但我还没有在 Windows 上验证它:
awk '{
sp = "";
delete seen;
for (i=1; i<=NF; i++) if (!seen[$i]++) { printf "%s%s", sp, $i; sp = " "; }
printf "\n";
}' file
(随意将其压缩到一行,它会很好地工作。)
AWK 擅长列式数据。默认情况下,它将每一行的文本划分为由连续的白色 space 分隔的字段(因此给定 hello world
,我们得到 = "hello"
和 = "world"
)。特殊的 NF
变量是它找到的字段数,因此 for (i=1; i<=NF; i++)
遍历每个字段(单词)作为 i
其值为 $i
.
我在这里使用关联数组(又名字典或散列)。索引 $i
处的 seen
数组(当前单词)从零开始(未初始化)。我们增加它,但就像 C 一样,awk 使用 x++
增加 x
但 return 它的原始值(对比 ++x
增加和 returns增值)。因此,当我们还没有在这个词处递增数组时,!seen[$i]++
为真 (!0
)——它对我们来说是新的。 seen
在每一行都被清除,因此我们每行都有唯一的单词而不是整个文件。
知道没看到,还得打印出来。请注意,单词之间原来的白色 space 丢失了(它没有存储在任何地方)。我们只是打印一个 space (但不是在新行的开头,因此是 sp
变量)然后是新单词。
在for循环之后,我们完成了这一行。永远不会有任何尾随 spaces。 (另外,实际的行尾丢失了,所以我们假设它是 \n
。如果你想要 DOS 行尾,使用 \r\n
。)
工具sed
并不是真正为这项工作设计的。 sed 只有两种记忆形式,pattern-space 和 hold-space,无非就是它能记住的两个简单的字符串。每次对这样的内存块进行操作时,都必须重写整个内存块并重新分析它。另一方面,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[$i]
替换为 s[tolower($i)]
。
像
这样的句子还有问题
"There was a horse in the bar, it ran out of the bar."
单词bar
可以在这里匹配,但是,
和.
使它不匹配。这可以通过以下方式解决:
awk 'BEGIN{RS=ORS="\r\n"; ere="[,.?:;27]"}
{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
我正在尝试删除文本中的重复字词。这些文章中描述的相同问题: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
这可能适合您 (GNU sed):
sed -E ':a;s/\<((\S+)\>.*)\s\<\>//gi;ta' file
匹配任何单词并删除前面的白色 space 及其重复项。重复。
N.B。正则表达式删除重复项而不考虑大小写。如果要将 One
与 one
分开处理,请使用:
sed -E ':a;s/\<((\S+)\>.*)\s\<\>//g;ta' file
我认为这在 awk 中 远 更快。
这应该适用于任何平台,但我还没有在 Windows 上验证它:
awk '{
sp = "";
delete seen;
for (i=1; i<=NF; i++) if (!seen[$i]++) { printf "%s%s", sp, $i; sp = " "; }
printf "\n";
}' file
(随意将其压缩到一行,它会很好地工作。)
AWK 擅长列式数据。默认情况下,它将每一行的文本划分为由连续的白色 space 分隔的字段(因此给定 hello world
,我们得到 = "hello"
和 = "world"
)。特殊的 NF
变量是它找到的字段数,因此 for (i=1; i<=NF; i++)
遍历每个字段(单词)作为 i
其值为 $i
.
我在这里使用关联数组(又名字典或散列)。索引 $i
处的 seen
数组(当前单词)从零开始(未初始化)。我们增加它,但就像 C 一样,awk 使用 x++
增加 x
但 return 它的原始值(对比 ++x
增加和 returns增值)。因此,当我们还没有在这个词处递增数组时,!seen[$i]++
为真 (!0
)——它对我们来说是新的。 seen
在每一行都被清除,因此我们每行都有唯一的单词而不是整个文件。
知道没看到,还得打印出来。请注意,单词之间原来的白色 space 丢失了(它没有存储在任何地方)。我们只是打印一个 space (但不是在新行的开头,因此是 sp
变量)然后是新单词。
在for循环之后,我们完成了这一行。永远不会有任何尾随 spaces。 (另外,实际的行尾丢失了,所以我们假设它是 \n
。如果你想要 DOS 行尾,使用 \r\n
。)
工具sed
并不是真正为这项工作设计的。 sed 只有两种记忆形式,pattern-space 和 hold-space,无非就是它能记住的两个简单的字符串。每次对这样的内存块进行操作时,都必须重写整个内存块并重新分析它。另一方面,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[$i]
替换为 s[tolower($i)]
。
像
这样的句子还有问题"There was a horse in the bar, it ran out of the bar."
单词bar
可以在这里匹配,但是,
和.
使它不匹配。这可以通过以下方式解决:
awk 'BEGIN{RS=ORS="\r\n"; ere="[,.?:;27]"}
{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