我能同时读写多个文件句柄吗?(Perl)

3
我正在尝试从两个文件中读取数据,并在第三个文件中生成输出。我最初想要在进行操作时编辑第一个文件,但除了使用数组之外,我没有找到合适的方法。
我的问题是,当我取消注释"_ref_param_handling"函数时,第三个文件(输出)为空。但是,以下内容使我最困惑:如果我在程序结尾处对输出文件进行UNIX基本的`cat`系统调用,它可以正常工作。如果我在编辑前打开文件句柄并在print FILEHANDLE LIST周围关闭它,也可以正常工作。
我肯定在这里漏掉了什么。除了我键盘和椅子之间的问题,还有什么原因呢?文件句柄冲突?作用域问题?
每个变量都已声明并具有我想要的值。
编辑:在三个文件上使用IO :: File没有改变任何内容。
新完整子例程代码
sub _ref_edit($) {
    my $manda_def = "$dir/manda_def.list";
    my $newrefhandle;
    my $ref       = $_[0];
    (my $refout   = $ref) =~ s/empty//;
    my $refhandle;
    my $parname   = '';
    my $parvalue  = '';
    my @val;

    _printMan;

    my $flush = readline STDIN;    # Wait for <enter>

    # If one or both of the ref. and the default values are missing
    if ( !( -e $manda_def && -e $ref ) ) {
        die "Cannot find $ref and/or $manda_def";
    }

    # Open needed files (ref & default)
    open( $refhandle, "<", $ref ) or die "Cannot open ref $ref : $!";
    open( $newrefhandle, ">>", $refout ) 
      or die "Cannot open new ref $refout : $!";

    # Read each line
    while ( my $refline = <$refhandle> ) {
    # If line read not an editable macro
        if ( $refline =~ /^define\({{(.+)}},\s+{{.*__VALUE__.*}}\)/ ){
        $parname = $1;         # $1 = parameter name captured in regexp
        # Prompt user
        $parvalue = _ref_param_handling( $parname, $manda_def );   
        # Substitution in ref
        $refline =~ s/__VALUE__/$parvalue/;
        # Param not specified and no default value
        $parvalue eq '' ? $refline=~s/__COM__/#/ : $refline=~s/__COM__//; 
        }

    print $newrefhandle $refline;
    }
    close $newrefhandle;
    close $refhandle;

    return $refout;
}    # End ref edit  

_ref_param_handle子例程仍然是:

open( $mde, '<', $_[1] )
      or die "Cannot open mandatory/default list $_[1] : $!";

    # Read default/mandatory file list 
    while (<$mde>) {       
       ( $name, $manda, $default, $match, $descript ) = split( /\s+/, $_, 5 ); 
       next if ( $name !~ $ref_param );  # If param read differs from parname

    (SOME IF/ELSE)

    } # End while <MDE>
    close $mde;
    return $input;
}

从manda_def文件中提取:

NAME  Mandatory? Default Match      Comm.
PORT          y NULL  ^\d+$ Database port
PROJECT       y NULL  \w{1,5}   Project name
SERVER        y NULL  \w+           Server name
modemRouting  n NULL  .+        
modlib        y bin   .+        
modules       y sms   .+

从ref_file中提取:

define({{PORT}},         {{__VALUE__}})dnl
define({{PROJECT}},      {{__VALUE__}})dnl
define({{SERVER}},       {{__VALUE__}})dnl
define({{modemRouting}}, {{__COM__{{$0}} '__VALUE__'}})dnl
define({{modlib}},       {{__COM__{{$0}} '__VALUE__'}})dnl
define({{modules}},      {{__COM__{{$0}} '__VALUE__'}})dnl

需要任何帮助都可以。


始终以 "use strict;" 和 "use warnings'" 开始你的 Perl 程序。 - Brad Gilbert
这只是子程序的一部分。我明白了! - Isaac Clarke
1
认为我理解你想做什么。但我不确定你是否想要替换所有这些{{}}。无论如何,你说这已经可以工作了,除了'>>'?你遇到了什么错误? - Leonardo Herrera
嘿,Leonardo,谢谢你的回复。我不想替换任何我的{{}},它们是我的m4注释分隔符。当使用'>>'访问现有的ref_file时,实际上会出现无限写入循环,但不要浪费时间在这上面。我正在调用$EDITOR(或vi),而不是逐行提示(仅在指定现有的ref_file时)。这样,用户可以修改他需要的任何内容。 - Isaac Clarke
4个回答

1

尝试在循环外打开第二个文件句柄以进行输入,并传递一个引用到子例程 _ref_param_handle。使用 seek 函数将文件定位回开头。
如果您的文件不太大,您还可以考虑将内容存储在数组中,而不是循环相同的内容。
编辑:
这里有一个小例子来支持我上面所说的:


#!/usr/bin/perl -w

sub test
{
 my $fh_to_read = $_[0] ;
 my $fh_to_write = $_[1] ;

 while(<$fh_to_read>)
 {
  print $fh_to_write  $_ ;
 }
 seek($fh_to_read,0,0) ;
}

open(FH1,"<dummy1");
open(FH2,"<dummy2");
open(FH3,">dummy3");

while(<FH2>)
{
 print FH3 "$_" ;
 test(\*FH1,\*FH3);
}

关于Perl 参考资料的信息


谢谢你的回答。我对Perl的引用不太熟悉(很难区分C++的指针/引用和Perl的Hard/Soft ref)。我尝试了_ref_param_handling($$),但将$mde传递给我的函数会返回一个错误。有什么诀窍(或解释... :))吗? - Isaac Clarke

1

不清楚哪里初始化了$refhandle$newrefhandle$mde。它们的值不同会影响打开操作的行为,也就是是否在打开新句柄前关闭任何文件句柄。

建议使用IO::File接口来打开/写入文件,因为这样可以更轻松地管理文件句柄,避免意外关闭。像这样...

use IO::File;

my $refhandle = IO::File->new("< $ref") or die "open() - $!";

$refhandle->print(...);

就编辑文件而言,这是我常用的一种模式,确保使用perl的-i行为。

sub edit_file
{
    my ($filename) = @_;

    # you can re-create the one-liner above by localizing @ARGV as the list of
    # files the <> will process, and localizing $^I as the name of the backup file.
    local (@ARGV) = ($filename);
    local($^I) = '.bak';

    while (<>)
    {
        s/original string/new string/g;
    }
    continue
    {
        print;
    }
}

好的,谢谢。幸运的是,IO::File 似乎已经包含在 Perl 的标准发行版中了(我可能无法在我将要工作的机器上安装任何软件包)。至于我的三个变量,它们只是声明为 "my _;"。这样做有问题吗? - Isaac Clarke
IO::File 似乎没有改变任何东西(请参见第一个帖子的编辑):( - Isaac Clarke
声明文件句柄时使用 my 并不错 - 根据文档,未定义的标量变量会被分配一个新匿名文件句柄的引用。 - Beano

1

根据我的理解,您的脚本想要将文件转换为以下形式:

define({{VAR1}}, {{__VALUE__}})
define({{VAR2}}, {{__VALUE__}})
define({{VAR3}}, {{__VALUE__}})
define({{VAR4}}, {{__VALUE__}})

变成这样:

define({{VAR1}}, {{}})
define({{VAR2}}, {{VALUE2}})
define({{VAR3}}, {{VALUE3}})
define({{VAR4}}, {{}})

以下代码可用。我不知道 manda_def 是什么意思,也没有费心去创建一个实际的变量替换函数。
#!/usr/bin/perl
use strict;
use warnings;

sub work {
    my ($ref, $newref, $manda_def) = @_;

    # Open needed files (ref & default)
    open(my $refhandle, '<', $ref) or die "Cannot open ref $ref : $!";
    open(my $newrefhandle, '>', $newref) or die "Cannot open new ref $newref: $!";

    # Read each line
    while (my $refline = <$refhandle>) {
        # if line read is not an editable macro
        if ($refline =~ /^define\({{(.+)}},\s+{{.*__VALUE__.*}}\)/){
            my $parvalue = _ref_param_handling($1, $manda_def); # manda_def?
            # Substitution in ref
            $refline  =~ s/__VALUE__/$parvalue/;
            # Param not specified and no default value
            $refline  =~ s/__COM__/#/ if $parvalue eq '';
        }
        print $newrefhandle $refline;
    }
    close $newrefhandle;
    close $refhandle;

    return $newref;
}

sub _ref_param_handling {
    my %parms = (VAR2 => 'VALUE2', VAR3 => 'VALUE3');
    return $parms{$_[0]} if exists $parms{$_[0]};
}

work('ref.txt', 'newref.txt', 'manda.txt');

事实上它比那稍微复杂一点。$manda_def 是一个文件路径,其中包含参数的默认值列表,说明它们是否是必需的,并为后续用户输入提供匹配模式。_ref_param_handling *通过另一个 while/filehandle 打开此文件(我认为这与其他两个文件冲突),并要求用户输入参数。如果您仍然想帮助,我明天早上会发布更多示例;) - Isaac Clarke
两件事。如果你根据任何答案修复了你的程序,你应该标记它。第二,你认为是什么导致了你的问题? - Leonardo Herrera
不,那时我没有修复任何东西,我只是发布了整个代码。正如我所说,我的脚本从未失败,只是在最后(当“关闭”发生时)才完成编写;) 那么我的问题有些解决了。 - Isaac Clarke
1
哦,你的意思是你只想在执行过程中检查进度吗?那么你应该刷新你的句柄。更简单的方法是在你的脚本开头添加"$| = 1"。 - Leonardo Herrera
抱歉我之前没有看到你的评论,但是没错,那基本上就是我想要做的!谢谢! - Isaac Clarke

0

各位,我认真考虑用我的无线鼠标上吊。

我的脚本从来没有失败过。我只是没有运行到最后(它实际上有一个非常长的参数列表)。打印只是在文件句柄关闭时完成的(或者我猜是这样)...

/我 *哭泣*

我已经花了24个小时在这上面了...


1
艾萨克,你能把实际的代码放到使用中吗?不是简化的片段,而是整个函数。我认为你的问题发生在其他地方。你已经在程序顶部使用了"use strict"和"use warnings"吗?你正在为变量使用小范围吗?此外,你的"ref"文件的一个小样本将有助于帮助你。 - Leonardo Herrera
1
不要太苛刻自己——我们都犯过这种错误……只是别再犯了 8-)!!! - Beano
我会在早上的第一时间发布所有东西,现在我离开我的开发计算机。当然,我使用警告和严格模式。如果没有它们,Perl 就太棘手了。关于范围,我总是使用 my(只有 GetOpts::Std 的 'our' 变量),即使有时候在网上或 Perl 书籍中读到这个会让我困惑。 - Isaac Clarke
也许你可以更新一下问题来说明这一点。不过,你所经历的并不罕见。 :) - brian d foy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接