我很好奇使用Storable的 store_fd 和 fd_retrieve 是否允许我将数据结构存储到程序自己的 DATA 文件句柄中。我意识到这不是最佳实践,我只是好奇它是否会起作用,我快速尝试它似乎不起作用。
4 回答
我不确定你为什么要这样做,但你可以伪造它。你应该尽量避免这种情况。
只是为了咯咯笑,您可以打开一个文件句柄,从中读取行$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
在普通情况下是不可能的:
$ cat 写入数据
#! /usr/bin/perl
use warnings;
print DATA "bar!\n";
$ ./写数据
名称“main::DATA”只使用了一次:./write-data 第 6 行可能有错字。
./write-data 第 6 行未打开的文件句柄 DATA 上的 print()。
但你可以创造非凡的条件:
#! /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;
unlink
在 Windows 上,由于其默认的文件共享语义,您会收到警告。如果那是您的平台,您可以随时清理END
或使用Win32::SharedFileOpen。
DATA
是读取与脚本一起存储的数据的句柄。Conway 的Inline::Files是我所知道的唯一一个谈论可写虚拟文件的模块。而且由于脚本文件通常是 ASCII 我不知道如果您在 Storable 的输出中在 MSDOS 上获得二进制 26 字节或在 UNIX 上获得二进制 4 会发生什么。
但是,如果您在谈论通过在其中键入数据来存储数据,只是为了从脚本中读取数据,那么二进制问题仍然会遇到您。
我想出了自己的解决方案……并不是我特别推荐它或其他任何方法;在我的例子中,它是一个单元测试脚本,具有多维散列结构中的参考值。我不会详细介绍这些东西的作用和原因,但最终结果是代码中的一个小修复或更改可能会导致许多值需要更新(在验证更改有效之后)。
因此,我将哈希移动到__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);
在脚本的开头,我DATA
在存储对初始句柄位置和 mtime 的引用之后加载哈希(mtime 用于确保在脚本执行期间文件没有被修改)。
use vars qw(%HASH $FILEPOS $MTIME);
{
$FILEPOS = tell(DATA);
$MTIME = (stat(DATA))[9];
local $/;
my $data = <DATA>;
%HASH = %{eval $data};
}
最后,要更新__DATA__
我在 处打开的部分__FILE__
,$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);
确保在开发时保留文件的备份,因为一个错误可能会破坏您的所有代码!
另请注意,同样适用于Storable
; 存储会更高效、更快。唯一需要注意的是它是二进制文件,这意味着文件差异可能不会显示在源代码管理中,而且它不像 Dumper 的输出那样容易编辑。