Как удалить из текстового файла любую строку, содержащую 2 или более слов (, не разделенных пробелом )?

У вас есть один зашифрованный раздел в Ubuntu, но, поскольку вы не знали, как вы его установили, самый простой способ избавиться от него — это:

  • Загрузка Ubuntu
  • Резервное копирование всех ваших данных на внешний диск в FAT32¹
  • Установить Mint; стирание всего диска
  • Установите Ubuntu
  • Восстановите резервную копию

Примечание¹:FAT32 не позволяет шифровать данные, поэтому вы не сможете случайно зашифровать их, не зная

1
17.07.2020, 12:46
5 ответов
awk '{for (i in a) if (index($0,i)) next; print; a[$0]}' file
-1
18.03.2021, 23:20

Это печатает все слова в файле, которые не являются комбинациями каких-либо двух слов в файле:

$ awk '{one[NR]=$1} END{for (i=1;i<=length(one);i++) for (j=1;j<=length(one);j++) two[one[i] one[j]]; for (i=1;i<=length(one);i++) if (!(one[i] in two)) print one[i]}' file
alpha
beta
gama
zeta

Для тех, кто предпочитает разбивать команды на несколько строк:

awk '
    {
    one[NR]=$1
    }

    END{
        for (i=1;i<=length(one);i++)
            for (j=1;j<=length(one);j++)
                two[one[i] one[j]]
        for (i=1;i<=length(one);i++)
            if (!(one[i] in two))
                print one[i]
     }' file

Другой пример

Давайте рассмотрим файл с похожими словами, но с комбинациями, иногда стоящими перед отдельными словами:

$ cat file2
alphabeta
alpha
gammaalpha
beta
gamma

Выполнение той же команды по-прежнему дает правильный результат:

$ awk '{one[NR]=$1} END{for (i=1;i<=length(one);i++) for (j=1;j<=length(one);j++) two[one[i] one[j]]; for (i=1;i<=length(one);i++) if (!(one[i] in two)) print one[i]}' file2
alpha
beta
gamma

Как это работает

  • one[NR]=$1

    Это создает массив oneс ключами, являющимися номерами строк, NR, и значениями, являющимися словом в этой строке.

  • END{...}

    Команды в фигурных скобках выполняются после того, как мы закончили чтение файла. Эти команды состоят из двух циклов. Этот первый цикл:

     for (i=1;i<=length(one);i++)
          for (j=1;j<=length(one);j++)
              two[one[i] one[j]]
    

    Это создает массив twoс ключами, состоящими из каждой комбинации двух слов в файле.

    Второй цикл:

      for (i=1;i<=length(one);i++)
          if (!(one[i] in two))
              print one[i]
    

    Этот цикл выводит каждое слово в файле, которое не отображается как ключ в массиве two.

Более короткая и простая версия

Эта версия использует более короткий код и выводит те же слова. Недостатком является то, что не гарантируется, что слова будут в том же порядке, что и во входном файле :

.
$ awk '{one[$1]} END{for (w1 in one) for (w2 in one) two[w1 w2]; for (w in one) if (!(w in two)) print w}' file1
gama
zeta
alpha
beta

Больше памяти -эффективный подход

Для больших файлов описанные выше методы могут привести к переполнению памяти. В этих случаях рассмотрим:

$ sort -u file | awk '{one[$1]} END{for (w1 in one) for (w2 in one) print w1 w2}' >doubles
$ grep -vxFf doubles file
alpha
beta
gama
zeta

Это использует sort -uдля удаления любых повторяющихся слов из файла1, а затем создает файл возможных двойных слов с именем doubles. Затем grepиспользуется для печати строк в file, которых нет в doubles.

3
18.03.2021, 23:20
  1. Сортировка файла
  2. Пропускать строки, пока они совпадают с предыдущей

В ОК:

sort file.txt | awk '!pattern || !($0~pattern) { pattern = $0; print }'

Перл:

$ sort file.txt | perl -wnlE 'BEGIN {my $pattern};if(!$pattern || !/$pattern/) {$pattern=$_; say }
0
18.03.2021, 23:20

С достаточно короткими файлами и при условии, что строки не содержат операторов ERE:

$ LC_ALL=C grep -vxE "($(paste -sd '|' file)){2,}" file
alpha
beta
gama
zeta

Возвращает строки, которые не содержат последовательности из 2 или более строк из file.

Это работает путем построения команды grepтипа:

LC_ALL=C grep -vxE '(alpha|beta|gama|alphabeta|zeta|gamabeta){2,}' file

Для больших файлов вы столкнетесь с ограничением длины или аргументов+окружения (или одного аргумента в Linux ). Это можно обойти, используя -f -для передачи регулярного выражения через стандартный ввод, а не через аргументы, но даже в этом случае вы столкнетесь с ограничениями на размер регулярных выражений.

Используя perlвместо grep, я могу обрабатывать большие входные данные:

perl -le '
  chomp (@words = <>);
  $re = "^(". join("|", map {qr{\Q$_\E}} @words). "){2,}\\z";
  for (@words) {print unless m/$re/}' file

(, что также позволяет избежать других ограничений, упомянутых выше ).

В любом случае, это займет много времени, так как каждое слово нужно сравнивать с каждым другим словом (, возможно, более одного раза ).

2
18.03.2021, 23:20
<file awk 'NF {print length "\t" $0}' | sort -k1n,1 | cut -f2- |
awk 'NR==1 {min=length}
(l=length) >= 2*min {
  delete k; # clear k array
  k[1];
  while (length(k))
    for (i in k) {
      for (j=l-i+1; j>=min; --j)
        if (substr($0,i,j) in seen) {
          if (i+j-1==l)
            next;
          k[i+j];
        }
      delete k[i];
    }
}
!seen[$0]++'

Строки, полностью состоящие из ранее просмотренных строк, не будут напечатаны.

Работает путем проверки подстрок на наличие уже просмотренных строк.

Требуется, чтобы входной файл был отсортирован по длине строки от самой короткой до самой длинной. awk | sort | cutделает это.

Следующая программа awkначинается с определения длины самой короткой строки (, сохраненной какmin). Любая строка, длина которой меньше 2*min, не нуждается в проверке ее подстрок. Вместо этого его можно добавить к хэшу массива seenи распечатать(!seen[$0]++используется как условие для печати не -дубликатов, подробнее:Как работает awk '!a[$0]++'?).minтакже можно использовать в качестве длины отсечки при проверке подстрок.

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

  • Если подстрока находится в конце строки, перейти к строке nextввода. Строка не печатается и не добавляется в видимый массив.
  • В противном случае добавьте следующую начальную позицию к kи продолжите поиск дополнительных подстрок.
  • Продолжайте попытки, пока не будут найдены новые стартовые позиции(while (length(k))).
  • Если указанный выше цикл не перешел к следующей строке, строка добавляется в seenхеш-массив (и печатается, если она еще не видна ).
2
18.03.2021, 23:20

Теги

Похожие вопросы