Storable - Perl 数据结构的持久性
use Storable;
store \%table, 'file';
$hashref = retrieve('file');
use Storable qw(nstore store_fd nstore_fd freeze thaw dclone);
# Network order
nstore \%table, 'file';
$hashref = retrieve('file'); # There is NO nretrieve()
# Storing to and retrieving from an already opened file
store_fd \@array, \*STDOUT;
nstore_fd \%table, \*STDOUT;
$aryref = fd_retrieve(\*SOCKET);
$hashref = fd_retrieve(\*SOCKET);
# Serializing to memory
$serialized = freeze \%table;
%table_clone = %{ thaw($serialized) };
# Deep (recursive) cloning
$cloneref = dclone($ref);
# Advisory locking
use Storable qw(lock_store lock_nstore lock_retrieve)
lock_store \%table, 'file';
lock_nstore \%table, 'file';
$hashref = lock_retrieve('file');
Storable 包为包含 SCALAR、ARRAY、HASH 或 REF 对象的 Perl 数据结构带来持久性,即可以方便地存储到磁盘并在以后检索的任何内容。
可以通过调用 store
来以常规过程方式使用它,其中包含要存储的对象的引用,以及应将映像写入其中的文件名。
对于 I/O 问题或其他内部错误,该例程返回 undef
,否则返回真值。严重错误将作为 die
异常传播。
要检索存储到磁盘的数据,请对文件名使用 retrieve
。存储到该文件中的对象将重新创建到内存中,并返回对根对象的引用。如果在读取时发生 I/O 错误,则返回 undef
。其他严重错误将通过 die
传播。
由于存储是递归执行的,您可能希望将对共享大量公共数据的对象的引用填充到一个数组或哈希表中,然后存储该对象。这样,当您检索回整个对象时,这些对象将继续共享它们最初共享的内容。
以轻微的标头开销为代价,您可以使用 store_fd
例程将数据存储到已打开的文件描述符中,并通过 fd_retrieve
从文件中检索数据。这些名称默认情况下不会导入,因此如果您需要这些例程,则必须显式地执行此操作。您提供的文件描述符必须已经打开,如果您要检索,则需要读取,如果您要存储,则需要写入。
store_fd(\%table, *STDOUT) || die "can't store to stdout\n";
$hashref = fd_retrieve(*STDIN);
您还可以按网络顺序存储数据,以便在多个平台之间轻松共享,或者在已知远程连接的套接字上存储数据。要调用的例程具有用于网络的初始 n
前缀,如 nstore
和 nstore_fd
。在检索时,您的数据将得到正确还原,因此您不必知道您是从本机数据还是网络顺序数据中还原的。双精度值也以字符串化形式存储,以确保可移植性,同时以轻微的风险损失小数点后的某些精度。
使用 fd_retrieve
时,对象按顺序检索,每个关联的 store_fd
一个对象(即一个递归树)。
如果您更倾向于面向对象,则可以从 Storable 继承并通过调用 store
作为方法直接存储您的对象。要存储的树的根是一个受祝福的引用(即一个对象)这一事实是特殊情况,因此检索不会提供对该对象的引用,而是提供受祝福的对象引用本身。(否则,您将获得对该受祝福对象的引用)。
Storable 引擎还可以将数据存储到 Perl 标量中,以便稍后检索。这主要用于在某些安全的紧凑内存位置冻结复杂结构(在那里它可以通过某些 IPC 发送到另一个进程,因为冻结结构实际上也对其进行序列化)。稍后,您可以在其他地方解冻 Perl 标量并在内存中重新创建原始复杂结构。
令人惊讶的是,要调用的例程被命名为freeze
和thaw
。如果您希望将冻结的标量发送到另一台机器,请改用nfreeze
以获取可移植映像。
请注意,冻结对象结构并立即解冻它实际上实现了该结构的深度克隆
dclone(.) = thaw(freeze(.))
Storable 为您提供了dclone
接口,该接口不会创建该中间标量,而是将结构冻结在某些内部内存空间中,然后立即将其解冻。
lock_store
和lock_nstore
例程等效于store
和nstore
,不同之处在于它们在写入之前获取文件的独占锁。同样,lock_retrieve
与retrieve
相同,但在读取之前也会获取文件的共享锁。
与任何咨询锁定方案一样,只有在您系统地使用lock_store
和lock_retrieve
时,保护才有效。如果您的应用程序的一方使用store
而另一方使用lock_retrieve
,您将得不到任何保护。
内部咨询锁定是使用 Perl 的 flock() 例程实现的。如果您的系统不支持任何形式的 flock(),或者如果您通过 NFS 共享文件,您可能希望使用其他形式的锁定,方法是使用 LockFile::Simple 等模块锁定文件,而不是锁定文件描述符。
Storable 的核心是用 C 编写的,以获得良好的速度。在操作 perl 内部时进行了额外的低级优化,以牺牲封装来换取更快的速度。
通常,Storable 按 Perl 内部存储的顺序存储哈希元素,即伪随机。如果您将$Storable::canonical
设置为某个TRUE
值,Storable 将按键对元素进行排序来存储哈希。这允许您通过比较其冻结表示(甚至压缩的冻结表示)来比较数据结构,这对于为复杂查询创建查找表很有用。
规范顺序并不意味着网络顺序;这两个是正交设置。
自 Storable 版本 2.05 起,可以使用 B::Deparse 序列化 CODE 引用。要启用此功能,请将 $Storable::Deparse
设置为 true 值。要启用反序列化,应将 $Storable::Eval
设置为 true 值。请注意,反序列化是通过 eval
完成的,如果 Storable 文件包含恶意数据,则此操作很危险。您可以将 $Storable::Eval
设置为子例程引用,该引用将用于代替 eval
。请参阅下面的示例,了解如何使用 Safe 隔间对 CODE 引用进行反序列化。
如果 $Storable::Deparse
和/或 $Storable::Eval
设置为 false 值,则在序列化和反序列化时将遵循 $Storable::forgive_me
(见下文)的值。
此版本的 Storable 可用于较新版本的 Perl,以序列化早期 Perl 不支持的数据。默认情况下,如果 Storable 遇到无法反序列化的数据,它将尝试执行正确操作,方法是 croak()
。但是,可以按如下方式更改默认值
Perl 5.6 添加了对代码点 > 255 的 Unicode 字符的支持,而 Perl 5.8 完全支持哈希键中的 Unicode 字符。Perl 在内部使用 utf8 编码包含这些字符的字符串,而 Storable 将它们序列化为 utf8。默认情况下,如果较早版本的 Perl 遇到无法表示的 utf8 值,它将 croak()
。要更改此行为,以便 Storable 将 utf8 编码值反序列化为字节字符串(有效地删除 is_utf8 标志),请将 $Storable::drop_utf8
设置为某个 TRUE
值。这是一种数据丢失,因为当 $drop_utf8
为 true 时,将无法判断原始数据是 Unicode 字符串还是恰好为有效 utf8 的一系列字节。
Perl 5.8 为受限哈希添加了支持,受限哈希的键被限制为给定集合,并且值可以锁定为只读。默认情况下,当 Storable 在不支持受限哈希的 perl 上遇到受限哈希时,它会将其反序列化为普通哈希,静默地丢弃任何占位符键,并使键和所有值解锁。要使 Storable croak()
,请将 $Storable::downgrade_restricted
设置为 FALSE
值。要恢复默认设置,请将其设置回某个 TRUE
值。
cperl PERL_PERTURB_KEYS_TOP 哈希策略在受限哈希上存在已知问题。
在 64 位系统上,某些数据结构可能超过 2G(即 I32_MAX)限制。在 32 位系统上,字符串也在 I32 和 U32(2G-4G)之间。自 Storable 3.00(不在 perl5 内核中)以来,我们能够存储和检索这些对象,即使 perl5 本身无法处理它们。这些字符串长度超过 4G,数组元素超过 2G,哈希元素超过 2G。cperl 禁止哈希元素超过 2G,但 cperl 中会失败。perl5 本身至少在 5.26 之前允许这样做,但无法迭代它们。请注意,在 perl 有机会中止之前,创建这些对象可能会导致操作系统出现内存不足异常。
如果 Storable 遇到内部版本号高于读取 Storable 已知版本的文件,早期版本的 Storable 会立即 croak。每次向文件格式词汇表中添加新数据类型(例如受限哈希)时,内部版本号都会增加。这意味着较新的 Storable 模块无法编写可由较旧 Storable 读取的文件,即使编写器不存储较新的数据类型也是如此。
此版本的 Storable 会推迟 croaking,直到它在文件中遇到无法识别的类型。这意味着它将继续读取由较新的 Storable 模块生成的文件,这些模块在写出内容时很小心,从而更容易在混合环境中升级 Storable 模块。
可以通过将 $Storable::accept_future_minor
设置为某个 FALSE
值来重新启用立即 croaking 的旧行为。
所有这些变量对支持相关功能的较新 Perl 没有影响。
Storable 使用“异常”范例,因为它不会尝试解决故障:如果发生一些不好的事情,则会从调用者的角度生成一个异常(请参阅 Carp 和 croak()
)。使用 eval {} 来捕获这些异常。
当 Storable 崩溃时,它会尝试通过 Log::Agent
包中的 logcroak()
例程报告错误(如果可用)。
通过让 store() 或 retrieve() 返回 undef
来报告常规错误。此类错误通常是 I/O 错误(或检索时的截断流错误)。
当 Storable 抛出“嵌套结构的最大递归深度已超过”错误时,我们已经用尽堆栈空间。不幸的是,在一些较早的 perl 版本中,清理递归数据结构会递归到 free 调用中,这会导致清理中的堆栈溢出。此数据结构不会被正确清理,它只会全局销毁期间被销毁。
任何类都可以定义挂钩,这些挂钩将在对该类的实例对象进行序列化和反序列化过程中调用。这些挂钩可以重新定义执行序列化的方式(因此,还可以重新定义对称反序列化的执行方式)。
由于我们之前说过
dclone(.) = thaw(freeze(.))
我们对挂钩所说的所有内容也应适用于深度克隆。但是,挂钩可以知道该操作是仅仅序列化还是克隆。
因此,当涉及序列化挂钩时,
dclone(.) <> thaw(freeze(.))
好吧,你可以保持它们同步,但不能保证它总是适用于别人编写的类。此外,这样做几乎没有好处:序列化挂钩只能保留对象的其中一个属性,而这可能不是在对同一对象进行深度克隆时应该发生的事情。
以下是挂钩接口
STORABLE_freeze
obj, cloning序列化挂钩,在序列化期间在对象上调用。它可以被继承,或像任何其他方法一样在类本身中定义。
参数:obj 是要序列化的对象,cloning 是一个标志,指示我们是在 dclone() 中还是通过 store() 或 freeze() 进行常规序列化。
返回值:一个 LIST ($serialized, $ref1, $ref2, ...)
,其中 $serialized 是要使用的序列化形式,而可选的 $ref1、$ref2 等是您希望 Storable 引擎序列化的额外引用。
在反序列化时,将返回相同的 LIST,但所有额外引用都将指向反序列化的结构。
在序列化流程中首次命中钩子时,您可能会让它返回一个空列表。这将向 Storable 引擎发出信号,进一步丢弃此类的钩子,从而恢复对底层 Perl 数据的默认序列化。钩子将在下一次序列化中再次正常处理。
除非您更了解,否则序列化钩子应始终说
sub STORABLE_freeze {
my ($self, $cloning) = @_;
return if $cloning; # Regular default serialization
....
}
以保持合理的 dclone() 语义。
STORABLE_thaw
obj, cloning, serialized, ...在反序列化期间对对象调用的反序列化钩子。但等等:如果我们正在反序列化,那么还没有对象...对吗?
错了:Storable 引擎为您创建了一个空对象。如果您了解 Eiffel,您可以将 STORABLE_thaw
视为一个备用创建例程。
这意味着可以像任何其他方法一样继承钩子,并且 obj 是您对此特定实例的受祝福的引用。
如果您了解 STORABLE_freeze
,则其他参数应该看起来很熟悉:当我们成为深度克隆操作的一部分时,cloning 为真;serialized 是您在 STORABLE_freeze
中返回给引擎的序列化字符串;并且可能有一个可选的引用列表,按照您在序列化时给出的顺序,指向反序列化的对象(已由 Storable 引擎处理)。
当 Storable 引擎找不到任何 STORABLE_thaw
钩子例程时,它会尝试通过动态地需要包(使用受祝福的包名)来加载类,然后重新尝试查找。如果在此时找不到钩子,引擎会发出 croaks。请注意,如果您在同一个文件中定义了多个类,此机制将失败,但 perlmod 已向您发出警告。
由您使用此信息以您想要的方式填充 obj。
返回值:无。
STORABLE_attach
class, cloning, serialized虽然 STORABLE_freeze
和 STORABLE_thaw
对于每个实例都是独立的类很有用,但此机制对于作为公共进程级或系统级资源存在的对象(例如单例对象、数据库池、缓存或备忘对象)来说存在困难(或不兼容)。
替代方法 STORABLE_attach
为这些共享对象提供了解决方案。不要使用 STORABLE_freeze
--> STORABLE_thaw
,而是改用 STORABLE_freeze
--> STORABLE_attach
。
参数:class 是我们附加到的类,cloning 是一个标志,表示我们处于 dclone() 中还是通过 thaw() 进行常规反序列化,serialized 是资源对象的存储字符串。
因为这些资源对象被认为是由整个进程/系统拥有,而不是被序列化的任何“属性”,所以对象下面的任何引用都不应包含在序列化的字符串中。因此,在任何实现 STORABLE_attach
的类中,STORABLE_freeze
方法不能返回任何引用,如果 STORABLE_freeze
尝试返回引用,Storable
将抛出一个错误。
返回共享资源对象的“附加”所需的所有信息必须仅包含在 STORABLE_freeze
返回字符串中。否则,STORABLE_freeze
的行为与 STORABLE_attach
类正常情况下的行为相同。
因为 STORABLE_attach
传递的是类(而不是对象),所以它也直接返回对象,而不是修改传递的对象。
返回值:类型为 class
的对象
谓词不可导出。必须通过显式地用 Storable 包名称作为前缀来调用它们。
Storable::last_op_in_netorder
Storable::last_op_in_netorder()
谓词将告诉你上次存储或检索操作中是否使用了网络顺序。如果你不知道如何使用它,那就忘记它吧。
Storable::is_storing
如果在存储操作中(通过 STORABLE_freeze 钩子),则返回 true。
Storable::is_retrieving
如果在检索操作中(通过 STORABLE_thaw 钩子),则返回 true。
有了钩子,就可以递归回到 Storable 引擎。事实上,钩子是常规 Perl 代码,而 Storable 在序列化和反序列化事物时很方便,那么为什么不使用它来处理序列化字符串呢?
但是,你需要了解一些事情
从 Storable 3.05 到 3.13,我们探测了引用、数组和哈希的堆栈递归限制,最大深度约为 ~1200-35000,否则我们可能会陷入堆栈溢出。顺便说一下,在 JSON::XS 中,此限制为 512。对于彼此不立即引用的引用,目前还没有这样的限制,所以你可能会陷入这样的堆栈溢出段错误。
此探测和我们执行的检查有一些限制
构建时的堆栈大小在运行时可能不同,例如,堆栈大小可能已使用 ulimit(1) 修改。如果它在运行时更大,则 Storable 可能会不必要地使 freeze() 或 thaw() 失败。如果它在构建时更大,则 Storable 在运行时处理深度结构时可能会分段错误。
堆栈大小在不同的线程中可能不同。
数组和哈希递归限制针对相同的递归深度分别进行检查,带有多个嵌套哈希中嵌套数组的大序列的冻结结构可能会耗尽处理器堆栈,而不会触发 Storable 的递归保护。
因此,这些现在具有简单的默认值,而不是在构建时探测。
您可以通过分别修改 $Storable::recursion_limit
和 $Storable::recursion_limit_hash
来控制最大数组和哈希递归深度。两者都可以设置为 -1
以防止任何深度检查,尽管不建议这样做。
如果您想测试限制是什么,则 stacksize 工具包含在 Storable
分发中。
如果您通过 freeze()(例如)序列化的内容指向我们尝试在挂钩中序列化的对象,则可以创建无限循环。
对象之间的共享引用不会保持共享:如果我们正在序列化对象 [A, C] 的列表,其中对象 A 和 C 都引用相同的对象 B,并且如果 A 中有一个序列化挂钩,其中声明 freeze(B),那么在反序列化时,我们将得到 [A', C'],其中 A' 引用 B',但 C' 引用 D,即 B' 的深度克隆。拓扑结构未保留。
stack_depth()
和 stack_depth_hash()
返回系统最大的堆栈递归限制。哈希限制通常是数组和 ref 限制的一半,因为 Perl 哈希 API 不是最优的。
这就是为什么 STORABLE_freeze
允许您提供要序列化的引用列表。引擎保证这些引用将在与其他对象相同的上下文中序列化,因此共享对象将保持共享。
在上述 [A, C] 示例中,STORABLE_freeze
挂钩可以返回
("something", $self->{B})
并且 B 部分将由引擎序列化。在 STORABLE_thaw
中,您将获得对 B' 对象的引用,该引用已为您反序列化。
因此,通常应避免递归,但仍支持递归。
CPAN 上提供了一个 Clone 模块,它以本机方式实现深度克隆,即无需冻结到内存并解冻结果。它的目标是将来替换 Storable 的 dclone()。但是,它目前不支持 Storable 挂钩来重新定义执行深度克隆的方式。
是的,有很多 :-) 但更准确地说,在 UNIX 系统中有一个名为 file
的实用程序,它根据数据文件的内容(通常是它们的前几个字节)识别数据文件。为此,需要向名为 magic 的特定文件教授数据的 签名。该配置文件所在的位置取决于 UNIX 的版本;通常类似于 /usr/share/misc/magic 或 /etc/magic。您的系统管理员需要更新 magic 文件。通过调用 Storable::show_file_magic(),将必要的签名信息输出到 STDOUT。请注意,GNU 实现的 file
实用程序版本 3.38 或更高版本,除了其他类型的 Perl 文件外,还应包含对识别 Storable 文件的开箱即用支持。
您还可以使用以下函数从 Storable 映像中提取文件头信息
如果给定文件是 Storable 映像,则返回一个描述它的哈希。如果该文件可读,但不是 Storable 映像,则返回 undef
。如果文件不存在或不可读,则 croak。
返回的哈希具有以下元素
version
这将返回文件格式版本。它是一个类似于 "2.7" 的字符串。
请注意,此版本号与 Storable 模块本身的版本号不同。例如,Storable v0.7 创建 v2.0 格式的文件,而 Storable v2.15 创建 v2.7 格式的文件。仅在添加会混淆模块旧版本的附加功能时,文件格式版本号才会递增。
早于 v2.0 的文件将具有版本号 "-1"、"0" 或 "1" 之一。当时没有使用次要版本号。
version_nv
这将以数字形式返回文件格式版本。它是一个类似于 "2.007" 的字符串。此值适用于数字比较。
常量函数 Storable::BIN_VERSION_NV
返回一个可比较的数字,该数字表示此版本的 Storable 完全支持的最高文件版本号(但请参阅上面对 $Storable::accept_future_minor
的讨论)。常量 Storable::BIN_WRITE_VERSION_NV
函数返回写入的文件版本,在某些配置中可能小于 Storable::BIN_VERSION_NV
。
major
、minor
这还返回文件格式版本。如果版本为“2.7”,则 major 为 2,minor 为 7。当 major 小于 2 时,minor 元素缺失。
hdrsize
这是可存储标头占用的字节数。
netorder
如果图像存储数据按网络顺序排列,则此项为 TRUE。这意味着它是使用 nstore() 或类似方式创建的。
byteorder
仅当 netorder
为 FALSE 时才存在此项。它是创建此图像的 perl 的 $Config{byteorder} 字符串。它是一个字符串,如“1234”(32 位小端)或“87654321”(64 位大端)。这必须与当前 perl 匹配,以便 Storable 可以读取图像。
intsize
、longsize
、ptrsize
、nvsize
仅当 netorder
为 FALSE 时才存在这些项。这些是创建此图像的 perl 的各种 C 数据类型的尺寸。这些必须与当前 perl 匹配,以便 Storable 可以读取图像。
nvsize
元素仅存在于文件格式 v2.2 及更高版本中。
file
文件名。
$buffer 应为可存储图像或其前几个字节。如果 $buffer 以可存储标头开头,则返回描述图像的哈希,否则返回 undef
。
哈希的结构与 Storable::file_magic() 返回的哈希相同。如果图像为文件图像,则 file
元素为 true。
如果提供了 $must_be_file 参数并且为 TRUE,则除非图像看起来像属于文件转储,否则返回 undef
。
可存储标头的最大大小当前为 21 字节。如果提供的 $buffer 只是可存储图像的第一部分,则它至少应该这么长,以确保 read_magic() 将其识别为可存储图像。
以下是一些代码示例,展示了 Storable 的可能用法
use Storable qw(store retrieve freeze thaw dclone);
%color = ('Blue' => 0.1, 'Red' => 0.8, 'Black' => 0, 'White' => 1);
store(\%color, 'mycolors') or die "Can't store %a in mycolors!\n";
$colref = retrieve('mycolors');
die "Unable to retrieve from mycolors!\n" unless defined $colref;
printf "Blue is still %lf\n", $colref->{'Blue'};
$colref2 = dclone(\%color);
$str = freeze(\%color);
printf "Serialization of %%color is %d bytes long.\n", length($str);
$colref3 = thaw($str);
它打印(在我的机器上)
Blue is still 0.100000
Serialization of %color is 102 bytes long.
CODE 引用序列化和在安全模块中的反序列化
use Storable qw(freeze thaw);
use Safe;
use strict;
my $safe = new Safe;
# because of opcodes used in "use strict":
$safe->permit(qw(:default require));
local $Storable::Deparse = 1;
local $Storable::Eval = sub { $safe->reval($_[0]) };
my $serialized = freeze(sub { 42 });
my $code = thaw($serialized);
$code->() == 42;
不要接受来自不受信任来源的 Storable 文档!没有办法配置 Storable,以便可以安全地使用它来处理不受信任的数据。虽然有各种选项可用于缓解特定安全问题,但这些选项不构成用户的完整安全网,处理不受信任的数据可能会导致分段错误、远程代码执行或权限提升。以下列出了一些已知功能,这些功能代表了应由本模块用户考虑的安全问题。
最明显的是,可选(默认情况下关闭)CODE 引用序列化功能允许将代码传输到反序列化进程。此外,任何序列化的对象都会导致 Storable 有助于在反序列化模块中加载与对象类对应的模块。对于经过处理的模块名称,这几乎可以加载任意代码。最后,当对象在反序列化过程中被销毁时,将调用反序列化对象的析构函数。恶意制作的 Storable 文档可能会将此类对象放入哈希键的值中,该哈希键被同一哈希中的另一个键/值对覆盖,从而导致立即执行析构函数。
要在解冻/检索时禁用祝福对象,请从 $Storable::flags
中删除标志 BLESS_OK
= 2 或将 thaw/retrieve 的第二个参数设置为 0。
要在解冻/检索时禁用绑定数据,请从 $Storable::flags
中删除标志 TIE_OK
= 4 或将 thaw/retrieve 的第二个参数设置为 0。
在 $Storable::flags
= 6 的默认设置下,即使是重命名的对象,创建或销毁随机对象也可以由攻击者控制。请参阅 CVE-2015-1592 及其 Metasploit 模块。
如果您的应用程序需要接受来自不受信任来源的数据,那么您最好使用功能较弱且更安全的序列化格式和实现。如果您的数据足够简单,Cpanel::JSON::XS 或 Data::MessagePack 是不错的替代方案。对于包含各种 Perl 特定数据类型(如正则表达式或别名数据)的更复杂的数据结构,Sereal 是最佳替代方案,并提供最大的互操作性。请注意,Sereal 默认情况下不安全,但您可以配置编码器和解码器以缓解任何安全问题。
如果您在哈希表中使用引用作为键,则在检索数据时肯定会感到失望。事实上,Perl 将用作哈希表键的引用字符串化。如果您稍后希望通过另一个引用字符串化访问这些项(即使用最初用于将值记录到哈希表中的引用来记录该值),则它将起作用,因为这两个引用字符串化成相同的字符串。
然而,它不会在 store
和 retrieve
操作序列中起作用,因为检索到的对象中的地址(它们是字符串化引用的部分)可能会与原始地址不同。您的结构的拓扑结构会保留,但不会保留那些隐藏的语义。
在重要的平台上,务必对传递给 Storable 函数的描述符调用 binmode()
。
规范地存储包含大型哈希的数据可能比正常存储相同的数据慢得多,因为必须分配、填充、排序和释放临时数组以保存每个哈希的键。一些测试表明存储速度减半——确切的损失将取决于您数据的复杂性。检索不会变慢。
Storable 现在对存储正则表达式有实验性支持,但有很大的局限性
需要 perl 5.8 或更高版本。
带有代码块的正则表达式,即 /(?{ ... })/
或 /(??{ ... })/
在解冻时将抛出异常。
正则表达式语法和标志在 perl 的历史中发生了变化,因此您在 perl 的一个版本中冻结的正则表达式可能无法在 perl 的另一个版本中解冻或表现出不同的行为。
根据 perl 的版本,正则表达式的行为可能会根据上下文而改变,但后来的 perl 会将该行为烘焙到正则表达式中。
如果无法解冻冻结的正则表达式,Storable 会抛出异常。
您不能存储 GLOB、FORMLINE 等。如果您能为这些操作定义语义,请随时增强 Storable 以便它能够处理它们。
除非您将 $Storable::forgive_me
设置为某个 TRUE
值,否则存储函数在遇到此类引用时将 croak
。在这种情况下,致命消息将转换为警告,并且会存储一些无意义的字符串。
设置 $Storable::canonical
可能会产生不等于比较的冻结字符串,因为数字可能会字符串化。当标量的字符串版本存在时,它是存储的形式;因此,如果你碰巧在同一数据结构上的两次冻结操作之间将数字用作字符串,你将得到不同的结果。
在网络顺序中存储双精度浮点数时,其值将存储为文本。但是,你也不应该期望非数字浮点值(例如无穷大和“非数字”)能够成功通过 nstore()/retrieve() 对。
由于 Storable 既不知道也不关心字符集(尽管它知道字符可能大于 8 位宽),因此主机和目标系统之间字符代码解释的任何差异都是你的问题。特别是,如果主机和目标使用不同的代码点来表示浮点数文本表示中使用的字符,你将无法交换浮点数据,即使使用 nstore() 也是如此。
Storable::drop_utf8
是一个简单的工具。没有办法将所有字符串作为 utf8 序列返回,或者尝试将 utf8 数据转换回 8 位,如果转换失败,则croak()
。
在 Storable 2.01 之前,存储时没有区分有符号整数和无符号整数。默认情况下,Storable 更喜欢存储标量的字符串表示(如果它有一个),所以这只会在大整数从未转换为字符串或浮点时存储时造成问题。换句话说,这些值是由整数运算(例如逻辑运算)生成的,然后在存储之前未在任何字符串或算术上下文中使用。
此部分仅适用于你在 Unix 或 Linux 上使用 perl 5.6.0 或 5.6.1 以及 Storable 2.02 或更早版本编写的现有数据,并且已配置为支持 64 位整数(不是默认值)。如果你得到了预编译的 perl,而不是运行 Configure 从源代码构建自己的 perl,那么它几乎肯定不会影响你,你现在就可以停止阅读(除非你好奇)。如果你在 Windows 上使用 perl,它不会影响你。
Storable 会编写一个文件头,其中包含各种 C 语言类型的尺寸,这些尺寸适用于构建 Storable 的 C 编译器(不按网络顺序写入时),并且会拒绝加载并非在相同(或兼容)架构上编写的 Storable 所编写的文件。需要进行此检查和机器字节顺序检查,因为文件中各种字段的尺寸由 C 语言类型的尺寸给出,因此在不同架构上编写的文件不兼容。这样做是为了提高速度。(按网络顺序写入时,所有字段都将按标准长度写出,这允许完全互操作,但读写时间更长)
Perl 5.6.x 引入了可选配置 perl 解释器以使用 C 的 long long
类型的功能,以便标量可以在 32 位系统上存储 64 位整数。但是,由于 Perl 配置系统在非 Windows 平台上生成 C 配置文件的方式,以及 Storable 生成其头的方式,Storable 文件头中没有任何内容反映 perl 写入是使用 32 位还是 64 位整数,尽管 Storable 在文件中以不同的方式存储某些数据。因此,在使用 64 位整数的 perl 上运行的 Storable 将从由 32 位 perl 编写的文件读取头,没有意识到数据实际上是以微妙的不兼容格式存在的,然后如果遇到存储的整数,就会出现严重错误(可能崩溃)。这是一个设计缺陷。
Storable 现在已更改为写出和读入包含有关整数大小的信息的文件头。不可能检测正在读入的旧文件是使用 32 位还是 64 位整数编写的(它们具有相同的文件头),因此不可能自动切换到正确的向后兼容模式。因此,此 Storable 默认为新的正确行为。
这意味着,如果您有由在 Unix 或 Linux 上配置了 64 位整数的 perl 5.6.0 或 5.6.1 上运行的 Storable 1.x 编写的文件,则默认情况下,此 Storable 将拒绝读取它,并给出错误 字节顺序不兼容。如果您有此类数据,则应将 $Storable::interwork_56_64bit
设置为 true 值,以使此 Storable 读写具有旧文件头的文件。您还应将您的数据或您正在与之通信的任何较旧 perl 迁移到此当前版本的 Storable。
如果您没有使用上面描述的 perl 特定配置写入数据,那么您没有必要也不应该做任何事情。不要设置标志 - 不仅在相同配置的 perl 上的 Storable 会拒绝加载它们,而且在不同配置的 perl 上的 Storable 会加载它们并认为它们是正确的,然后可能会在读取它们的过程中失败或崩溃。
感谢(按时间顺序)
Jarkko Hietaniemi <[email protected]>
Ulrich Pfeifer <[email protected]>
Benjamin A. Holzman <[email protected]>
Andrew Ford <[email protected]>
Gisle Aas <[email protected]>
Jeff Gresham <[email protected]>
Murray Nesbitt <[email protected]>
Marc Lehmann <[email protected]>
Justin Banks <[email protected]>
Jarkko Hietaniemi <[email protected]> (AGAIN, as perl 5.7.0 Pumpkin!)
Salvador Ortiz Garcia <[email protected]>
Dominic Dunlop <[email protected]>
Erik Haugan <[email protected]>
Benjamin A. Holzman <[email protected]>
Reini Urban <[email protected]>
Todd Rinaldo <[email protected]>
Aaron Crane <[email protected]>
对他们的错误报告、建议和贡献。
Benjamin Holzman 贡献了绑定的变量支持,Andrew Ford 贡献了哈希的规范顺序,Gisle Aas 修复了我对 perl 内部的一些误解,并通过简单地对对象进行计数而不是标记它们来优化输出流中“标签”的发射(导致 Storable 映像从 0.6 版本开始的二进制不兼容性——当然,旧映像仍然被正确理解)。Murray Nesbitt 使 Storable 线程安全。Marc Lehmann 添加了重载和对绑定项支持的引用。Benjamin Holzman 为重载类添加了性能改进;感谢 Grant Street Group 支付费用。Reini Urban 从 p5p 接管了维护,并添加了安全修复和大型对象支持。
Storable 由 Raphael Manfredi <[email protected]> 编写。维护现在由 cperl http://perl11.org/cperl 完成
请通过电子邮件向我们发送问题、错误修复、评论和投诉,但如果您有赞美,您应该将它们发送给 Raphael。请不要通过电子邮件向 Raphael 发送问题,因为他不再使用 Storable,并且您的消息将在转发给我们的过程中延迟。
克隆.