Используя SED / AWK, извлеките строки из текстового файла, где строка содержит N общих слов с предыдущей строкой. - PullRequest
1 голос
/ 25 апреля 2019

Вот пример текстового файла:

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 ]

2 голосов
/ 25 апреля 2019
$ 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 голос
/ 25 апреля 2019

Это может работать для вас (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

Решение состоит из трех частей:

Часть первая

Aинициируется перемещение окна из 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
1 голос
/ 25 апреля 2019

Вы можете обеспечить уникальные значения с помощью хэшей, вот пример сценария:

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
1 голос
/ 25 апреля 2019

Вот решение в 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, также в nawk на 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
0 голосов
/ 26 апреля 2019

если ваши данные в 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
0 голосов
/ 25 апреля 2019
$ 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
0 голосов
/ 25 апреля 2019

Альтернативное решение:

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>
0 голосов
/ 25 апреля 2019

В одну сторону:

$ 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

Сохранить содержимое строки в ассоциативном массиве.Затем проверьте ассоциативный массив и увеличьте счетчик х.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...