Perl 使用“each”进行迭代时从DB_文件绑定哈希中删除项目不安全? 问题

Perl 使用“each”进行迭代时从DB_文件绑定哈希中删除项目不安全? 问题,perl,each,netbsd,tie,Perl,Each,Netbsd,Tie,我使用的是NetBSD 6.1、Perl v5.18.1和DB_文件v1.818。如果我使用each迭代DB_文件绑定的散列,并从散列中删除每个项,则不会删除所有项。下面是一个演示问题的脚本: use strict; use warnings; use DB_File; my $dbfile = "/tmp/foo.db"; ! -f $dbfile or unlink($dbfile) or die("unable to delete $dbfile"); my %db; tie(%db,

我使用的是NetBSD 6.1、Perl v5.18.1和DB_文件v1.818。如果我使用
each
迭代DB_文件绑定的散列,并从散列中删除每个项,则不会删除所有项。下面是一个演示问题的脚本:

use strict;
use warnings;
use DB_File;

my $dbfile = "/tmp/foo.db";
! -f $dbfile or unlink($dbfile) or die("unable to delete $dbfile");

my %db;
tie(%db, "DB_File", "/tmp/foo.db", O_RDWR|O_CREAT, 0644);

# add some random records
my @chars = ("0".."9", "a".."f");
for (1..10) {
    my ($key, $val);
    $key .= $chars[rand(@chars)] for 1..10;
    $val .= $chars[rand(@chars)] for 1..32;
    $db{$key} = $val;
}

# this doesn't delete everything from the database!
keys(%db); # reset the iterator
while (my ($key, $val) = each(%db)) {
    delete $db{$key};
}

foreach (keys(%db)) {
    print("\$db{$_} = $db{$_}\n");
}

untie(%db);
当我运行它时,10条记录中的4条(有时5条)不会被删除:

$db{4a8e5792e0}=7a4d078a3f0f3cba750cb395fcc3343d
$db{d28e8cb226}=17af1122f0b94113416693b1c4165954
$db{a3ae4e2e24}=3c15270cf16601722bd8106b1727dbc2
$db{886c469eb4}=f1496f83f7866d09c9e28aae8e1b62e6
$db{2c53ebd993}=facfe8228240878aac825de4d97ca22b
如果我在Linux(Ubuntu14.04)系统上运行该脚本,那么它总是有效的(所有记录都被删除)

如果我切换到键上的
foreach
循环,那么它可以在NetBSD和Linux上工作:

# this always works
foreach (keys(%db)) {
    delete $db{$_};
}
文件上说了什么 我还没有找到任何东西清楚地表明,通过
每次迭代时进行删除并不总是有效的

以下是我能够找到的:

  • 报告说:

    foreach
    如果VAR是一个绑定变量或其他特殊变量,则可能不会达到您预期的效果

    我不知道这是什么意思,但奇怪的是,
    foreach
    案例就是它的工作原理

  • 报告说:

    在散列中的任何插入都可能改变顺序,任何删除都可能改变顺序,但
    每个
    返回的最新键可以在不改变顺序的情况下删除


    对我来说,这意味着在迭代时删除当前条目是安全的

  • 在迭代时,没有提到删除

问题 这是一个问题:

  • 是由计算机中的一个错误引起的
  • 由DB_文件中的错误引起的
  • 每个
的已知限制
  • 绑定哈希的已知限制
  • DB_文件绑定哈希的已知限制

  • 为什么键上的
    foreach
    可以工作,而
    每个都不能工作?

    我的直觉是,这种行为是绑定哈希的限制。似乎无法保证DB_文件在删除最近获取的密钥时不会重新刷新

    你还问了他们之间的区别

    while (my ($key, $val) = each(%db)) {
        delete $db{$key};
    }
    

    在第一种情况下,您在迭代绑定散列时正在处理绑定散列。因此,迭代器可能无法访问所有键


    在第二种情况下,您首先迭代绑定哈希,以获得完整的键列表。在完整列表上循环时,保证删除所有条目。

    “对我来说,这意味着在迭代时删除当前条目是安全的。”对于散列,是的。但你这里没有杂烩。Perl的文档无法声明DB_文件支持什么或不支持什么。@ikegami:好的,这样就排除了“每个
    的已知限制”。是吗?哦,我有个错误的印象,
    keys()
    发出了一个类似Python的“生成器”,用于在非常大(可能是绑定的)散列上进行高效迭代。很高兴知道,谢谢!假设可以使用NetBSD的Berkeley DB实现编写一个C程序,该实现可以在迭代时删除,而不会使迭代器失效,那么应该可以编写类似的DB_文件。因此,在我看来,这似乎是一个组合:(1)捆绑让行为取决于实现(允许实现表现良好或糟糕),以及(2)DB_文件的实现没有最佳地使用DB接口,或者NetBSD DB实现使得在迭代时无法安全地删除(很高兴知道是哪个)。
    foreach (keys(%db)) {
        delete $db{$_};
    }