如何使用C或Perl将文件的所有元素与另一个文件的所有元素进行比较,以获得更大的数据?文件1包含100,000个这样的数字,文件2包含500,000个元素。
我在foreach中使用foreach来拆分数组中的每个元素。它在perl中运行得很好,但是从file1中的File2检查和打印每一列元素的时间是40分钟。有28个这样的专栏。
有没有办法减少时间或使用其他语言,如C?
0.1
0.11
0.12
0.13
0.14
0.15
0.16
0.17
0.18
0.19
0.2
0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 0.1 0.11 0.12 0.13 0.14 0.15 0.16 0.17 0.18 0.19 0.2 0.21 0.22 0.23 0.24 0.25 0.26 0.27 0.28
1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 1.1 1.11 1.12 1.13 1.14 1.15 1.16 1.17 1.18 1.19 1.2 1.21 1.22 1.23 1.24 1.25 1.26 1.27 1.28
如果文件2中的元素匹配则打印'列号',如果不打印'0'。
1 2 0 0 0 0 0 0 0 10 11 12 13 14 15 16 17 18 19 20 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
这是我正在使用的代码。注意:它在文件1中按列方式检查File2,如果 true ,则打印列号,如果 false ,则打印'0'。它将打印28个不同文件中每列的输出。
#!/usr/bin/perl-w
chomp($file = "File1.txt");
open(FH, $file);
@k_org = <FH>;
chomp($hspfile = 'file2.txt');
open(FH1, $hspfile);
@hsporg = <FH1>;
for $z (1 .. 28) {
open(OUT, ">$z.txt");
foreach (@hsporg) {
$i = 0;
@h_org = split('\t', $_);
chomp ($h_org[0]);
foreach(@k_org) {
@orginfo = split('\t', $_);
chomp($orginfo[0]);
if($h_org[0] eq $orginfo[0]) {
print OUT "$z\n";
$i = 1;
goto LABEL;
} elsif ($h_org[0] ne $orginfo[0]) {
if($h_org[0]=~/(\w+\s\w+)\s/) {
if($orginfo[0] eq $1) {
print OUT "0";
$i = 1;
goto LABEL;
}
}
}
}
if ($i == 0) {
print OUT "0";
}
LABEL:
}
}
close FH;
close FH1;
close OUT;
答案 0 :(得分:5)
如果您sort(1)
文件,则可以一次检查。不应该花费超过几秒钟(包括排序)。
另一种方法是将file1中的所有值加载到哈希中。这需要更多的内存消耗,特别是如果file1很大,但应该很快(再次,不超过几秒)。
我会选择perl而不是C来完成这样的工作,而且我在C中比在perl中更精通。这种工作在perl中编写代码要快得多,不易出错,运行速度也快。
答案 1 :(得分:3)
此脚本运行测试用例。请注意,您的预期输出是客观错误的:在文件2第1行第20列中,值0.2
存在。
#!perl
use 5.010; # just for `say`
use strict; use warnings;
use Test::More;
# define input files + expected outcome
my $file_1_contents = <<'_FILE1_';
0.1
0.11
0.12
0.13
0.14
0.15
0.16
0.17
0.18
0.19
0.2
_FILE1_
my $file_2_contents = <<'_FILE2_';
0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 0.1 0.11 0.12 0.13 0.14 0.15 0.16 0.17 0.18 0.19 0.2 0.21 0.22 0.23 0.24 0.25 0.26 0.27 0.28
1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 1.1 1.11 1.12 1.13 1.14 1.15 1.16 1.17 1.18 1.19 1.2 1.21 1.22 1.23 1.24 1.25 1.26 1.27 1.28
_FILE2_
my $expected_output = <<'_OUTPUT_';
1 2 0 0 0 0 0 0 0 10 11 12 13 14 15 16 17 18 19 20 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
_OUTPUT_
# open the filehandles
open my $file1, "<", \$file_1_contents or die "$!";
open my $file2, "<", \$file_2_contents or die "$!";
open my $expected, "<", \$expected_output or die "$!";
my %file1 = map { chomp; 0+$_ => undef } <$file1>;
while (<$file2>) {
chomp;
my @vals = split;
# If value exists in file1, print the col number.
my $line = join " " => map { exists $file1{0+$vals[$_]} ? $_+1 : 0 } 0 .. $#vals;
chomp(my $expected_line = <$expected>);
is $line, $expected_line;
}
done_testing;
要将完全相同的输出打印到28个文件,您将删除测试代码,而不是
say {$_} $line for @filehandles;
代替。
您现有的代码效率很低且非常简单。让我来帮你解决这个问题。
首先,使用use strict; use warnings;
启动所有你的Perl脚本,如果你有一个现代perl(v10或更高版本),你可以use 5.010;
(或任何你的版本)是)激活其他功能。
chomp
调用接受一个变量,并从字符串末尾删除$/
的当前值(通常是换行符)。这很重要,因为readline运算符不会为我们这样做。声明一个常量变量并不好。相反,做
my $file = "File1.txt";
my $hspfle = "File2.txt";
use strict
强制您正确声明变量,您可以使用my
执行此操作。
要打开文件,您应该使用以下习语:
open my $fh, "<", $filename or die "Can't open $filename: $!";
而不是or die ...
,您可以在脚本顶部use autodie
。
如果您无法打开文件,告诉您原因($!
)并指定显式打开模式(此处:<
= read),这将中止脚本。这可以避免文件名中包含特殊字符的错误。
词法文件句柄(在my
变量中,与裸字文件句柄相比)具有适当的范围,并自行关闭。您应该使用它们还有其他各种原因。
split
函数采用正则表达式,而不是字符串作为第一个参数。如果你仔细检查你的程序,你会看到split
中@hsporg
的每个元素@k_org
28次,而if ($h_org[0] eq $orginfo[0]) {
...;
} elsif ($h_org[0] ne $orginfo[0]) {
...;
}
中的每个元素都是28次@hsporg次。这是非常缓慢的,而且是不必要的,因为我们事先可以做到这一点。
如果条件为假,则无需再次在
中明确地测试错误$a ne $b
因为not $a eq $b
完全等同于goto
。
在Perl中使用# random example
LOOP: for my $i (1 .. 10) {
for my $j (1 .. 5) {
next if $i == $j; # start next iteration of current loop
next LOOP if 2 * $i == $j; # start next iteration of labeled loop
last LOOP if $i + $j == 13;# like `break` in C
}
是非常不同寻常的,并且跳转到某处的标签也不是特别快。标签主要用于循环控制:
redo
next
循环控制动词类似于#!/usr/bin/perl
use strict; use warnings;
use autodie; # automatic error messages
my ($file, $hspfile) = ("File1.txt", "file2.txt");
open my $fh1, "<", $file;
open my $fh2, "<", $hspfile;
my @k_org = <$fh1>;
my @hsporg = <$fh2>;
# Presplit the contents of the arrays:
for my $arr (\@k_org, \@hsporg) {
for (@$arr) {
chomp;
$_ = [ split /\t/ ]; # put an *anonymous arrayref* into each slot
}
}
my $output_files = 28;
for my $z (1 .. $output_files) {
open my $out, ">", "$z.txt";
H_ORG:
for my $h_org (@hsporg) {
my $i = 0;
ORGINFO:
for my $orginfo (@k_org) {
# elements in array references are accessed like $arrayref->[$i]
if($h_org->[0] eq $orginfo->[0]) {
print $out "$z\n";
$i = 1;
last ORGINFO; # break out of this loop
} elsif($h_org->[0] =~ /(\w+\s\w+)\s/ and $orginfo->[0] eq $1) {
print $out "0";
$i = 1;
last ORGINFO;
}
}
print $out "0" if not $i;
}
}
# filehandles are closed automatically.
,但如果有循环条件,则不会重新检查循环条件。
由于这些循环控制功能,以及打破任何封闭循环的能力,维护标志或精心设计的结果通常是非常必要的。
这是一个清理过的脚本版本,没有修复太多的实际算法:
...;
for (@$arr) {
chomp;
$_ = (split /\t/, $_, 2)[0]; # save just the first element
}
...;
ORGINFO:
for my $orginfo (@k_org) {
# elements in array references are accessed like $arrayref->[$i]
if($h_org eq $orginfo) {
...;
} elsif($h_org =~ /(\w+\s\w+)\s/ and $orginfo eq $1) {
...;
}
}
现在我们可以进一步优化:在每一行中,您只使用第一个元素。这意味着我们不必存储其余部分:
split
此外,访问标量比访问数组元素要快一些。
last
的第三个arg限制了结果片段的数量。因为我们只对第一个领域感兴趣,所以我们也不必分开其余的领域。
接下来,我们ORGINFO
循环H_ORG
,然后检查一个标志。这是不必要的:我们可以直接跳转到ORGINFO
循环的下一次迭代,而不是设置标志。如果我们自然退出print
循环,则保证不会设置该标志,因此我们可以执行 H_ORG:
for my $h_org (@hsporg) {
for my $orginfo (@k_org) {
if($h_org eq $orginfo) {
print $out "$z\n";
next H_ORG;
} elsif($h_org =~ /(\w+\s\w+)\s/ and $orginfo eq $1) {
print $out "0";
next H_ORG;
}
}
print $out "0";
}
:
print_index
然后,将相同的数据进行28次比较,将其打印到不同的文件中。更好:定义两个子print_zero
和# make this initialization *before* you use the subs!
my @filehandles = map {open my $fh, ">", "$_.txt"; $fh} 1 .. $output_files;
...; # the H_ORG loop
sub print_index {
for my $i (0 .. $#filehandles) {
print {$filehandles[$i]} $i+1, "\n";
}
}
sub print_zero {
print {$_} 0 for @filehandles;
}
。在这些内容中,您循环输出文件句柄:
# no enclosing $z loop!
H_ORG:
for my $h_org (@hsporg) {
for my $orginfo (@k_org) {
if($h_org eq $orginfo) {
print_index()
next H_ORG;
} elsif($h_org =~ /(\w+\s\w+)\s/ and $orginfo eq $1) {
print_zero();
next H_ORG;
}
}
print_zero();
}
然后:
{{1}}
这可以避免检查您已知的数据不匹配。
答案 2 :(得分:1)
在C中你可以尝试使用“qsort”和“bsearch”函数
首先,您需要将文件加载到数组中。
比你应该执行qsort()(除非你确定元素有一个命令)。并使用bsearch()对数组执行二进制搜索。
http://linux.die.net/man/3/bsearch
这比逐个检查所有元素要快得多。
如果它不存在,你可以尝试在perl中实现二进制搜索,这是一个简单的算法。