1 2 3 4
my $file = 'd:/tmp/numbers.bin'; tie my $nr1, 'Number', (-file => $file, -key => 'foo') or die $!; tie my $nr2, 'Number', (-file => $file, -key => 'bar') or die $!; print "$nr1 $nr2\n";
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
package Number; use strict; use warnings; use IO::File; use Carp; use Fcntl ':flock'; use Tie::Scalar; our @ISA = qw(Tie::StdScalar); # VARs intern my $fh = new IO::File; my %rep = (); # Repository my $repkey = undef; # Pepository Key # Constructor sub TIESCALAR{ my $class = shift; my %args = ( -file => undef, -key => undef, -auto => 1, @_ ); croak "No file, use -file in args!" if not defined $args{-file}; croak "No key, use -key in args!" if not defined $args{-key}; deserialize($args{-file}) or return; $repkey = $args{-key} || '_default'; $rep{$repkey} ||= 0; $rep{$repkey}++ if $args{-auto} == 1; my $nr = $rep{$repkey}; return bless \$nr, $class; } # hash to file sub serialize{ if(defined $fh){ truncate $fh, 0; seek $fh, 0, 0; while(my ($k, $v) = each %rep){ print $fh pack("N", length $k).$k.pack("N", $v); } } } # hash from $file sub deserialize{ my $file = shift; $fh->open($file, O_CREAT|O_BINARY|O_RDWR) or return; flock($fh, LOCK_EX) or die "Your system does not support flock()!"; binmode $fh, ':raw'; seek $fh, 0, 0; my ($buffer, $key, $klen, $number) = (undef, undef, undef, undef); while(read $fh, $buffer, 4){ $klen = unpack "N", $buffer; read $fh, $key, $klen; read $fh, $buffer, 4; $rep{$key} = unpack "N", $buffer; } return 1; } sub DESTROY{ serialize(); undef $fh; } ########################################################################### 1; ######################################################################## ###########################################################################
2011-04-06T06:15:52 rostiSchau dir mal meinen zweiten Code an. Dort handhabe ich Schlüssel, das ganze ist sogar noch eine Ebene komplexer als du es machen willst, da hinter dem Hash mit den Schlüsseln noch Arrays sitzen. Das funktioniert problemlos. Das mehrfache Initialisieren kannst du vermeiden, wenn du schaust, ob die Datei schon geladen wurde und dann nur speicherst, wenn alle Referenzen aufgelöst wurden (z.B. durch verlassen des Wertebereiches).Aufgrund dessen, dass bei jedem tie()-Aufruf die Zuordnung zum -key überschrieben wird, komme ich zu dem Schluss, dass Tie::Scalar als Baseclass hierzu ungeeigent ist.
1 2 3 4 5
tie(my $x1, 'Tie::Scalar::StorageKeyList', file=>'test.bin', key=>'foo', position=>0, value=>0); $x1++; tie(my $x2, 'Tie::Scalar::StorageKeyList', file=>'test.bin', key=>'bar', position=>0, value=>0); $x2++;
QuoteJedoch würde ich zu gerne wissen, welche Vorteile du in deinem Anwendungsfall von tie bezüglich einer normalen Funktion siehst.
1
2
3
4
5
6
Rate serialize store
serialize 3704/s -- -48%
store 7143/s 93% --
Rate deserialize retrieve
deserialize 3448/s -- -61%
retrieve 8929/s 159% --
1
2
3
4
5
6
7
8
9
10
11
12
Benchmark: timing 10000 iterations of serialize, store...
serialize: 26 wallclock secs ( 4.44 usr + 17.41 sys = 21.84 CPU) @ 457.81/s (n=10000)
store: 42 wallclock secs ( 4.25 usr + 14.83 sys = 19.08 CPU) @ 524.16/s (n=10000)
Rate serialize store
serialize 458/s -- -13%
store 524/s 14% --
Benchmark: timing 10000 iterations of deserialize, retrieve...
deserialize: 9 wallclock secs ( 3.86 usr + 4.27 sys = 8.12 CPU) @ 1230.77/s (n=10000)
retrieve: 6 wallclock secs ( 2.23 usr + 2.42 sys = 4.66 CPU) @ 2147.77/s (n=10000)
Rate deserialize retrieve
deserialize 1231/s -- -43%
retrieve 2148/s 75% --
2011-04-06T07:56:39 rostiQuoteJedoch würde ich zu gerne wissen, welche Vorteile du in deinem Anwendungsfall von tie bezüglich einer normalen Funktion siehst.
Mit tie() wird es modular. Und ich kann auch mal einen numerischen Wert (da muss noch eine Prüfung rein, dass der Wert auch wirklich numerisch ist) ggf. persistent ändern.
2011-04-06T07:56:39 rostiEine Anwendung ist das Abholen meiner Mails per Script vom POP3-Server, da brauche ich fortlaufende Nummern für die lokalen Dateinamen; hier haben wir den Fall, dass beim Starten des Scripts einmal automatisch hochgezählt wird und dann das Script je empfangene Mail selbst weiterzählt. Es sind auch Mails von mehreren POP's anzuholen....
1 2 3 4 5 6
my $counter=Conter->new(file=>$file,keys=>[qw(foo bar bam)]); $counter->up('foo'); # oder mit dynamischer Methoden-Deklaration: $counter->foo_up(); # oder mit overload: $counter+="foo";
1 2
tie (my %counter, 'Tie::Counter', file=>$file, keys=>[qw(foo bar bam)]); $counter{foo}++;
1 2 3
tie my %h, 'Numbers', $file; my $obj = tied %h; $obj->export2mysql; # further methods..
QuoteADVISORY LOCKING
The "lock_store" and "lock_nstore" routine are equivalent to "store" and "nstore", except that they get an exclusive lock on the file before writing. Likewise, "lock_retrieve" does the same as "retrieve", but also gets a shared lock on the file before reading.
As with any advisory locking scheme, the protection only works if you systematically use "lock_store" and "lock_retrieve". If one side of your application uses "store" whilst the other uses "lock_retrieve", you will get no protection at all.
The internal advisory locking is implemented using Perl's flock() routine. If your system does not support any form of flock(), or if you share your files across NFS, you might wish to use other forms of locking by using modules such as LockFile::Simple which lock a file using a filesystem entry, instead of locking the file descriptor.
2011-04-06T17:12:40 topegDu kannst auch auf ein Filehandle schreiben:
Dann musst du seek($fh, 0,0); und lock($fh, LOCK_EX); selber machen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
package Numbers; use strict; use warnings; use IO::File; use Carp; use Fcntl ':flock'; use Tie::Hash; our @ISA = qw(Tie::StdHash); use Storable qw(fd_retrieve store_fd); # VARs intern my $fh = new IO::File; # LOCK_EX my $init = 0; # Constructor sub TIEHASH{ my $class = shift; my $ref = shift or return; croak "No ref HASH in Arg" if ref $ref ne 'HASH'; croak "No file is given, use {-file => \$file} in Args" if not exists $ref->{-file}; my $self = _initialize($ref->{-file}) or return; # IO-Error # apply autoincrement for custom keys like 'foo', 'bar' if(exists $ref->{-auto}){ if(ref $ref->{-auto} eq 'ARRAY'){ foreach my $k(@{$ref->{-auto}}){ $self->{$k}++; } } else{ carp "Second Arg must be a ARRAY-Ref ['foo','bar']" } } return bless $self, $class; } # hash from $file sub _initialize{ my $file = shift; return if $init == 1; $init = 1; $fh->open($file, O_CREAT|O_BINARY|O_RDWR) or return; flock($fh, LOCK_EX) or carp "Your system does not support flock()!"; binmode $fh, ':raw'; my $ref = {}; eval { $ref = fd_retrieve($fh) }; # caught exception: file is void if($@){ return {} } else { return $ref } } # hash to file sub _serialize{ my $ref = shift; seek $fh, 0, 0; truncate $fh, 0; store_fd($ref, $fh); undef $fh; } # Overload method, make sure that value is numeric sub STORE{ my $self = shift; my $key = shift; my $value = shift; if($value =~ /^\d+$/){ $self->{$key} = $value; } else{ carp "Value is not numeric"; } } sub DESTROY{ my $self = shift; _serialize($self); } ########################################################################### 1; ######################################################################## ########################################################################### package main; my $file = 'd:/tmp/storednumbers.bin'; tie my %h, 'Numbers', {-file => $file, -auto => ['foo','bar']} or die $!; foreach my $k(keys %h){ print "$k => $h{$k}\n"; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
#!/usr/bin/perl use strict; use warnings; use PersistentCounter; my $file1 = 'storecounter1.bin'; tie my %h1, 'PersistentCounter', {-file => $file1, -increment => ['foo','bar']} or die $!; my $file2 = 'storecounter2.bin'; tie my %h2, 'PersistentCounter', {-file => $file2, -increment => ['bam','bur']} or die $!; print "H1\n"; foreach my $k (sort keys %h1) { print "$k => $h1{$k}\n"; } print "H2\n"; foreach my $k (sort keys %h2) { print "$k => $h2{$k}\n"; } tie my %h3, 'PersistentCounter', {-file => $file1} or die $!; $h1{bar}++; $h3{bar}++; print "H3\n"; foreach my $k (sort keys %h3) { print "$k => $h3{$k}\n"; }
QuoteWas mir an dem Modul nicht so recht gefällt ist...
2011-04-07T08:09:18 rostiEin paar Fragen dazu: Hast Du mit use File::Spec; da noch was vor, wenn ich das auskommentiere, ist es ohne Effekt.
2011-04-07T08:09:18 rostiUnd was ist der Unterschied zwischen Tie::ExtraHash und Tie::StdHash?
2011-04-07T08:09:18 rostiMeine alte Perl-Version 5.6.1 kennt nur StdHash
2011-04-07T08:09:18 rostiAlso: brauchen wir ein require v??? für Deine Änderungen?
2011-04-07T08:09:18 rostiWir brauchen entweder noch ein paar Methoden für $ob = tied(%hash); oder überschreiben DELETE() und STORE(), damit Keys zur Laufzeit gelöscht oder hinzugefügt werden können.
Edit2: Ok, der Sinn predefined Keys ist klar. STORE() funktioniert nur mit predefined Keys und ist gut so. Ich überschreibe mal DELETE() so, dass es mit predefined keys (und nur mit diesen) tut.
2011-04-07T08:09:18 rostiuse Yes;
Ich schreibe die POD und teste das Modul bis zur Produktionsreife.
=item Authors
Du und ich ;)
1 2
# Not supported sub CLEAR{ carp q(Clear is not supported, use delete $hash{$key} for specified $key) }
2011-04-07T14:48:51 rosti[0] operativer Hash, nur predefined Keys
[1] file und fh
[2] entire Hash, hinzugekommen
2011-04-07T14:57:50 topegIch habe die Interne Struktur völlig geändert. Alle Daten stecken jetzt nur noch in %files
Die Struktur ist da: $files{$file}={ data=>{...}, fh=>..., file=>'...', objcount=>... }
Darum habe ich auch direkt von TIE::HASH geerbt.
2011-04-07T14:48:51 rosti->[0] operativer Hash
->[1] file und fh