0

以下のスクリプトを実行すると、破損したyamlファイルが次のように取得されます

--- 
1: 
  name1: abc
  name2: abc
---
me3: abc
---

質問

私が間違っていることを誰かが見ることができますか?

#!/usr/bin/perl

use strict;
use YAML::Syck;
use Fcntl ':flock', 'SEEK_SET';
use warnings;
use Data::Dumper;

my $acc;
my $acc_fh;

$acc->{1}{name1} = "abc";

unlink 'test.yaml';

# write initial
open F, '>', 'test.yaml';
print F YAML::Syck::Dump($acc);
close F;


($acc, $acc_fh) = read_yaml_with_lock('test.yaml');
$acc->{1}{name2} = "abc";
$acc->{1}{name3} = "abc";
write_yaml_with_lock($acc, $acc_fh);

($acc, $acc_fh) = read_yaml_with_lock('test.yaml');
delete $acc->{1}{name3};
write_yaml_with_lock($acc, $acc_fh);


sub read_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);
}

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;
}
4

2 に答える 2

3

その"me3"部分はの残りで、部分的に(4 文字)" name3"で上書きされます。"---\n"初めて書き込むときは、より多くのデータがあります。次に、ファイル ハンドルの位置を巻き戻し、古いデータをすべて上書きしない短いデータを書き込みます。

あなたの解決策は、ファイルハンドルを渡して巻き戻すことをスキップし、代わりopenに各サブルーチンに適切なものを使用することであると思います。例えば:

sub read_yaml {
    my $file = shift;
    open my $fh, '<', $file or die $!;
    ...
    close $fh;
}

sub write_yaml {
    my ($file, $obj) = @_;
    open my $fh, '>', $file or die $!;
    ...
    close $fh;
}

操作の合間にファイルハンドルを開いたままにしておくことは、実際にはそれほど有用でも効率的でもなく、いくつかの問題を引き起こします。

于 2012-09-05T14:11:51.303 に答える
3

同じファイルに 2 回書き込みます。2 回目に記述している YAML コードは、呼び出しの間にそのハッシュ キーを削除するため、1 回目よりも短くなります。ただし、最初にファイルを作成したり、2 回目に書き込みを行ったりすることはunlinkありません。truncateしたがって、破損として表示されるのは、最初に書き込まれ、2 回目には上書きされていないファイルの部分です。

于 2012-09-05T14:08:32.443 に答える