有没有办法让子程序在处理时发回数据?例如(此示例仅用于说明) - 子例程读取文件。当它正在读取文件时,如果满足某些条件,则“返回”该行并继续处理。我知道有些人会回答 - 你为什么要那样做?你为什么不......?,但我真的想知道这是否可能。
答案 0 :(得分:7)
实现此类功能的常用方法是使用回调函数:
{
open my $log, '>', 'logfile' or die $!;
sub log_line {print $log @_}
}
sub process_file {
my ($filename, $callback) = @_;
open my $file, '<', $filename or die $!;
local $_;
while (<$file>) {
if (/some condition/) {
$callback->($_)
}
# whatever other processing you need ....
}
}
process_file 'myfile.txt', \&log_line;
或甚至没有命名回调:
process_file 'myfile.txt', sub {print STDERR @_};
答案 1 :(得分:4)
有些语言使用"generators"或"coroutines"提供此类功能,但Perl却没有。上面链接的生成器页面包含Python,C#和Ruby(以及其他)中的示例。
答案 2 :(得分:3)
Coro模块看起来对这个问题很有用,虽然我不知道它是如何工作的,也不知道它是否做了它所宣传的内容。
答案 3 :(得分:2)
在Perl中执行此操作的最简单方法可能是使用迭代器类型的解决方案。例如,这里我们有一个子程序,它在文件句柄上形成closure:
open my $fh, '<', 'some_file.txt' or die $!;
my $iter = sub {
while( my $line = <$fh> ) {
return $line if $line =~ /foo/;
}
return;
}
子遍历这些行,直到找到一个匹配模式/foo/
然后返回它,否则返回任何内容。 (标量上下文中的undef
。)因为文件句柄$fh
是在子站的范围之外定义的,所以它在调用之间保持驻留在内存中。最重要的是,保留其状态,包括文件中当前的搜索位置。因此,对子程序的每次调用都会恢复读取最后一次停止的文件。
使用迭代器:
while( defined( my $next_line = $iter->() ) ) {
# do something with each line here
}
答案 4 :(得分:0)
如果你真的想要这样做,你可以使用线程。一种选择是分叉读取文件的单独线程,当它找到某个行时,将它放在线程之间共享的数组中。然后另一个线程可以在找到它们的时候获取它们并处理它们。下面是一个读取文件,在文件行中查找“X”并在找到文件时执行操作的示例。
use strict;
use threads;
use threads::shared;
my @ary : shared;
my $thr = threads->create('file_reader');
while(1){
my ($value);
{
lock(@ary);
if ($#ary > -1){
$value = shift(@ary);
print "Found a line to process: $value\n";
}
else{
print "no more lines to process...\n";
}
}
sleep(1);
#process $value
}
sub file_reader{
#File input
open(INPUT, "<test.txt");
while(<INPUT>){
my($line) = $_;
chomp($line);
print "reading $line\n";
if ($line =~ /X/){
print "pushing $line\n";
lock(@ary);
push @ary, $line;
}
sleep(4)
}
close(INPUT);
}
尝试使用此代码作为test.txt文件:
line 1
line 2X
line 3
line 4X
line 5
line 6
line 7X
line 8
line 9
line 10
line 11
line 12X
答案 5 :(得分:-1)
如果您的语言支持闭包,您可以执行以下操作:
顺便说一句,该函数不会继续处理文件,它会在你调用它时运行,所以它可能不是你需要的。
(这是一个类似伪代码的javascript)
function fileReader (filename) {
var file = open(filename);
return function () {
while (s = file.read()) {
if (condition) {
return line;
}
}
return null;
}
}
a = fileReader("myfile");
line1 = a();
line2 = a();
line3 = a();
答案 6 :(得分:-1)
递归子怎么样?重新open
现有的文件句柄不会重置输入行号,因此它会从它停止的位置继续。
以下是process_file
子例程打印出包含foo的空白行分隔的"\n\n"
段落的示例。
sub process_file {
my ($fileHandle) = @_;
my $paragraph;
while ( defined(my $line = <$fileHandle>) and not eof(<$fileHandle>) ) {
$paragraph .= $line;
last unless length($line);
}
print $paragraph if $paragraph =~ /foo/;
goto &process_file unless eof($fileHandle);
# goto optimizes the tail recursion and prevents a stack overflow
# redo unless eof($fileHandle); would also work
}
open my $fileHandle, '<', 'file.txt';
process_file($fileHandle);