Как случайно удалить 100 блоков из текстового файла - PullRequest
0 голосов
/ 30 мая 2018

Предположим, у меня есть огромный текстовый файл, как показано ниже:

19990231  
blabla   
sssssssssssss  
hhhhhhhhhhhhhh  
ggggggggggggggg                  

20090812  
blbclg  
hhhhhhhhhhhhhh  
ggggggggggggggg  
hhhhhhhhhhhhhhh

20010221  
fgghgg  
sssssssssssss  
hhhhhhhhhhhhhhh  
ggggggggggggggg

<etc>  

Как можно случайно удалить 100 блоков, которые начинаются с цифровых символов и заканчиваются пустой строкой?Например:

20090812  
blbclg  
hhhhhhhhhhhhhh  
ggggggggggggggg  
hhhhhhhhhhhhhhh  
<blank line>

Ответы [ 3 ]

0 голосов
/ 30 мая 2018

Использование awk и shuf для удаления 4 блоков из 6 блоков, где каждый блок имеет длину 3 строки:

$ cat tst.awk
BEGIN { RS=""; ORS="\n\n" }
NR==FNR { next }
FNR==1 {
    cmd = sprintf("shuf -i 1-%d -n %d", NR-FNR, numToDel)
    oRS=RS; RS="\n"
    while ( (cmd | getline line) > 0 ) {
        badNrs[line]
    }
    RS=oRS
    close(cmd)
}
!(FNR in badNrs)

$ awk -v numToDel=4 -f tst.awk file file
1
2
3

10
11
12

Просто измените numToDel=4 на numToDel=100 для вашего реального ввода.

Входной файл, использованный для проверки вышеуказанного, был сгенерирован:

$ seq 18 | awk '1; !(NR%3){print ""}' > file

, что привело к:

$ cat file
1
2
3

4
5
6

7
8
9

10
11
12

13
14
15

16
17
18
0 голосов
/ 30 мая 2018

здесь есть решение без перемешивания

$ awk -v RS= -v ORS='\n\n' -v n=100 '
        BEGIN  {srand()} 
        NR==FNR{next} 
        FNR==1 {r[0]; 
                while(length(r)<=n) r[int(rand()*NR)]} 
       !(FNR in r)' file{,} 

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

Поскольку shuf очень быстро, я не думаю, что это принесет вам прирост производительности, но, возможно, будет проще в этом случае.

0 голосов
/ 30 мая 2018

Это не так сложно.Хитрость заключается в том, чтобы сначала определить записи, и это можно сделать с помощью разделителя записей:

RS: Первый символ строкового значения RS должен бытьразделитель входной записи;<newline> по умолчанию.Если RS содержит более одного символа, результаты не указываются. Если RS равно нулю, то записи разделяются последовательностями, состоящими из <newline> плюс одна или несколько пустых строк, начальные или конечные пустые строки не должны приводить к пустым записям в начале или конце ввода, и<newline> всегда должен быть разделителем полей, независимо от значения FS.

Таким образом, количество записей определяется как:

$ NR=$(awk 'BEGIN{RS=""}END{print NR}' <file>)

Затем можно использовать shuf, чтобы получить сто случайных чисел от 1 до NR:

$ shuf -i 1-$NR -n 100

Эту команду вы снова вводите в awk, чтобы выбрать записи:

$  awk -v n=100 '(NR==n){RS="";ORS="\n\n"}       # reset the RS for reading <file>
                 (NR==FNR){print $1; a[$1];next} # load 100 numbers in memory
                 !(FNR in a) { print }           # print records
                ' <(shuf -i 1-$NR -n 100) <file>

Мы также можем сделать это за один раз, используя Knuth shuffle и выполнив двойной проход файла

awk -v n=100 '
   # Create n random numbers between 1 and m
   function shuffle(m,n,    b, i, j, t) {
       for (i = m; i > 0; i--) b[i] = i
       for (i = m; i > 1; i--) {
          # j = random integer from 1 to i
          j = int(i * rand()) + 1
          # swap b[i], b[j]
          t = b[i]; b[i] = b[j]; b[j] = t
       }
       for (i = n; i > 0; i--) a[b[i]]
   }
   BEGIN{RS=""; srand()}
   (NR==FNR) {next}
   (FNR==1)  {shuffle(NR-1,n) }
   !(FNR in a) { print }' <file> <file>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...