使用SED / AWK,从文本文件中提取行,其中该行与上一行具有N个常用词

时间:2019-04-25 12:05:42

标签: shell awk sed

这里有一个示例文本文件:

word1 word2 word3 word4
word4 word5 word6 word7
word6 word7 word8 word9
word9 word6 word8 word3
word1 word4 word5 word4

提取前一行N个常用词的行是什么命令?

在示例文件中,将输出包含3个常见的不同单词的行并与前一行一起输出:

word9 word6 word8 word3

注意:使用编程语言(提取array_sentence1.uniq和array_sentence2.uniq)很容易,但是我使用sed / awk搜索解决方案。

8 个答案:

答案 0 :(得分:2)

$ cat tst.awk
{
    delete seen
    cnt = 0
    for (i=1; i<=NF; i++) {
        word = $i
        cnt += ( !seen[word]++ && prev[word] ? 1 : 0 )
    }

    if (cnt >= 3) {
        print
    }

    delete prev
    for (word in seen) {
        prev[word]++
    }
}

$ awk -f tst.awk file
word9 word6 word8 word3

答案 1 :(得分:1)

这是AWK中的解决方案:

▶ cat > FILE <<EOF
word1 word2 word3 word4
word4 word5 word6 word7
word6 word7 word8 word9
word9 word6 word8 word3
word1 word4 word5 word4
EOF

我的原始解决方案在这里。假设每行中的单词都是唯一的。

# script.awk

NR > 1 {                   # On lines other than the first:
  split(last, last_ar)     #   Split the last record and the
  split($0, curr_ar)       #   current record.

  found = 0                #   Count how many words curr_ar
  for (i in curr_ar)       #   and last_ar have in common.
    for (j in last_ar)
      if (last_ar[j] == curr_ar[i])
        found++

  if (found >= 3) print    #   ... and print this record
                           #   if 3 or more were found.
}

{
  last = $0                # On all lines.
} 

要处理唯一性,我有一个经过修改的解决方案,该解决方案使用GNU AWK的length函数(在Mac OS X上也是如此)

# script.gawk

NR > 1 {
  split(last, last_ar)
  split($0, curr_ar)

  delete found          # Count how many unique occurrences
  for (i in curr_ar)    # of words are seen.
    for (j in last_ar)
      if (last_ar[j] == curr_ar[i])
        found[curr_ar[i]]++

  if (length(found) >= 3) print
}

{
  last = $0
}

测试:

▶ gawk -f script.gawk FILE
word9 word6 word8 word3

答案 2 :(得分:1)

您可以使用哈希确保唯一值,这是一个示例脚本:

parse.awk

# Only start checking from the second line
NR > 1 {
  c = 0        # Variable to hold the common word count

  # Run through unique words and compare to previous line
  for(i=1; i<=NF; i++) {
    if( $i in h && !($i in g) ) {
      c++
      g[$i]
    }
  }

  # Reset the associative arrays
  delete h
  delete g
}

# If we had enough matches print the current line
c >= N

# Collect current line into the h associative array
{
  for(i=1; i<=NF; i++)
    h[$i]
}

像这样运行它:

awk -f parse.awk N=3 infile

输出:

word9 word6 word8 word3

答案 3 :(得分:1)

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

sed -nE 'N;h;s/(.*)(\n.*)/\n\1 \2 /;:a;s/(\n(\S+\s+).*\n.*)\2/N\1/;s/\n\S+\s+/\n/;ta;/^N{3}/{g;s/.*\n//p};g;D' file

解决方案包括三个部分:

第一部分

启动了两行移动的窗口。

制作包含原始2行窗口的原始模式空间的副本。

在模式空间前添加换行符,并在两行的末尾添加其他空格。换行符用作唯一单词计数的分隔符,空格允许每行中的最后一个单词匹配。

第二部分

引发了模式匹配循环,其中第一个单词及其后续空格与第二行中的任何单词匹配。如果匹配,则从第二行中删除该单词,并在引入的换行符之前增加一个计数器。删除第一行中的第一个单词,并重复该过程,直到第一行中没有其他单词为止。

检查计数器所需的匹配数,如果发现为真,则刷新模式空间的副本,删除第一行,并打印第二行。

第三部分

无论如何,都将刷新模式空间,删除第一行,并重复该过程直到文件结束。

上面的解决方案打印出N个或更多匹配项的行(在上面的解决方案中,如OP的示例中N设置为3),因为只有N个匹配项使用:

sed -nE 'N;h;s/(.*)(\n.*)/\n\1 \2 /;:a;s/(\n(\S+\s+).*\n.*)\2/N\1/;s/\n\S+\s+/\n/;ta;/^N{3}\n/{g;s/.*\n//p};g;D' file

答案 4 :(得分:0)

一种方法:

$ awk '{x=0;for(i=1;i<=NF;i++)if ($i in a)x++;split("",a);for(i=1;i<=NF;i++){a[$i]};}x==3' file
word9 word6 word8 word3

将行内容存储在关联数组中。然后检查关联数组并增加计数器x。

答案 5 :(得分:0)

替代解决方案:

awk '{
       c=0; 
       for(i=1;i<=NF;i++)
       {
         if(l[$i]){c+=1}
       }
     }
     {
       delete l; 
       for(i=1;i<=NF;i++)
       {
         l[$i]=1
       }
     } 
     c>=3' <your file>

答案 6 :(得分:0)

$ echo '
> word1 word2 word3 word4
> word4 word5 word6 word7
> word6 word7 word8 word9
> word9 word6 word8 word3
> word1 word4 word5 word4
> ' | awk -v n=3 '
> NR == 1 { for (i = 1; i <= NF; i++) { word[$i]++ } }
> NR >  1 { counter = 0
>           for (i = 1; i <= NF; i++) {
>               if (word[$i]-- > 0) counter++ }
>           if (counter >= n) print $0
>           delete word
>           for (i = 1; i <= NF; i++) { word[$i]++ } }
> '
word9 word6 word8 word3

答案 7 :(得分:0)

如果您的数据位于d文件中,请在gnu awk上尝试

awk 'NR==1{for(;i++<NF;)a[i]=$i;next} {for(i=0;i++<NF;){for(j in a){if($i==a[j])c++;if(c==3){print;exit}}}; c=0;i=length(a);NF+=i;for(j=0;i<NF;)a[++i]=$++j} ' d