2013-11-07 3 views
0

Я работаю с текстовым файлом, который содержит данные в формате, как так:Perl - Чтение два файла сравнить содержимое

To Kill A Mocking Bird|Harper Lee|S1|4A 
Life of Pi|Yann Martel|S3|5B 
Hunger Games|Suzzanne Collins|S2|2C 

Фактический файл данных имеет много больше записей, и есть более 3-х экземпляров от S1.

Я пишу программу в Perl для сравнения данных в этом файле с другим файлом, главным образом с информацией о подаче, например S1, 4A.

Я подошел к этому, предварительно сохранив данные из файла в строку. Затем я разделил строку, используя канал | в качестве разделителя и сохранил его в массиве. Затем я использовал цикл foreach для итерации по массиву, чтобы найти соответствующую информацию.

Обратите внимание, что все файлы находятся в одном каталоге.

#!/usr/bin/perl 

open(INFO, "psychnet3.data"); 
my $dbinfo = <INFO>; 
close(INFO); 

@dbarray = split("|", $dbinfo); 
$index_counter = 0; 

foreach $element (@dbarray) { 

    if ($element =~ "S1") { 
    open(INFO, ">>logfile.txt"); 
    print INFO "found a S1"; 
    close(INFO); 

    if ($dbarray[$index_counter + 1] =~ "4A") { 
     $counter++; 
     open(INFO, ">>logfile.txt"); 
     print INFO "found S1 4A"; 
     close(INFO); 
    } 
    } 
    $index_counter++; 
} 

В выходной файл, он не находит все экземпляры S1.

Я также пробовал использовать eq в качестве условного вместо =~ и до сих пор не повезло.

Я новичок в Perl, исходя из C#, есть ли какой-либо синтаксис, с которым я совершаю ошибку, или это логическая ошибка?

+1

'my $ dbinfo = ;' это будет читать только первую строку, поэтому будет дано только первый экземпляр. вам нужно сохранить в массиве, чтобы получить полный файл. 'my @dbinfo = ;' и может понадобиться другой цикл. Это всего лишь намек. Исходный код можно оптимизировать несколькими способами. – jkshah

+0

Пожалуйста, покажите содержимое 'psychnet3.data'. Это всего лишь одна строка? – Borodin

+0

Что вы ожидаете от '@ dbarray'? – Borodin

ответ

1

Существует немало способов сделать это, некоторые из которых включают регулярные выражения, а некоторые другие - нет. Если поля вы стремитесь это единственный 3-го и 4-го файла и файлы имеют стандартную структуру, то это может быть сделано как этот

EDIT:

Файл не так последовательны, поэтому использовать regex вместо этого.

Также удален массив @dbinfo. Это не обязательно, и память не бесплатно :)

(не забудьте изменить имя дескриптора файла, чтобы избежать конфликта с внутренним контуром с дескрипторов файлов таким же именем)

open(MINFO, "psychnet3.data"); 
while (my $line = <MINFO>) { 
    if ($line =~ m/\|S1/i) { 
     open(INFO, ">>logfile.txt"); 
     print INFO "found a S1"; 
     close(INFO); 

     $line =~ m/\|4A/i 
      $counter++; 
      open(INFO, ">>logfile.txt"); 
      print INFO "found S1 4A"; 
      close(INFO); 
     } 
    } 
} 
close(<MINFO); 
+0

фактический файл, содержащий данные, не очень согласован, поэтому требуемые данные не всегда появляются с постоянным индексом. – e2chaoeng

+0

Я отредактировал свой ответ, чтобы использовать регулярное выражение. Например. Первое регулярное выражение будет соответствовать 'S1' только тогда, когда предел будет выполняться в' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' Также это регулярное выражение нечувствительно к регистру (флаг 'i') – foibs

+0

спасибо человеку! понял :-) – e2chaoeng

0

Вы не говоря уже о том, как вы сравнить эти данные. Это делается по названию книги? Или это делается автором? Это затрудняет понимание того, как эта информация должна быть сохранена.

Ваши данные немного сложнее, чем хранение отдельных фрагментов данных. Это означает, что структуры данных Perl по умолчанию, скаляр ($foo), массив (@foo) и хэш (%foo) просто не будут его обрезать. Пришло время узнать о references.

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

$ref_to_foo_array = \@foo; 

$ref_to_foo_array это место в памяти, где мой @foo массив хранится.Большим преимуществом является обращение к целому массиву значений, теперь я имею в виду одно значение: Место в памяти, где хранится @foo. Это означает, что я могу поставить эту информацию в массив или хэш:

$bar[0] = $ref_to_foo_array; 
$bar[1] = $ref_to_some_other_array; 

Теперь @bar не только хранить два значения. Вместо этого @bar хранит информацию в двух массивах! У меня есть массив массивов !.

Чтобы получить обратно свой исходный массив, я просто разыменовать его, поставив правильный сигилу перед моим Справочно:

@foo = @{ $bar[0] }; 

Чтобы сделать вещи проще, я могу использовать -> как средство разыменовании одно значение:

$array_reference = $bar[0]; 
$array_reference->[0]; # First item in the array being referenced 
$array_reference->[1]; # Second item 

конечно, я мог бы сделать это тоже:

$bar[0]->[0] # First item in the array being referenced 

Так что же все это делать? Часы:

use strict; 
use warnings; 
use autodie; 
use feature qw(say); 

use constant { 
    BOOK_FILE => 'psychnet3.data', 
}; 

open my $book_fh, "<", BOOK_FILE; 

my %book_hash; 
for my $book (<$book_fh>) { 
    chomp $book; 
    my ($title, $author, $section, $shelf) = split /\s*\|\s*/, $book; 

    my $temp_book_hash; 
    $temp_book_hash{AUTHOR} = $author; 
    $temp_book_hash{SECTION} = $section; 
    $temp_book_hash{SHELF} = $shelf; 

    $book_hash{$title} = \$temp_book_hash; 
} 

У меня есть %temp_book_hash, заклиненный по названию книги. Тем не менее, этот единственный хэш хранит автора, раздел и «я», где хранится эта книга. Каждая книга имеет три разных бита информации, связанных с ней, но я могу хранить всю эту информацию в единой структуре данных. Нет необходимости хранить параллельные массивы или хеши.

Как получить эту информацию? Простой:

my $title = "To Kill a Mockingbird"; 
my %temp_book_hash = %{ $book_hash{$title} }; 
say "The book $title was written by $temp_book_hash{AUTHOR}"; 

По разыменования Хэш я хранится в $book_hash{$title}, я могу вытащить имя автора, и подача информации.

Синтаксис немного неуклюжий. Я постоянно делаю временные переменные, чтобы передавать информацию туда и обратно. К счастью, Perl позволяет мне пропустить этот шаг. Вот тот же цикл, как и раньше:

for my $book (<$book_fh>) { 
    chomp $book; 
    my ($title, $author, $section, $shelf) = split /\s*\|\s*/, $book; 

    $book_hash{$title} = {}; # Line not necessary 

    $book_hash{$title}->{AUTHOR} = $author; 
    $book_hash{$title}->{SHELF} = $shelf; 
    $book_hash{$title}->{SECTION} = $section; 
} 

Вместо того, что временный хэш, я могу хранить дату непосредственно в мой самый внешний хэш. Этот синтаксис намного короче и чище. И это легче понять.

Линия $book_hash{$title} = {}; заявляет, что $book_hash{$title} будет хранение хэша-ссылки, а не какая-то стандартная строки или номера. Эта строка не нужна вообще. Perl выяснит, что вы храните хеш-ссылку с $book_hash{$title}->{AUTHOR} = $author;. Тем не менее, мне нравится _declare мое намерение, что я храню ссылку в этой переменной. Таким образом, если дальше в моей программе есть $book_hash{$title} = $author;, другой разработчик узнает, что я допустил ошибку.

я могу использовать тот же -> нотацию, чтобы вытащить информацию из моей книги, без необходимости создания временных переменных тоже:

my $title = "To Kill a Mockingbird"; 
say "The book $title was written by " . $book_hash{$title}->{AUTHOR}; 

Вы упомянули, что вы сравниваете два файла.Представьте себе, что я храню первый в %book_hash, а второй - в $book_hash2. Я могу пройти через мои книги и посмотреть, какие из них некорректно отложены.

for my $title (keys %book_hash) { 
    if ($book_hash{$title}->{SHELF} ne $book_hash2{$title}->{SHELF}) { 
     say "The book $title is stored on two different shelves!" 
    } 
    else { 
     say "The book $title is on the correct shelf"; 
    } 
} 

Ссылки немного трудно понять, но я надеюсь, что вы можете увидеть силу быть в состоянии хранить всю информацию о вашей книге в одной структуре данных.

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