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

Я изучаю 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 из входного файла совпадает в файле ions_cofactors_solvents`, то удалите все эти строки, в которых он совпадает.


person milan    schedule 09.01.2018    source источник
comment
Вам бы очень помогло, если бы вы правильно изложили свою программу, а это только вежливо, когда вы просите бесплатную помощь с вашим кодом. Пожалуйста, исправьте это: это практически нечитаемо.   -  person Borodin    schedule 09.01.2018
comment
Спасибо за полезные комментарии. Я не использую какой-либо текстовый редактор, что плохо. я изменил это   -  person milan    schedule 09.01.2018
comment
Спасибо за попытку улучшить макет. Однако это ухудшило ситуацию, по крайней мере, удалив важную идентификацию строки 39.   -  person Yunnosch    schedule 09.01.2018
comment
Если вы продолжите редактирование, пожалуйста: 1) сделайте отступ не более чем на 4 символа 2) используйте пробелы, а не табуляции (или, по крайней мере, используйте и то, и другое последовательно) 3) не более одной пустой строки за раз, и то только по уважительной причине.   -  person Yunnosch    schedule 09.01.2018
comment
Хорошо, спасибо. Я просто хотел облегчить чтение, добавив больше пробелов и табуляций, вот и все.   -  person milan    schedule 09.01.2018
comment
Не могли бы вы дважды проверить, что код, показанный сейчас (после многих правок), по-прежнему является кодом, который вызывает описанную вами ошибку?   -  person Yunnosch    schedule 09.01.2018
comment
Это не дает мне ту же ошибку, теперь выдает это предупреждение: Global symbol "$files" requires explicit package name at select_pdb_filesREAL.pl line 11. Хотя после запуска скрипта он не вносил никаких изменений и не удалял строки в файлах.   -  person milan    schedule 09.01.2018
comment
Пожалуйста, объясните, как вы понимаете строку 39 foreach($file_each[$line]). Какой список обрабатывать? Какова переменная для каждой записи списка? Сколько записей в списке?   -  person Yunnosch    schedule 09.01.2018
comment
Вы отредактировали use stricts; в коде. Теперь вам предстоит работать с этим. Вы должны сохранить его, исправить ошибки, которые он вызывает (например, добавив несколько стратегических my), а затем вернуться с очищенным кодом, который снова выдает первоначальную ошибку. Или, может быть, попытка исправить это. Тогда, пожалуйста, ответьте сами или оставьте вопрос удаленным.   -  person Yunnosch    schedule 09.01.2018
comment
С вашим последним комментарием ваш вопрос теперь может получить несколько голосов за невоспроизводимую ошибку, возможно, против. Так что лучше удалите вопрос сейчас, отладьте, протестируйте, отредактируйте, а затем восстановите.   -  person Yunnosch    schedule 09.01.2018
comment
Я разделил каждую строку, начинающуюся с ^HETATM, и взял только четвертый элемент is[3], но я не помещал эти элементы в массив. Я хотел сказать, что для каждой строки, которая начинается с ^HETATM, посмотрите, совпадают ли элементы is[3] в каждой строке с тем, что находится в массиве @ion, и для тех, кто удаляет целые строки из входных файлов. Я также пытался поместить все is[3] из каждой строки в массив и, а затем использовать этот массив в цикле foreach, но у меня была та же проблема.   -  person milan    schedule 09.01.2018
comment
Ок, так и сделаю..   -  person milan    schedule 09.01.2018
comment
Вы не можете удалить строки из файла, используя rm! Пожалуйста, приведите пример содержания ions_solvents_cofactors.   -  person Borodin    schedule 09.01.2018
comment
Очень плохая идея объявлять каждую переменную глобально в начале программы. Объявление должно быть как можно ближе к первому вхождению переменной.   -  person Borodin    schedule 09.01.2018
comment
Вы хотите удалить строки из исходных файлов PDB или хотите сохранить резервную копию?   -  person Borodin    schedule 09.01.2018
comment
Я думал, что если использовать команду system или ``, я могу сделать это так же, как команду bash. Но я, наверное, ошибаюсь. Содержимое ions_solvents_cofactors представляет собой список различных букв, которые выглядят следующим образом: SB SN IN 008 03S 06C 0KA 0NG 0NM Итак, если в этом списке найдено 308, я хотел удалить все эти строки, в которых встречается 308.   -  person milan    schedule 09.01.2018
comment
Спасибо за подсказку об объявлении переменной, я не настолько опытен, так что это очень полезно. Это файлы txt, и это не имеет значения, я могу перезаписать исходный файл txt или создать новый и скопировать в другую папку.   -  person milan    schedule 09.01.2018
comment
Какую операционную систему ты используешь?   -  person Borodin    schedule 09.01.2018
comment
Я использую центос 6.9   -  person milan    schedule 09.01.2018
comment
@milan: Ваш файл PDB не соответствует < i>Protein Data Bank формат, поэтому мои решения просто разбивают каждую строку на пробелы. Я предполагаю, что вы хотите сопоставить поле Имя атома?   -  person Borodin    schedule 09.01.2018
comment
@milan: с возможностью ошибок по одному, я предполагаю, что вы имеете в виду столбец, который содержит LEU и 308 в вашем образце?   -  person Borodin    schedule 09.01.2018
comment
@milan: Вы почти никогда не хотите split /\s+/, и если вы уверены в этом, вам следует добавить комментарий к коду, чтобы объяснить себя. split ' ' наверное правильно. Это также значение по умолчанию, поэтому, если вы оставите запись файла в $_, вы можете сказать просто my @fields = split.   -  person Borodin    schedule 09.01.2018


Ответы (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;
        }
    } 
}
person Borodin    schedule 09.01.2018
comment
Я никогда не использовал какой-либо модуль, я думаю, что если они не встроены, я могу легко их установить. Спасибо за Ваше объяснение. Это похоже на суперорганизованный код, по Вашему описанию я его хорошо понял. Я запущу его завтра утром, потому что я на Дальнем Востоке, пора спать. Я надеюсь, что он удаляет целые строки всякий раз, когда col4 соответствует элементу из файла. Просто хотел спросить Вас, в моем предыдущем коде я знал, что уровень навыков низкий, но как я мог изменить эту строку удаления в perl, чтобы она также работала? Просто чтобы знать в будущем, если я столкнусь с такой же задачей. - person milan; 09.01.2018
comment
@milan: Tie::File был добавлен в ядро ​​​​Perl с версией 5.7.3 в марте 2002 года. Если ваш Perl старше, вам действительно следует подумать об его обновлении, иначе мой код должен работать как есть. В стандартном Perl доступно множество полезных модулей, но одним из самых больших преимуществ Perl является обширная сеть архивов Perl. (CPAN), где обычные люди, такие как вы и я, могут отправлять код, который может быть полезен другим. Стандартные инструменты командной строки, такие как sed и awk, становятся бесполезными, если вы знаете perl. - person Borodin; 09.01.2018
comment
@milan: Надеюсь, он удаляет целые строки всякий раз, когда col4 соответствует элементу из файла Как я уже сказал, Tie::File отображает каждую строку текстового файла в элемент массива. Если вы прочтете документацию по splice, то увидите, что она удаляет элементы из массива. . Это означает, что он также удалит строку из файла PDB. Но вы должны всегда выполнять программу до конца. Если вы прервете его, файл, вероятно, будет непригоден для использования. - person Borodin; 09.01.2018
comment
"bsd_glob" is not defined in %File::Glob::EXPORT_TAGS at script_coord.pl line 5 main::BEGIN() called at /usr/share/perl5/Carp.pm line 5 eval {...} called at /usr/share/perl5/Carp.pm line 5 Can't continue after import errors at script_coord.pl line 5 Это ошибка, которую я получил при попытке запустить скрипт. Я искал в Интернете, как определить bsd_glob в Centos, но не смог найти. Я использую Perl 5.10, поэтому думаю, что это не проблема. - person milan; 10.01.2018
comment
Я попытался запустить его, удалив bsd_glob, он работает, но все еще удаляет некоторые строки, которые в 4-м столбце не имеют элемента, соответствующего файлу elements from ion_solvent_cofactor. Однако он должен искать только col4 тех строк, которые начинаются с ^HETATM. Итак, я запустил его на каком-то .txt, в котором нет ^HETATM, и он все еще удалял некоторые строки. Он также удаляет строки, когда я запускал его в файлах, в которых в 4-м столбце ^HETATM нет элементов, соответствующих одному из списка. Например, в одном файле нужно удалить всего 4 строки, а удаляется около 3000 строк, что странно. - person milan; 10.01.2018
comment
@milan: В вашем вопросе об этом ничего не упоминается. Сократите вашу проблему до небольшой выборки данных, с которыми мы можем работать, и задайте новый вопрос, показав как ваш файл PDB, так и файл списка. И у вас должно быть :bsd_glob, а не bsd_glob. - person Borodin; 10.01.2018
comment
Да, ты прав! Я полностью забыл упомянуть об этом в вопросе. Простите за это. Я открою новый вопрос и уточню, в чем проблема, если он не работает со скриптом, который Вы написали. В любом случае, если он также читает строки, начинающиеся с ^ATOM, это не должно быть проблемой, потому что под col[4] из ^ATOM не должно быть элементов, совпадающих с содержимым ions_solvents_cofactors файла. - person milan; 10.01.2018
comment
@milan: Если вы добавите к этому вопросу файл списка, который не дает правильных результатов с PDB, который вы уже показали, мы можем избежать совершенно нового вопроса. Проблема, несомненно, в том, что вы не полностью указали требование. - person Borodin; 10.01.2018