Thread 20 Byte für Datum und Uhrzeit
(5 answers)
Opened by rosti at 2025-05-19 18:06 Quote Je nach Endian pack("N", $timestamp); oder pack("V", $timestamp); ergibt genau 4 Bytes. Die Image-File-Directory (IFD) ist ein ziemliches Wirrwar. Um an das Datum zu kommen, muß man die ganze IFD0 auslesen und den das Datum betreffenden Marker herausfischen. Damit bekommt man dann den Offset wo man seinen Dateizeiger draufsetzen darf um da diese 20 Bytes 2025:05:7 11:11:11 lesen zu dürfen. Also wenn man von Exif nur das Datum haben will, geht das noch relativ zügig. Ih habe mein Fotoarchiv (ca. 30 Tausend jpeg) mal drüberlaufen lassen, das war in einer Minute erledigt. Untenstehend die ganze Klasse. Code (perl): (dl
)
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; |