QuoteWie würdest du denn Datum+Uhrzeit+TZ kürzer in Bytes kodieren?
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 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
package ExifDate; # Zweckbestimmung: Auslesen Aufnahmedatum in IFD0 use strict; use warnings; use IO::File; require Time::Local; use IO::String; sub new{ my $class = shift; my $file = shift; # lokale Datei oder binary return eval{ my $self = bless{}, $class; if( -f $file ){ $self->{FH} = IO::File->new; $self->{FH}->open($file, O_BINARY|O_RDONLY) or die $^E; } else{ $self->{FH} = IO::String->new; $self->{FH}->print( $file ); $self->{FH}->seek(0,0); } $self->{offsetIFD0} = $self->findIFD0; if( $self->{offsDateTime} = $self->findMarker($self->{offsetIFD0}, 0x0132) ){ $self->{DateTime} = $self->readDateTime($self->{offsDateTime}); } else{ die "Kein Datum in Bilddatei\n"; } $self; }; } # Jeder Eintrag einer IFD hat eine Länge von 12 Bytes # Lese an $entryOffset 2, 2, 4, 4 Bytes # |_________ Daten des Eintrags (bei einfachen Daten) oder Verweis auf die Daten # |____________ Länge der Daten des Eintrags als 32-Bit-Wert # |_______________ Datentyp des Eintrags # |__________________ Typ des Eintrags als 16-Bit-Wert (der sog. Marker) sub readDateTime{ my $self = shift; my $offs = shift; my $rec = $self->readon(12,$offs); my $mask = $self->{bigEnd} ? "nnNN" : "vvVV"; my ($marker, $type, $len, $val) = unpack $mask, $rec; # Hilfreich beim Entwickeln # my $info = sprintf "MARKER: 0x%02X Typ:%d Len:%d Val:%d\n", $marker, $type, $len, $val; # MARKER: 0x132 Typ:2 Len:20 Val:2376 #print "len: $len\n"; # 20 2022-06-08 06:59:55 return unpack "A*", $self->readon($len, $val + $self->{tiffOffset}); } sub readon{ my $self = shift; my $fh = $self->{FH}; # hier das FileHandle my $len = shift; my $offs = shift; $offs = defined $offs ? $offs : $fh->tell; $fh->seek($offs,0); $fh->read(my $buffer, $len); return $buffer; } sub findIFD0{ my $self = shift; my $IntelString = pack("C8", 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x49, 0x49); my $MotorolaString = pack("C8", 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x4D, 0x4D); my $offset = 0; my @fifo = (); for( $offset = 0; $offset < 50; $offset++ ){ my $byt = $self->readon(1); push @fifo, $byt; shift @fifo if scalar @fifo > 8; if( join('', @fifo) eq $IntelString){ $self->{format} = 'Intel'; $self->{bigEnd} = 0; last; } elsif( join('', @fifo) eq $MotorolaString){ $self->{format} = 'Motorola'; $self->{bigEnd} = 1; last; } } die "Keine Exif-Daten in Bilddatei\n" if $offset > 49; $self->{tiffOffset} = $offset - 1; $self->{IFD0pointer} = $self->getUint32($self->{tiffOffset}+4); die "Not Valid Tiff-Data\n" if $self->{IFD0pointer} < 8; return $self->{IFD0pointer} + $self->{tiffOffset}; } sub getUint16{ my $self = shift; my $offs = shift; my $bin = $self->readon(2,$offs); return $self->{bigEnd} ? unpack "n", $bin : unpack "v", $bin; } sub getUint32{ my $self = shift; my $offs = shift; my $bin = $self->readon(4,$offs); return $self->{bigEnd} ? unpack "N", $bin : unpack "V", $bin; } # ExifIFDPointer' => 'MARKER: 0x8769 Typ:4 Len:1 Val:118 sub readTagValue{ my $self = shift; my $offs = shift; my $rec = $self->{FH}->readon(12,$offs); my $mask = $self->{bigEnd} ? "nnNN" : "vvVV"; my ($marker, $type, $len, $val) = unpack $mask, $rec; # Hilfreich beim Entwickeln #my $info = sprintf "MARKER: 0x%02X Typ:%d Len:%d Val:%d", $marker, $type, $len, $val; return $val; } sub findMarker{ my $self = shift; my $dirStart = shift; my $marker = shift; my $entries = $self->getUint16($dirStart); for (my $i = 0; $i < $entries; $i++) { my $entryOffset = $dirStart + $i*12 + 2; my $m = $self->getUint16($entryOffset); #printf "%04X\n", $m; if( $m == 0x8769 ){ $self->{ExifIFDPointer} = $self->readTagValue($entryOffset); } return $entryOffset if $marker == $m; } # versuche ExifPointer zu finden # 0x8769 => "ExifIFDPointer", return; } sub DateTime{ my $self = shift; my($date, $time) = split /\s+/, $self->{DateTime}; $date =~ s/:/-/g; return "$date $time"; } sub gmtime{ my $self = shift; my ($date, $xtime) = split /\s/, $self->DateTime; my ($year, $mon, $mday) = split "-", $date; my ($hour, $min, $sec) = split ":", $xtime; my $time = Time::Local::timegm( $sec, $min, $hour, $mday, $mon-1, $year ); return wantarray ? ($sec, $min, $hour, $mday, $mon, $year) : $time; } 1;######################################################################### __END__ package main; use strict; use Data::Dumper; use IO::File; my $file = "/fotoarchiv/2022/06/08.06.2022_003.jpg"; my $fh = IO::File->new(); $fh->open($file, O_BINARY|O_RDONLY) or die $!; read($fh, my $bin, -s $fh); print ExifDate->new($bin)->DateTime, "\n", join "\n",ExifDate->new($file)->gmtime;
1
2
3
4
5
6
# Jeder Eintrag einer IFD hat eine Länge von 12 Bytes
# Lese an $entryOffset 2, 2, 4, 4 Bytes
# |_________ Daten des Eintrags (bei einfachen Daten) oder Verweis auf die Daten
# |____________ Länge der Daten des Eintrags als 32-Bit-Wert
# |_______________ Datentyp des Eintrags
# |__________________ Typ des Eintrags als 16-Bit-Wert (der sog. Marker)