如何从文本文件中随机删除100个块

时间:2018-05-30 06:29:41

标签: python linux awk

假设我有一个巨大的文本文件,如下所示:

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 :(得分:3)

这并不困难。诀窍是首先定义记录,这可以使用记录分隔符完成:

  

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>

答案 1 :(得分:2)

使用 <dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-runtime-web_${scala.binary.version}</artifactId> <version>${flink.version}</version> </dependency> awk删除6个块中的4个块,其中每个块长3行:

shuf

只需将$ 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

答案 2 :(得分:1)

这是一个没有随机播放的解决方案

$ 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{,} 

双遍算法,第一轮是计算记录数,创建索引号的随机列表,达到所需值,打印不在列表中的记录。请注意,如果删除的数字更接近记录数,则性能会降低(获得新数字的概率会很低)。对于你的情况,600中的100个不会是一个问题。在另一种情况下,选择要打印的记录而不是删除记录会更容易。

由于shuf非常快,我认为这不会给你带来性能提升,但这种方式可能更简单。