gpt4 book ai didi

perl - 如何将文件句柄传递给函数?

转载 作者:行者123 更新时间:2023-12-02 05:11:15 24 4
gpt4 key购买 nike

当我运行下面的代码时,我得到

Can't use string ("F") as a symbol ref while "strict refs" in use at ./T.pl line 21.

第 21 行在哪里

flock($fh, LOCK_EX);

我做错了什么?

#!/usr/bin/perl

use strict;
use warnings;
use Fcntl ':flock', 'SEEK_SET'; # file locking
use Data::Dumper;
# use xx;

my $file = "T.yaml";
my $fh = "F";
my $obj = open_yaml_with_lock($file, $fh);

$obj->{a} = 1;

write_yaml_with_lock($obj, $fh);

sub open_yaml_with_lock {
my ($file, $fh) = @_;

open $fh, '+<', $file;
flock($fh, LOCK_EX);
my $obj = YAML::Syck::LoadFile($fh);

return $obj;
}

sub write_yaml_with_lock {
my ($obj, $fh) = @_;

my $yaml = YAML::Syck::Dump($obj);
$YAML::Syck::ImplicitUnicode = 1;
seek $fh,0, SEEK_SET; # seek back to the beginning of file

print $fh $yaml . "---\n";
close $fh;
}

最佳答案

你做错的是使用字符串“F”作为文件句柄。这从来都不是有效的事情;你可以使用裸字作为filehandle (open FH, ...; print FH ...),或者您可以传入一个空标量和 perl 会为其分配一个新的打开文件对象多变的。但如果传入字符串F,那么就需要引用然后处理为 F,而不是 $fh。但是,不要这样做。

这样做:

sub open_yaml_with_lock {
my ($file) = @_;

open my $fh, '+<', $file or die $!;
flock($fh, LOCK_EX) or die $!;

my $obj = YAML::Syck::LoadFile($fh); # this dies on failure
return ($obj, $fh);
}

我们在这里做了几件事。一,我们不存储全局文件句柄。全局状态让你的程序变得极其很难理解——我对你的 10 行帖子感到很难——应该避免。如果您愿意,只需返回文件句柄保留它。或者,您可以像 open 那样为它起别名:

sub open_yaml_with_lock {
open $_[0], '+<', $_[1] or die $!;
...
}

open_yaml_with_lock(my $fh, 'filename');
write_yaml_with_lock($fh);

但说实话,这真是一团糟。将这些东西放入一个对象中。新建打开并锁定文件。添加一个 write 方法。完毕。现在你可以重用此代码(并让其他人也这样做),而不必担心出错了。减轻压力。

我们在这里做的另一件事是检查错误。是的,磁盘可以失败。文件可能有拼写错误。如果你幸福地忽略返回值open和flock,那么你的程序可能不会按照你的想法做它正在做。该文件可能无法打开。该文件可能不是正确锁定。有一天,你的程序将无法正常运行因为您将“file”拼写为“flie”,并且无法打开该文件。你会挠头好几个小时想知道发生了什么。最终,你会放弃,回家,稍后再试。这次,你不会打错文件名,它就会起作用。几个小时将已经被浪费了。你会比你应该死的早几年因为累积的压力。所以只需使用autodie或编写
die $!
在你的系统调用之后,这样你就会收到一条错误消息出问题了!

如果您编写use autodie qw/open folly,您的脚本将是正确的
在顶部寻找 close/
。 (实际上,你还应该检查一下“打印”有效或使用 File::Slurp或者syswrite,因为 autodie 无法检测到失败的 print 语句。)

无论如何,总结一下:

  • 定义$fh时不要打开$fh。将 open my $fh 写入 避免考虑这个。

  • 始终检查系统调用的返回值。让自动模具做 这是给你的。

  • 不保留全局状态。不要编写一堆函数 旨在一起使用但依赖于隐含的先决条件 就像一个打开的文件。如果函数有前提条件,请将它们放入 一个类并使构造函数满足先决条件。 这样,您就不会意外编写有错误的代码!

更新

好的,下面是如何使其更加面向对象。首先我们将进行“纯 Perl”OO然后使用 Moose 。驼鹿是我将在任何实际工作中使用什么; “纯Perl”只是为了为了让刚接触 OO 和 OO 的人更容易理解Perl。

package LockedYAML;
use strict;
use warnings;

use Fcntl ':flock', 'SEEK_SET';
use YAML::Syck;

use autodie qw/open flock sysseek syswrite/;

sub new {
my ($class, $filename) = @_;
open my $fh, '+<', $filename;
flock $fh, LOCK_EX;

my $self = { obj => YAML::Syck::LoadFile($fh), fh => $fh };
bless $self, $class;
return $self;
}

sub object { $_[0]->{obj} }

sub write {
my ($self, $obj) = @_;
my $yaml = YAML::Syck::Dump($obj);

local $YAML::Syck::ImplicitUnicode = 1; # ensure that this is
# set for us only

my $fh = $self->{fh};

# use system seek/write to ensure this really does what we
# mean. optional.
sysseek $fh, 0, SEEK_SET;
syswrite $fh, $yaml;

$self->{obj} = $obj; # to keep things consistent
}

然后,我们可以在主程序中使用该类:

use LockedYAML;

my $resource = LockedYAML->new('filename');
print "Our object looks like: ". Dumper($resource->object);

$resource->write({ new => 'stuff' });

错误会抛出异常,可以通过以下方式处理 Try::Tiny和 YAML只要实例存在,文件就会保持锁定状态。你可以,的当然,同时有许多 LockedYAML 对象,这就是我们的原因使其面向对象。

最后是 Moose 版本:

package LockedYAML;
use Moose;

use autodie qw/flock sysseek syswrite/;

use MooseX::Types::Path::Class qw(File);

has 'file' => (
is => 'ro',
isa => File,
handles => ['open'],
required => 1,
coerce => 1,
);

has 'fh' => (
is => 'ro',
isa => 'GlobRef',
lazy_build => 1,
);

has 'obj' => (
is => 'rw',
isa => 'HashRef', # or ArrayRef or ArrayRef|HashRef, or whatever
lazy_build => 1,
trigger => sub { shift->_update_obj(@_) },
);

sub _build_fh {
my $self = shift;
my $fh = $self->open('rw');
flock $fh, LOCK_EX;
return $fh;
}

sub _build_obj {
my $self = shift;
return YAML::Syck::LoadFile($self->fh);
}

sub _update_obj {
my ($self, $new, $old) = @_;
return unless $old; # only run if we are replacing something

my $yaml = YAML::Syck::Dump($new);

local $YAML::Syck::ImplicitUnicode = 1;

my $fh = $self->fh;
sysseek $fh, 0, SEEK_SET;
syswrite $fh, $yaml;

return;
}

其用法类似:

 use LockedYAML;

my $resource = LockedYAML->new( file => 'filename' );
$resource->obj; # the object
$resource->obj( { new => 'object' }); # automatically saved to disk

Moose 版本更长,但运行时一致性更高检查并且更容易增强。 YMMV。

关于perl - 如何将文件句柄传递给函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6845376/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com