【问题标题】:Using SED/AWK, extract lines from text file where line have N common words with previous line使用 SED/AWK,从文本文件中提取行与前一行有 N 个常用词的行
【发布时间】:2019-04-25 12:05:42
【问题描述】:

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

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 搜索解决方案。

【问题讨论】:

  • 到目前为止你尝试了什么?
  • 我现在已经解决了这个问题,但公平地说,@Thor 应该是公认的答案。
  • @AlexHarvey:不用担心

标签: shell awk sed


【解决方案1】:
$ 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

【讨论】:

    【解决方案2】:

    如果您的数据在 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
    

    【讨论】:

      【解决方案3】:

      这是 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 的长度函数,也在 Mac OS X 上的 nawk 中:

      # 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
      

      【讨论】:

        【解决方案4】:

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

        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
        

        【讨论】:

          【解决方案5】:

          这可能对你有用(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行的移动窗口被煽动。

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

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

          第二部分

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

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

          第三部分

          不管上述情况如何,模式空间都会被刷新,第一行被删除并重复该过程直到文件结束。

          上面的解决方案打印 N 个或更多匹配的行(在上面的解决方案中,N 设置为 3,如 OP 的示例中所示)仅用于 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
          

          【讨论】:

            【解决方案6】:

            一种方式:

            $ 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。

            【讨论】:

              【解决方案7】:

              替代解决方案:

              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>
              

              【讨论】:

              • 永远不要使用名为 l 的变量,因为它看起来过多地与数字 1 对齐,从而混淆了您的代码。
              【解决方案8】:
              $ 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
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2016-04-21
                • 2013-01-07
                • 2021-02-14
                • 2017-01-12
                • 1970-01-01
                • 2013-08-29
                • 1970-01-01
                • 2023-03-05
                相关资源
                最近更新 更多