Как удалить строки, которые соответствуют элементам из другого файла

Я нахожусь в процессе изучения Perl и пытаюсь понять, как выполнить эту задачу. У меня есть папка с кучей текстовых файлов, и у меня есть файл ions_solvents_cofactors который содержит список из трех букв.

Я написал скрипт, который открывает и читает каждый файл в папке и должен удалить те строки, которые в определенном столбце [3] соответствуют некоторому элементу из списка. Это не работает хорошо. У меня есть проблема в конце сценария, и я не могу понять, что это такое.

Я получаю ошибку: rm: invalid option -- '5'

Мой входной файл выглядит так:

ATOM   1592 HD13 LEU D  46      11.698 -10.914   2.183  1.00  0.00           H  
ATOM   1593 HD21 LEU D  46      11.528  -8.800   5.301  1.00  0.00           H  
ATOM   1594 HD22 LEU D  46      12.997  -9.452   4.535  1.00  0.00           H  
ATOM   1595 HD23 LEU D  46      11.722  -8.718   3.534  1.00  0.00           H  
HETATM 1597  N1  308 A   1       0.339   6.314  -9.091  1.00  0.00           N  
HETATM 1598  C10 308 A   1      -0.195   5.226  -8.241  1.00  0.00           C  
HETATM 1599  C7  308 A   1      -0.991   4.254  -9.133  1.00  0.00           C  
HETATM 1600  C1  308 A   1      -1.468   3.053  -8.292  1.00  0.00           C 

Вот сценарий:

#!/usr/bin/perl -w

$dirname = '.';
opendir( DIR, $dirname ) or die "cannot open directory";
@files = grep( /\.txt$/, readdir( DIR ) );

foreach $files ( @files ) {

    open( FH, $files ) or die "could not open $files\n";
    @file_each = <FH>;
    close FH;

    close DIR;

    my @ion_names = ();

    my $ionfile   = 'ions_solvents_cofactors';
    open( ION, $ionfile ) or die "Could not open $ionfile, $!";
    my @ion = <ION>;
    close ION;

    for ( my $line = 0; $line <= $#file_each; $line++ ) {

        chomp( $file_each[$line] );
        if ( $file_each[$line] =~ /^HETATM/ ) {
            @is = split '\s+', $file_each[$line];
            chomp $is[3];
        }

        foreach ( $file_each[$line] ) {    #line 39

            if ( "@ion" =~ $is[3] ) {
                system( "rm $file_each[$line]" );
            }
        }
    }
}

Так, например, если 308 из входного файла совпадений в файле ion_cofactors_solvents`, затем удалите все эти строки, в которых он совпадает.

1 ответ

Решение

Я бы использовал Tie::File модуль, который позволяет tie массив для модуля, так что любые изменения, которые вы вносите в массив, отражаются в файле

Я использовал glob найти все .txt файлы, с возможностью :bsd_glob чтобы поддерживать пробелы в путях к файлам

Первая задача - создать хеш %matches который отображает все значения в ions_solvents_cofactors 1. Это упрощает проверку файлов PDB на требуемые значения

Тогда это просто вопрос использования tie на каждой .txt файл и тестирование каждой строки, чтобы увидеть, представлено ли значение в столбце 4 в хэше

Я использую переменную $i индексировать в @file массив, который отображает файл на диске. Если совпадение найдено, элемент массива удаляется с помощью splice @file, $i, 1, (Это естественно оставляет $i индексирование следующего элемента в последовательности без увеличения $i.) Если совпадения нет $i увеличивается для индексации следующего элемента массива, оставляя строку на месте

use strict;
use warnings 'all';

use File::Glob ':bsd_glob';
use Tie::File;

my %matches = do {
    open my $fh, '<', 'ions_solvents_cofactors.txt';
    local $/;
    map { $_ => 1 } split ' ', <$fh>;
};

for my $pdb ( glob '*.txt' ) {

    tie my @file, 'Tie::File', $pdb or die $!;

    for ( my $i = 0; $i < @file; ) {

        next unless my $col4 = ( split ' ', $file[$i] )[3];

        if ( $matches{$col4} ) {
            printf qq{Removing line %d from "%s"\n},
                    $i+1,
                    $pdb;
            splice @file, $i, 1;
        }
        else {
            ++$i;
        }
    } 
}
Другие вопросы по тегам