【问题标题】:perl to remove lines from fileperl 从文件中删除行
【发布时间】:2014-05-01 18:12:50
【问题描述】:

我的文件看起来像:

ATOM 2517 O   VAL 160 8.337  12.679  -2.487
ATOM 2518 OXT VAL 160 7.646  12.461  -0.386
TER 
ATOM 2519 N   VAL 161 -14.431  5.789 -25.371
ATOM 2520 H1  VAL 161 -15.336  5.698 -25.811
ATOM 2521 H2  VAL 161 -13.416 10.529  17.708
ATOM 2522 H3  VAL 161 -14.363  9.436  18.498
ATOM 2523 CA  VAL 161   4.400  9.233  16.454
ATOM 2524 HA  VAL 161   3.390  9.170  16.047

我必须删除“TER”、“TER”之前的行和 TER 之后的 3 行,并使文件像这样连续:

ATOM 2517 O   VAL 160   8.337 12.679  -2.487
ATOM 2519 N   VAL 161 -14.431  5.789 -25.371
ATOM 2523 CA  VAL 161   4.400  9.233  16.454
ATOM 2524 HA  VAL 161   3.390  9.170  16.047

【问题讨论】:

标签: perl bash


【解决方案1】:

一个简单的逐行脚本。

用法: perl script.pl -i.bak fileglob

例如perl script.pl -i.bak File*MINvac.pdb

这将更改原始文件,并使用扩展名 .bak 保存每个文件的备份。请注意,如果TER 行出现得太靠近文件末尾,则会导致警告。另一方面,其他解决方案也会如此。

如果您不想保存备份(请小心,因为更改是不可逆的!),请改用-i。

代码:

#!/usr/bin/perl
use v5.10;
use strict;
use warnings;

my $prev;
while (<>) {
    if (/^TER/) {
        print scalar <>;  # print next line
        <> for 1 .. 3;    # skip 3 lines
        $prev = undef;    # remove previous line
    } else {
        print $prev if defined $prev;
        $prev = $_;
    }
    if (eof) {  # New file next iteration?
        print $prev;
        $prev = undef;
    }
}

【讨论】:

  • @Sinan Ünür:您编辑了我的答案,使其无法在外部文件中使用。虽然这对于有经验的用户来说可能看起来更好,但它不适用于 OP 关于如何编辑现有文件的问题。
  • 我支持可以复制、粘贴和运行代码的答案。当然,如果您不喜欢,您可以随时恢复编辑。
  • @SinanÜnür 您可以复制/粘贴我之前的代码,假设您添加了一个 shebang,并且知道为脚本提供文件名参数,并重定向输出。我想事后看来,这是我对 OP 所做的一些假设。
  • 是的。如果您查看 OP 对 Zaid 的评论,即使是现成的脚本也不够。
  • @SinanÜnür 新的独立版本,适用于多文件使用。
【解决方案2】:

我意识到我应该用 Perl 编写它,但现在我已经用 Python 编写了它。无论如何我都会发布它,因为它可能会被证明是有用的,看不出有什么害处。

#!/usr/bin/python2.7
import sys
import glob
import os

try:
    dir = sys.argv[1]
except IndexError:
    print "Usage: "+sys.argv[0]+" dir"
    print "Example: "+sys.argv[0]+" /home/user/dir/"
    sys.exit(1)

for file in glob.glob(os.path.join(dir, 'File*_*MINvac.pdb')):
    fin = open(file, "r")
    content = fin.readlines()
    fin.close()

    for i in range(0, len(content)):
        try:
            if "TER" in content[i]:
                del content[i]
                del content[i-1]
                del content[i:i+3]
        except IndexError:
            break
    fout = open(file, "w")
    fout.writelines(content)
    fout.close()

编辑:添加了对多个文件的支持,就像 OP 想要的那样。

【讨论】:

  • 如果我在所有 200 个文件上运行此程序,必须进行哪些更改?因为 1 个系统有 200 个文件并且有 120 个系统..
  • @kanika 你应该在第一篇文章中这么说。文件名是否正规?
  • 对此我很抱歉.. 是的文件被命名为 File10_0MINvac.pdb...File10_60MINvac.pdb....即文件--_--MINvac.pdb 在所有文件中通用。
  • @kanika 我已经编辑了我的答案,如果文件的名称中包含模式 File*_*MINvac.pdb,它现在应该可以处理目录中的多个文件。据我所知,我还尝试了几个文件。
  • @kanika 如果您收到有效的答案,请将其标记为答案,以便其他人可以轻松找到它。我发现您在之前的任何问题中都没有这样做过,即使您之前已经收到过答案。
【解决方案3】:

因此,对于每组 6 行连续的行,您要丢弃除第三行之外的所有行如果第二行是 TER?

TIMTOWTDI,但这应该可行:

my @queue;
while (<>) {
    push @queue, $_;
    @queue = $queue[2]  if @queue == 6 and $queue[1] =~ /^TER$/;
    print shift @queue  if @queue == 6;
}
print @queue;  # assume no TERs in last 4 lines

【讨论】:

    【解决方案4】:
    use strict;
    use warnings;
    use Tie::File;
    
    my @array;
    
    tie @array, 'Tie::File', 'myFile.txt' or die "Unable to tie file";
    
    my %unwanted = map  { $_ => 1 }                # Hashify ...
                   map  { $_-1, $_, $_+2 .. $_+4 } # ... the five lines ...
                   grep { $array[$_] =~ /^TER/ }   # ... around 'TER'  ...
                   0 .. $#array ;                  # ... in the file
    
    # Remove the unwanted lines
    @array = map { $array[$_] } grep { ! $unwanted{$_} } 0 .. $#array;
    
    untie @array;  # The end
    

    【讨论】:

    • 谢谢..这运行完美。我有一个问题。如果我有大约 200 个文件,我如何附加编以在所有文件上运行..?
    • 虽然Tie::File 是一个优秀的模块,但这不是我推荐的解决方案。如果输入有很多匹配行,它将使用大量内存。@TLP 的解决方案具有可预测的内存开销,大致与最长行的长度成正比。另外,我猜它会更快,因为它以线性方式读取输入。
    猜你喜欢
    • 2014-07-27
    • 1970-01-01
    • 1970-01-01
    • 2012-09-24
    • 1970-01-01
    • 2013-02-19
    • 1970-01-01
    • 2018-05-17
    • 1970-01-01
    相关资源
    最近更新 更多