我正在使用包含如下格式的数据的文本文件:
To Kill A Mocking Bird|Harper Lee|S1|4A
Life of Pi|Yann Martel|S3|5B
Hunger Games|Suzzanne Collins|S2|2C
实际数据文件包含更多条目,S1
个实例超过3个。
我正在用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#,是否有任何语法我犯了错误,或者是逻辑错误?
答案 0 :(得分:1)
有很多方法可以做到这一点,其中一些方法包括正则表达式,另一些方法则没有。如果您寻找的字段是文件的第3个和第4个,并且您的文件具有标准结构,则可以像这样完成
编辑:
文件不一致,所以请改用正则表达式。
还删除了@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);
答案 1 :(得分: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";
}
}
参考文献有点难以理解,但我希望您能够看到能够将您的所有书籍信息存储在单一数据结构中的能力。