总结;

总结;

我有一长串的行号 (35389208),我不想在我的文件中出现。行号指的是文件中的行 (例如第 277 行)。我不想要的行号列表如下:

277
278
279
280
289
290
291
292
321
322
....

从我的文件中排除这些行号的最佳方法是什么?在 perl sed 或 awk (或其他任何方法) 中解决。

答案1

如果可以选择将所有行号读入内存,则可以这样做awk

awk 'FNR == NR { h[$1]; next } !(FNR in h)' line-numbers.txt input.txt

如果您的可用内存有限并且line-numbers.txt文件按数字排序,则可以这样做:

删除行.awk

BEGIN {  
  lines_file = "line-numbers.txt"
  if(!(getline n < lines_file)) { 
    print "Unable to open lines file " lines_file > "/dev/stderr" 
    exit 
  } 
} 

FNR != n

FNR == n {
  getline n < lines_file
}

像这样运行:

awk -f delete-lines.awk input.txt

测试包含以下line-numbers.txt内容:

277
278
279
280
289
290
291
292
321
322

input.txt表示为seq 325

首先是内存中的行号:

seq 325 | awk 'FNR == NR { h[$1]; next } !(FNR in h)' line-numbers.txt -

然后一次读取一个行号:

seq 325 | awk -f delete-lines.awk -

两种情况下的输出(省略第 1 至 274 行):

.
.
.
275
276
281
282
283
284
285
286
287
288
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
323
324
325

答案2

您可以尝试使用正则表达式sed

sed '/^[0-9]*$/d' filename.txt

这将从文件中删除仅包含数字的行。

以下 Perl 脚本将删除n从文件中取出第 - 行input.txt,并将其余内容输出到stdout。可以在 中指定行号line_numbers.txt

#!/usr/bin/perl

my @lines_to_exclude;

open(my $fh_line_numbers, "<", "line_numbers.txt") or die "Failed to open file: $!\n";
while(<$fh_line_numbers>) { 
  chomp; 
  push @lines_to_exclude, $_;
} 
close $fh_line_numbers;

my $linecounter = 1;

open (my $fh_datafile, '<', 'input.txt') or die "Cannot open $filename: $!";

while ( my $line = <$fh_datafile> ) {

  if ( ! ( $linecounter ~~ @lines_to_exclude ) ) {
    print $line;
  }

  $linecounter++;
}

close($fh_datafile);

(该~~运算符仅在 perl >= 5.10 中可用)

答案3

这可能对你有用(GNU sed):

sed 's/.*/&d/' line-numbers-to-delete-file | sed -f - file-to-be-shortened

从包含要删除的行的文件生成一个脚本,并使用您想要缩短的文件作为输入将其提供给 sed 实例。

答案4

请注意,除了尝试 2 中的附加代码外,所有代码实际上都与 OP 要求的相反。正如您在尝试 2 中看到的那样,调整命令很容易。

我有一个文本文件,里面有 1.108.752 行,大小约为 83 MB。我想从中获取 46.744 行,范围从第 15 行到第 1.108.716 行,即平均每 24 行。

总结;

第二次尝试比第一次更快。第三次尝试仅适用于较少的行。

第一次尝试(糟糕)

对于我想要的每一行,sed从文本文件的开头读取行,但不打印它们 ( -n)。当它到达我想要的行时,打印它 ( p),然后退出 ( q),而不是读到文件末尾。然后对下一个行号再次执行此操作。

显然,每次运行都会花费更长的时间,因为sed每次都要经过比以前更多的行。

如果我计算正确的话,在我的例子中,总共需要对文本文件进行 307332472188 次传递。天哪。

请注意,对于这种方法,linenumbers 文件中的行顺序无关紧要:

while read line; do
    sed -n "${line}{p;q}" "${INFILE}"
done

计时结果:2568.80s user 256.10s system 92% cpu 51:00.37 total。不好。

第二次尝试(更好)

这将从文件中读取行号并附加p(同样,用于打印此行)。此字符串通过管道传输到下一个sed,它从文件(-f)读取,这里STDIN写为-,每次都是第一个 的输出sed,实际上是要打印的行号:

sed 's/$/p/' "${LINENUMS}" | sed -n -f - "${INFILE}"

计时结果:146.54s user 0.18s system 100% cpu 2:26.70 total。非常好!

如果你想不是打印行文件中的行(就像OP想要做的那样),稍微改变命令,以便行号被d选举而不是打印,并打印所有其他行而不是删除它们(-n):

sed 's/$/d/' "${LINENUMS}" | sed -f - "${INFILE}"

第三次尝试(更糟糕)

这对我来说根本不起作用,因为我要提取的行太多了。虽然它应该适用于(少得多)的行,但我不知道限制是多少。

我尝试为 sed 创建一个长字符串,我预计这将导致sed只浏览文件一次(!),除了字符串中的行号之外不打印任何内容:

sed -n "12p;15p;24p;345p;...;12345;" ${INFILE}"

但这会产生一个420076长度约为 字符的字符串,将其输入进去sed后只会得到sed: Argument list is too long。这是可以理解的。

相关内容