可以存储到DATA文件句柄吗?

时间:2010-02-05 20:10:20

标签: perl storable

我很好奇如果使用Storable的store_fd和fd_retrieve将允许我将数据结构存储到程序自己的DATA文件句柄中。我意识到这不是最佳实践,我只是好奇它是否有效,我尝试它的快速尝试似乎不起作用。

4 个答案:

答案 0 :(得分:2)

我不确定你为什么要这样做,但你可以伪造它。你应该尽量避免这种情况。

只是为了咯咯笑,你可以打开一个文件句柄,从$0读取行并打印它们直到你到达__DATA__,然后添加新的__DATA__部分。然后,如果系统在程序运行时锁定文件,则可以通过$0将新文件重命名为exec

#!perl

my $mode = (stat($0))[2] & 07777;

open my($fh), '<', $0 or die "I can't open me! $!\n";
open my($new), '>', "$0.new" or die "I can't open you! $!\n";
eval { chmod( $mode, $new ) } or warn "Couldn't set permissions: $@\n";

while( <$fh> )
    {
    last if /^__DATA__$/;
    print { $new } $_;
    }

print "I am $$\n";
print { $new } "__DATA__\n", join '|', $$, time, (stat($0))[1];

rename( "$0.new", $0 )

__DATA__
64574|1265415126|8843292

答案 1 :(得分:1)

DATA读取与脚本一起存储的数据的句柄。 Conway的Inline::Files是我所知道的唯一可以讨论可写虚拟文件的模块。由于脚本文件通常是ASCII,我不知道如果在MSDOS上有二进制26字节,或者在Storable的输出中有UNIX上的二进制4,会发生什么。

但是,如果你在谈论通过输入来存储数据,只有从脚本中读取它,那么二元问题仍然面临着你。

因此,最好使用YAMLJSON来保持持久性。我知道YAML会在从DATA中检索数据时处理祝福。

答案 2 :(得分:1)

在普通条件下这是不可能的:

$ cat write-data
#! /usr/bin/perl

use warnings;
print DATA "bar!\n";

$ ./write-data
Name "main::DATA" used only once: possible typo at ./write-data line 6.
print() on unopened filehandle DATA at ./write-data line 6.

但是你可以创造非凡的条件:

#! /usr/bin/perl

use warnings;
use strict;

use Data::Dumper;
use File::Temp qw/ tempfile /;
use Storable qw/ store_fd fd_retrieve /;

sub store_in_DATA {
  my($data) = @_;

  my($fh,$path) = tempfile;
  unlink $path           or warn "$0: unlink: $!";

  *DATA = $fh;
  store_fd $data, \*DATA or warn "$0: print: $!";

  seek DATA, 0, 0        or warn "$0: seek: $!";
}

store_in_DATA { foo => "There is no spoon.\n" };

undef $/;
my $ref = fd_retrieve \*DATA;
print Dumper $ref;

在Windows上,由于其默认的文件共享语义,您将在unlink上收到警告。如果这是您的平台,您可以在END时间进行清理或使用Win32::SharedFileOpen

答案 3 :(得分:0)

我提出了自己的解决方案......不是我特别推荐它或其他任何解决方案;在我的例子中,它是一个单元测试脚本,在多维哈希结构中具有引用值。我不会详细介绍这些内容的作用和原因,但最终结果是代码中的小修复或更改会导致许多值需要更新(在验证更改后有效)。

因此,我使用__DATA__将哈希移动到Data::Dumper部分。将其写入文件句柄的代码如下所示:

use Data::Dumper;
$Data::Dumper::Terse = 1; # to Eval whole thing as a hash
$Data::Dumper::Indent = 1; # Looks better, just a preference
$Data::Dumper::Sortkeys = 1; # To keep changes minimal in source control
print $fh Dumper(\%HASH);

在脚本开始时,我在存储对初始句柄位置和mtime的引用后加载来自DATA的哈希值(mtime用于确保在脚本执行期间文件未被修改)。

use vars qw(%HASH $FILEPOS $MTIME);

{
    $FILEPOS = tell(DATA);
    $MTIME = (stat(DATA))[9];
    local $/;
    my $data = <DATA>;
    %HASH = %{eval $data};
}

最后,要更新我在__DATA__打开__FILE__的{​​{1}}部分,请将其截断并写入。我简化了这个例子的错误处理。

$FILEPOS

请确保在开发过程中保留文件的备份,因为单个错误可能会破坏您的所有代码!

另请注意,同样适用于open(my $fh, '>>', __FILE__) or die $!; seek($fh, $FILEPOS, 0) or die $!; die "File changed" if ((stat($fh))[9] != $MTIME); truncate($fh, $FILEPOS) or die $!; # Assumes Dumper is already loaded and configured as in first code snippet print $fh Dumper(\%HASH); ;存储将更有效,更快捷。唯一需要注意的是它是二进制文件,这意味着文件差异很可能不会出现在源代码控制中,并且编辑它并不像Dumper的输出那么容易。