2013-02-06 7 views
2

У меня есть два текстовых файлов text1.txt и text2.txt как нижеКак сравнить два текстовых файла и удалить соответствующее содержимое и перейти к выходу в perl?

text1

ac 
    abc 
    abcd 
    abcde 

text2

ab 
    abc 
    acd 
    abcd 

выход

ac 
abcde 

Мне нужно сравнить два файла и удалить содержимое с text1, когда есть совпадение во втором файле.

Я хочу код в Perl. В настоящее время я пытаюсь использовать приведенный ниже код.

#!usr/bin/perl 
use strict; 
use warnings; 

open (GEN, "text1.txt") || die ("cannot open general.txt"); 
open (SEA, "text2.txt") || die ("cannot open search.txt"); 
open (OUT,">> output.txt") || die ("cannot open intflist.txt"); 
open (LOG, ">> logfile.txt"); 

undef $/; 
foreach (<GEN>) { 

    my $gen = $_; 
    chomp ($gen); 
    print LOG $gen; 

    foreach (<SEA>) { 

    my $sea = $_; 
    chomp($sea); 
    print LOG $sea; 

    if($gen ne $sea) { 
     print OUT $gen; 
    } 
    } 
} 

В этом я получаю все сообщения от text1, не имеет себе равное содержание. Пожалуйста, помогите мне.

+0

Матч в позиции (номер строки) или в любом месте? – user1126070

ответ

1

Я думаю, что вы должны прочитать текст2 в массиве, а затем во втором foreach на этом массиве использовать массив.

@b = <SEA>; 

Или еще во втором цикле указатель файла будет в конце уже

1

Один из способов:

#!/usr/bin/perl 
use strict; 
use warnings; 

$\="\n"; 

open my $fh1, '<', 'file1' or die $!; 
open my $fh2, '<', 'file2' or die $!; 
open my $out, '>', 'file3' or die $!; 

chomp(my @arr1=<$fh1>); 
chomp(my @arr2=<$fh2>); 

foreach my $x (@arr1){ 
     print $out $x if (!grep (/^\Q$x\E$/,@arr2)); 
} 

close $fh1; 
close $fh2; 
close $out; 

После выполнения выше, файл «file3» содержит:

$ cat file3 
ac 
abcde 
+1

Нет смысла закрывать файлы в конце программы, если вы не хотите обрабатывать какие-либо ошибки. Perl будет убирать для вас. – Borodin

+0

Работает только в том случае, если файлы достаточно малы, чтобы вписаться в память. – user1126070

+0

Я пробовал этот подход. Я не могу удалить строки со специальными символами, например, у меня есть строка с потерянной + найденной в обоих текстовых файлах, чтобы она не приходила в выходной файл, но это не так. кроме этого каждая вещь работает нормально благодаря @guru – Ramesh

1

Это мой план:

  1. Прочитайте содержимое первого файла в хэше с помощью счетчика вхождений. Например, работая с вашими данными вы получаете:

    %lines = ('ac' => 1, 
        'abc' => 1, 
        'abcd' => 1, 
        'abcde' => 1); 
    
  2. Прочитайте второй файл, удалив предыдущие хэш% строк, если ключ существует.

  3. Распечатайте ключи %lines в требуемом файле.

Пример:

use strict; 

open my $fh1, '<', 'text1' or die $!; 
open my $fh2, '<', 'text2' or die $!; 
open my $out, '>', 'output' or die $!; 
my %lines =(); 

while(my $key = <$fh1>) { 
    chomp $key; 
    $lines{$key} = 1; 
} 

while(my $key = <$fh2>) { 
    chomp $key; 
    delete $lines{$key}; 
} 

foreach my $key(keys %lines){ 
    print $out $key, "\n"; 
} 

close $fh1; 
close $fh2; 
close $out; 
+0

Я понимаю ваш план, но я новичок в perl, что для хэшей, можете ли вы предоставить код, если это возможно, спасибо за вашу идею. – Ramesh

+0

Я не получаю желаемый результат для этого кода – Ramesh

1

Ваша главная проблема заключается в том, что вы не определен входной разделитель записей $/. Это означает, что файл будет считан как одна строка, и все, что вы можете сделать, это сказать, что эти два файла отличаются друг от друга.

Удалить undef $/, и все будет работать намного лучше. Однако внутренний цикл for будет читать и распечатывать все строки в file2, которые не соответствуют первой строке file1. Во второй раз, когда этот цикл встречается, все данные были прочитаны из файла, поэтому тело цикла не будет выполнено вообще.Вы должны либо открыть file2 внутри внешнего цикла, либо прочитать файл в массив и перебрать его.

Опять же, вы действительно хотите напечатать все строки от file2, которые не равны каждой строке в file1?

Update

Как я уже писал в своем комментарии, это звучит, как вы хотите вывести строки в text1, которые не появляются в любом месте в text2. Это легко достигается с помощью хэш:

use strict; 
use warnings; 

my %exclude; 

open my $fh, '<', 'text2.txt' or die $!; 
while (<$fh>) { 
    chomp; 
    $exclude{$_}++; 
} 

open $fh, '<', 'text1.txt' or die $!; 
while (<$fh>) { 
    chomp; 
    print "$_\n" unless $exclude{$_}; 
} 

С данными вы показываете в вашем вопросе, который производит этот выход

ac 
abcde 
+0

на самом деле я хочу напечатать строки из файла1, которые не равны file2. Я понимаю, что за $/seperator спасибо за это. – Ramesh

+0

@Ramesh: Если вы понимаете о '$ /', то почему вы читаете файлы в циклах 'for', которые будут выполняться только один раз? – Borodin

+0

@Ramesh: Ваша цель * «напечатать строки из файла1, которые не равны файлу2», требует лучшего определения. Если 'file1' содержит' aa \ nbb \ n', а file2 содержит 'bb \ naa \ n', то какой результат вы хотите? – Borodin

0

Я хотел бы посмотреть вашу проблему так:

  • У вас есть набор S строк в file.txt.
  • У вас есть набор F запрещенных строк в forbidden.txt.
  • Вы хотите, чтобы строки были разрешены, поэтому S \ F (setminus).

Существует структура данных в Perl, который реализует набор строк: хэш. (Он также может отображаться в скалярах, но это вторично).

Итак, сначала мы создаем набор линий, которые у нас есть. Мы выпускаем все строки в этом файле карте undef, так как нам не нужно это значение:

open my $FILE, "<", "file.txt" or die "Can't open file.txt: $!"; 
my %Set = map {$_ => undef} <$FILE>; 

Мы создаем запрещенный набор одинаково:

open my $FORBIDDEN, "<", "forbidden.txt" or die "Can't open forbidden.txt: $!"; 
my %Forbidden = map {$_ => undef} <$FORBIDDEN>; 

Множество минус работает как либо из этих способов:

  • для каждого элемента х в S, x находится в результирующем наборе R iff x не находится в F.

    my %Result = map {$_ => $Set{$_}} grep {not exists $Forbidden{$_}} keys %Set; 
    
  • В результате установлено R изначально S.Для каждого элемента в F, мы удалить этот пункт из R:

    my %Result = %Set; # make a copy 
    delete $Result{$_} for keys %Forbidden; 
    

(функция keys обращается элементы в наборе строк)

Мы можем затем распечатать все ключи: print keys %Result.

Но что, если мы хотим сохранить заказ? Записи в хеше также могут нести связанное значение, так почему бы не номер строки? Мы создаем множество S так:

open my $FILE, "<", "file.txt" or die "Can't open file.txt: $!"; 
my $line_no = 1; 
my %Set = map {$_ => $line_no++} <$FILE>; 

Теперь это значение переносится вокруг со строкой, и мы можем получить доступ к нему в конце. В частности, мы сортируем ключи в хэш после номера строки:

my @sorted_keys = sort { $Result{$a} <=> $Result{$b} } keys %Result; 
print @sorted_keys; 

Примечание: Все это предполагает, что файлы заканчиваются символом новой строки. Кроме того, вам нужно будет chomp.

+0

Я пробовал это, но я не получаю все результаты, у меня есть около 658 файлов, на которых должно отображаться 609, но я получаю только 382 строки. пожалуйста, помогите мне – Ramesh

+0

@Ramesh Конечно, но вам нужно будет показать мне точный код, который не удался, а также некоторый ввод, который заставляет его потерпеть неудачу. Я не могу магически угадать ваши проблемы на полпути по всему миру ... Пожалуйста, обновите свой вопрос соответственно. – amon

+0

Я добавил код выше @amon – Ramesh

Смежные вопросы