Perl: Bytes einer Binär-Datei rausgrabbeln

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
Benutzeravatar
mistersixt
Beiträge: 6601
Registriert: 24.09.2003 14:33:25
Lizenz eigener Beiträge: GNU Free Documentation License

Perl: Bytes einer Binär-Datei rausgrabbeln

Beitrag von mistersixt » 14.01.2005 08:02:20

Moin moin,

ich habe hier eine Binär-Datei mit ein paar 1000 Nachrichten, die immer genau 108 Bytes lang sind. Die 108 Byte beinhalten Datenfelder immer gleicher Länge. Wie kann ich jetzt in Perl beispielsweise die Bytes 70 bis 77 einer Variablen zuweisen? Mit split komme ich nicht weiter, soll es substr sein oder gibt es noch eine bessere Methode?


Danke und Gruss, mistersixt

PS: Nächste Frage: wie lese ich eine Binär-Datei ein? Wenn ich die mit while (<>) einlese, erkennt er mir ein paar 1000 Zeilen(!), die Anzahl ist aber unterschiedlich zu der Anzahl der 108-Byte langen Nachrichten :( .
--
System: Debian Bookworm, 6.11.x.-x-amd64, ext4, AMD Ryzen 7 3700X, 8 x 3.8 Ghz., Radeon RX 5700 XT, 32 GB Ram, XFCE

geier369
Beiträge: 32
Registriert: 01.09.2004 07:49:17

Beitrag von geier369 » 14.01.2005 08:50:26

Hallo,

glaube read() könnte dir helfen,
liest entweder chars oder Bytes ein.

siehe perldoc -f read

read( DATEIHANDLE, VAR, LENGTH,OFFSET )
Also in etwa:

open(BINDAT, "<:raw", $path) or die "fuck it!";
$offset = 69;
while( read( BINDAT, $var, 7, $offset )){
do something with $var## die Zeile ist kein Perlcode :)
$offset += 108;
}
close(BINDAT);
Viel Spass beim perlen

Benutzeravatar
mistersixt
Beiträge: 6601
Registriert: 24.09.2003 14:33:25
Lizenz eigener Beiträge: GNU Free Documentation License

Beitrag von mistersixt » 14.01.2005 09:01:21

Hossa, getestet und sieht sehr vielversprechend aus, danke!

Gruss, mistersixt.
--
System: Debian Bookworm, 6.11.x.-x-amd64, ext4, AMD Ryzen 7 3700X, 8 x 3.8 Ghz., Radeon RX 5700 XT, 32 GB Ram, XFCE

Benutzeravatar
mistersixt
Beiträge: 6601
Registriert: 24.09.2003 14:33:25
Lizenz eigener Beiträge: GNU Free Documentation License

Beitrag von mistersixt » 14.01.2005 09:35:30

Mmmh, habe hier doch einen komischen Effekt, erstmal der Code:

Code: Alles auswählen

#!/usr/bin/perl
#
$DATEI = "/pub/message.dat";

open(BINDAT, "<:raw", $DATEI) or die "Datei oeffnen geht nicht";
$OFFSET = 67;
while( read( BINDAT, $DATENSATZ, 11, $OFFSET )){  
        print $DATENSATZ;
        $OFFSET += 108;
}
close(BINDAT); 
Ab Byte 68 gibt es immer 11 ASCII-Zeichen. Wenn ich das nun laufen lassen (./scan_mls_log.pl | less) bekomme ich ein wildes Durcheinander angezeigt, lenke ich die Ausgabe in eine Datei um, wächst diese ins Unendliche an (sprich, da scheint was zu loopen). Jemand eine Idee, was ich falsch mache?

Danke, mistersixt.
--
System: Debian Bookworm, 6.11.x.-x-amd64, ext4, AMD Ryzen 7 3700X, 8 x 3.8 Ghz., Radeon RX 5700 XT, 32 GB Ram, XFCE

perls255
Beiträge: 46
Registriert: 05.01.2005 13:54:24
Wohnort: München

Beitrag von perls255 » 14.01.2005 09:49:16

Hi,

<Antwort nach grobem Überflilegen des Themas ohne genaue Auseinandersetzung damit>

Wenn Du in Perl mit Binärdateien zu tun hast, schau Dir auf jeden Fall die Funktionen "pack" und "unpack" an. Ist anfangs etwas gewöhnungsbedürftig, aber genau für Binär-Daten gemacht.

HTH

Stephan

geier369
Beiträge: 32
Registriert: 01.09.2004 07:49:17

Beitrag von geier369 » 14.01.2005 10:08:42

Probier mal so,
Könnte sein das du ne Abruchbedingung brauchst in der Schleife:

#!/usr/bin/perl
#
$DATEI = "/pub/message.dat";

open(BINDAT, "<:raw", $DATEI) or die "Datei oeffnen geht nicht";
open(OUTPUT,">> logfile.dat) or die "bla";
$size = (stat($DATEI))[7]; Dateigroesse
$OFFSET = 67;
while( read( BINDAT, $DATENSATZ, 11, $OFFSET )){
print OUTPUT $DATENSATZ . "\n";
$DATENSATZ = "";
$OFFSET += 108;
if( $OFFSET > $size ){ last }
}
close(BINDAT);


Ne andere Sache ist, wenn du ASCII rausfiltern willst,
kannst du mit dem shellprog strings eventuell die Binärdaten
vorher entfernen
$out = `strings $DATEI`;

Benutzeravatar
mistersixt
Beiträge: 6601
Registriert: 24.09.2003 14:33:25
Lizenz eigener Beiträge: GNU Free Documentation License

Beitrag von mistersixt » 14.01.2005 10:18:42

Hallo geier,

der Offset scheint nicht zu funktionieren, ich habe jetzt mal ff. gemacht:

Code: Alles auswählen

#!/usr/bin/perl
#
$DATEI = "/tmp/message.dat";
open(BINDAT, "< $DATEI") or die "Datei oeffnen geht nicht"; 
binmode(BINDAT);
$OFFSET = 68;

$hossa = read( BINDAT, $DATENSATZ, 11, $OFFSET );

foreach (split(//, $DATENSATZ)) {
        printf("%02x ", ord($_));
}
print "\n";
close(BINDAT);
Hier bekomme ich als Ausgabe 68 mal "00" und anschliessend die allerersten(!) 11 Byte der Datei (anstatt 11 Byte ab Byte Position 68 ):

Code: Alles auswählen

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1f 00 00 00 00 00 03 30 35 3a
ISprich, "00 1f 00 00 00 00 00 03 30 35 3a" sind die ersten 11 Byte und nicht die 11 Byte ab Position 68 :( . In $hossa steht 11.

Was ist da faul?
--
System: Debian Bookworm, 6.11.x.-x-amd64, ext4, AMD Ryzen 7 3700X, 8 x 3.8 Ghz., Radeon RX 5700 XT, 32 GB Ram, XFCE

geier369
Beiträge: 32
Registriert: 01.09.2004 07:49:17

Beitrag von geier369 » 14.01.2005 11:07:57

hmmm,

also die Rueckgabe von read ist die Anzahl der gelesenen Bytes,
da is 11 wohl erstmal korrekt.

Mit OFFSET ist allerdings echt bloed:
Hab gerade gelesen dass sich OFFSET auf die Position inder
generierten Variablen und nicht Auf den Dateihandle beziehet.

Vielleicht sollte man die ganze Datei in eine Variable mit OFFSET 0
und LENGTH=Dateigroesse schreiben,

die Variable sollte dann die gesamte Datei in einem String enthalten,
der dann mit substr geparst werden kann.

Naja, nich ganz so schick wie ich dachte

Benutzeravatar
mistersixt
Beiträge: 6601
Registriert: 24.09.2003 14:33:25
Lizenz eigener Beiträge: GNU Free Documentation License

Beitrag von mistersixt » 14.01.2005 11:18:21

Ok, ich mache mich mal an den Tip von perls255 und versuche mich mal an unpack ...

Gruss, mistersixt.
--
System: Debian Bookworm, 6.11.x.-x-amd64, ext4, AMD Ryzen 7 3700X, 8 x 3.8 Ghz., Radeon RX 5700 XT, 32 GB Ram, XFCE

geier369
Beiträge: 32
Registriert: 01.09.2004 07:49:17

Beitrag von geier369 » 14.01.2005 11:31:39

Hier vielleicht noch ein Versuch

$size = (stat($DATEI))[7];

read( BINDAT, $DATENSATZ, $size, 0 );
foreach (split(//, $DATENSATZ)) {
$readable .= printf("%02x ", ord($_));
}
$end = 3x67;
while( $end < (3x$size) ){ #hex und leerzeichen
$out = substr( $readable, $end, 3x11);
# $out von hex nach ASCII;
print $out . "\n";
$end += 3x67;
}

PS: poste mal die Loesung, ist ein interessantes Ding

Benutzeravatar
mistersixt
Beiträge: 6601
Registriert: 24.09.2003 14:33:25
Lizenz eigener Beiträge: GNU Free Documentation License

Beitrag von mistersixt » 14.01.2005 12:21:31

Code: Alles auswählen

#!/usr/bin/perl
#

$DATEI = "/tmp/message.dat";

open(BINDAT, "< $DATEI") or die "Datei oeffnen geht nicht"; 
binmode(BINDAT);
$size = (stat($DATEI))[7];

print $size,"\n";

read( BINDAT, $DATENSATZ, $size, 0 ); 
close(BINDAT);

$schleife = 0;  
while( $schleife < $size ){    
        print "\$schleife = $schleife\n";
        $trader  = substr( $DATENSATZ, $schleife + 75, 11);
        $is_call = substr( $DATENSATZ, $schleife + 74,  1);
        print $trader . "\n";
        printf ("%02x\n", ord($is_call));
        $schleife += 108;
}          
So gehts. Als Beispiel grabbel ich mir erstmal direkt einen ASCII-String raus ($trader) und anschliessend ein Flag ($is_call).

Danke für die Tips geier,

Gruss, mistersixt.
--
System: Debian Bookworm, 6.11.x.-x-amd64, ext4, AMD Ryzen 7 3700X, 8 x 3.8 Ghz., Radeon RX 5700 XT, 32 GB Ram, XFCE

Benutzeravatar
Joghurt
Beiträge: 5244
Registriert: 30.01.2003 15:27:31
Wohnort: Hamburg
Kontaktdaten:

Beitrag von Joghurt » 14.01.2005 14:15:11

Auch ich schließe mich an: nimm unpack! Genau dafür ist es da!

Benutzeravatar
godsmacker
Beiträge: 902
Registriert: 16.03.2003 21:50:26
Lizenz eigener Beiträge: Artistic Lizenz
Wohnort: Chemnitz
Kontaktdaten:

Beitrag von godsmacker » 14.01.2005 16:29:15

Etwas ab vom Thema, aber zum auslesen immer wieder gleich langer Daten kann man uebrigens sehr gut die Spezialvariable oder auch bzw.

Code: Alles auswählen

$INPUT_RECORD_SEPARATOR
, wenn man das English Modul verwendet, benutzen.

Code: Alles auswählen

{
    local $/ = \108;
    open my $fh, $myfile or die $!;
    while (my $msg = <$fh>) {
        ...;
    }
}
Dabei werden immer genau 108 bytes lange Strings in $msg gelesen. Damit spart man sich das haessliche herumspielen mit read.

-Florian

Benutzeravatar
mistersixt
Beiträge: 6601
Registriert: 24.09.2003 14:33:25
Lizenz eigener Beiträge: GNU Free Documentation License

Beitrag von mistersixt » 14.01.2005 17:34:07

Das hört sich auch interessant an, werde ich das nächste Mal probieren (oder halt unpack, das sah mir aber spontan nicht sehr trivial aus).

Gruss, mistersixt.
--
System: Debian Bookworm, 6.11.x.-x-amd64, ext4, AMD Ryzen 7 3700X, 8 x 3.8 Ghz., Radeon RX 5700 XT, 32 GB Ram, XFCE

Antworten