Zeichenketten binär behandeln

Servus.

Ich lese aus einer Datei drei Byte und möchte diese als ein Wort binär maskieren. Im konkreten Fall

 read IN, $temp, 3;
 if ((hex(unpack(H6, $temp)) & 1)) {
 print OUT "aaa";
 } else {
 print OUT "xxx";
 }

mache ich es mir besonders beim Fortschreiben recht bequem - da das Ergebnis nur 0 oder 1 lauten kann schreibe ich einfach zwei versch. beliebige Zeichenketten fort - für meine Aufgabenstellung hier hinreichend. Wenn ich aber mehrere Bits maskieren möchte, muss ich das Ergebnis der Operation - wieder als ein drei-Byte-Wort - fortschreiben. Irgendwie kann ich mich aber mit der pack- und unpack-Funktion nicht so recht anfreunden, es kommt immer nur Datenmüll bei raus.

Wie muss ich hier formulieren, um das maskierte Ergebnis sauber fortzuschreiben?

Danke für Tipps (besser noch: Codeschnipsel)
Schorsch

Hallo,

Ich lese aus einer Datei drei Byte und möchte diese als ein
Wort binär maskieren. Im konkreten Fall

mache ich es mir besonders beim Fortschreiben recht bequem -
da das Ergebnis nur 0 oder 1 lauten kann schreibe ich einfach
zwei versch. beliebige Zeichenketten fort - für meine
Aufgabenstellung hier hinreichend. Wenn ich aber mehrere Bits
maskieren möchte, muss ich das Ergebnis der Operation - wieder
als ein drei-Byte-Wort - fortschreiben. Irgendwie kann ich
mich aber mit der pack- und unpack-Funktion nicht so recht
anfreunden, es kommt immer nur Datenmüll bei raus.

Wenn ich Dich richtig verstanden haben sollte,
möchtest Du sowas machen:

 ...
 sysopen (IN, $fname, O\_RDONLY) or die "open $!";
 binmode IN; # beware of DOS
 sysread IN, $temp, 3;

 my @bytes = unpack 'C' x 3, $temp;
 
 close IN;
 ...
 my @boolean = qw(aaa xxx);
 print "[@bytes]", "\n";
 print "$\_ is $boolean[$\_ & 1]", "\n" for@ bytes;
 ...

Aber vielleicht habe ich etwas übersehen.

Grüße

CMБ

Wenn ich Dich richtig verstanden haben sollte,
möchtest Du sowas machen:

Jetzt ist mir vielleicht eingefallen, was Du
möchtest. Du willst ja „eigentlich“ (Dein Code
war ja nur eine Notlösung) alle 3 Bytes u. U.
„maskiert“ wieder ausgeben - dann musst Du
einmal ‚unpack‘-en und einmal ‚pack‘-en,
also sowas (alle Bytes maskiert mit 0xFC):

 ...
 use Fcntl qw( :smiley:EFAULT);

 sysopen (IN, $fname, O\_RDONLY) or die "$fname $!";
 binmode IN; # beware of DOS
 sysread IN, $tmpst, 3;
 close IN;

 @bytes = unpack 'C' x 3, $tmpst;
 @bmask = map $\_ & 0xFC, @bytes;
 $tmpst = pack 'C' x 3, @bmask;
 
 sysopen (OUT, $oname, O\_WRONLY|O\_CREAT|O\_TRUNC) or die "$oname $!";
 binmode OUT;
 syswrite OUT, $tmpst, 3;
 close OUT;
 ...

Oder doch nicht? Hmmm …

Grüße

CMБ

1 Like

Wenn ich Dich richtig verstanden haben sollte,
möchtest Du sowas machen:


sysopen (IN, $fname, O_RDONLY) or die „open $!“;
binmode IN; # beware of DOS
sysread IN, $temp, 3;

my @bytes = unpack ‚C‘ x 3, $temp;

close IN;

my @boolean = qw(aaa xxx);
print „[@bytes]“, „\n“;
print „$_ is $boolean[$_ & 1]“, „\n“ for@ bytes;

Aber vielleicht habe ich etwas übersehen.

Nein, ist nicht das, was ich meinte. Ich habe beispielsweise die Zeichenkette „ABC“ (0x414243) eingelesen und möchte je Byte das niederstwertige Bit ausblenden indem ich mit 0xFEFEFE maskiere (könnte aber auch beliebige andere Bits ausblenden wollen). Das Ergebnis wäre hier 0x404242; fortschreiben müsste ich also die Zeichenkette „@BB

Als Ergebnis von

 print hex(414243) & hex(fefefe);

bekomme ich aber die Ausgabe 4211266 - mathematisch durchaus korrekt - muss diese Ausgabe aber zum fortschreiben wieder in das drei-Byte Wort „@BB“ wandeln. Und weiss halt nicht, wie ich das mit pack mache bzw. ob diese Funktion hier überhaupt zu verwenden ist.

Ich kann’s ganz klassisch funktionell machen

my $zw1, $zw2;
my $temp = hex(414243) & hex(fefefe);
print chr($zw1 = int($temp/256/256)) . 
 chr($zw2 = int(($temp-$zw1\*256\*256)/256)) .
 chr(int($temp-$zw1\*256\*256-$zw2\*256));
  • aber dieser Ausdruck sollte sich doch deutlich eleganter darstellen lassen.

Ich hoffe, meinen Wunsch jetzt verständlicher ausgedrückt zu haben
Schorsch

Jetzt ist mir vielleicht eingefallen, was Du
möchtest. Du willst ja „eigentlich“ (Dein Code
war ja nur eine Notlösung) alle 3 Bytes u. U.
„maskiert“ wieder ausgeben - dann musst Du
einmal ‚unpack‘-en und einmal ‚pack‘-en,

Ja, genau.

@bytes = unpack ‚C‘ x 3, $tmpst;
@bmask = map $_ & 0xFC, @bytes;
$tmpst = pack ‚C‘ x 3, @bmask;

So passt’s. Und ist vor allem wesentlich eleganter als mein obiger Ausdruck.

Vielen Dank
Schorsch

Hallo

@bytes = unpack ‚C‘ x 3, $tmpst;
@bmask = map $_ & 0xFC, @bytes;
$tmpst = pack ‚C‘ x 3, @bmask;

So passt’s. Und ist vor allem wesentlich eleganter als mein
obiger Ausdruck.

Hmmmm, warte mal. Elegant wäre

  • generell indirekte Filehandles ($fd),
  • benannte Konstanten,
  • angemessene Operatorverkettung

vielleicht z.B.:

 ...
 use constant BYTECNT =\> 3;
 use constant BYTEFMT =\> 'C' x BYTECNT;
 use constant MASK =\> 0xFE;

 open( my $infd, '', $outname ) or die "$outname $!";
 binmode $\_ for $infd, $outfd;

 read $infd, $data, BYTECNT;
 print $outfd **pack BYTEFMT, (map $\_ & MASK, unpack BYTEFMT, $data)**;

 close $\_ for $infd, $outfd;
 ...

Aber letztlich ist das auch Geschmackssache :wink:

Grüße

CMБ

Passt nicht
Hier hängt das Schwert:

@bmask = map $_ & 0xFC, @bytes;

Das hatte ich vorhin übersehen: Meine Beispiele werden dadurch abgedeckt, nicht aber die Forderung (könnte aber auch beliebige andere Bits ausblenden wollen).

Die Maske ist also nicht auf drei mal ein Byte, sondern auf ein Drei-Byte-Wort zu beziehen.

Meine Grundidee war, das eingelesene drei-Byte-Wort in ein vier-Byte-Integer zu wandeln, dieses beliebigen Operationen zu unterziehen und die Integer-Variable dann wieder als 3-Byte-Wort abzuspeichern.

Du hast mir aber jetzt schon einige Möglichkeiten gezeigt, die mir bislang unbekannt waren, und dafür habe ich zu danken
Schorsch

Hallo

Die Maske ist also nicht auf drei mal ein Byte, sondern auf
ein Drei-Byte-Wort zu beziehen.

Meine Grundidee war, das eingelesene drei-Byte-Wort in ein
vier-Byte-Integer zu wandeln, dieses beliebigen Operationen zu
unterziehen und die Integer-Variable dann wieder als
3-Byte-Wort abzuspeichern.

Ach so, dann gehts sogar ein wenig einfacher:

 ...
 use constant MASK =\> 0xfefefe;

 open( my $infd, '', $outname ) or die "$outname $!";
 binmode $\_ for $infd, $outfd;

 read $infd, $data, 3;
 
**$val |= $\_ ;  
  
 print $outfd pack('LX', $val & MASK);  
   
 close $\_ for $infd, $outfd;  
 ...**  

Ich Shift-e/Or-e praktisch die gelesenen Bytes
3 mal auf einen leeren Wert ‚$val‘ und nehme
bei der Ausgabe ein Byte wieder raus
(‚X‘-> backup one byte). $shft muss vorher Null sein.

Vielleicht hilfts ja :wink:

Grüße

CMБ

Hallo nochmal,

ich hab schon Schlafsachen an, da
fahre ich den Rechner nochmal hoch -
das hat mir keine Ruhe gelassen :wink:

Wenn man direkt

print $outfd pack(‚LX‘, $val & MASK);

packen kann, müsste man ja auch direkt entpacken
können - und ich habe auch herausbekommen, warum
das bei mir nicht ging, also sowas wie:

$value = unpack(‚L‘, $temp); # $temp = 3 bytes

es geht, wenn man dem ‚L‘-Format einfach
die benötigten 4 Bytes anbietet. Damit reduziert
sich Dein ganzes Problem auf folgende Zeilen:

 ...
 use constant MASK =\> 0xfefefe;
 ...
 ... 
 read $infd, $data, 3; # 3 Bytes lesen
 $val = unpack('L', $data."\0"); # 4 Bytes daraus machen

 $val &= MASK; # Irgendwas anstellen
 
 print $outfd pack('LX', $val); # ein Byte mit 'X' weglassen
 ...

(kein Loop mehr.)

Jetzt fahre ich die Mühle wieder runter :wink:

Grüße

CMБ

PS.: hierdurch habe ich einiges dazugelernt …

Passt exakt!

$val = unpack(‚L‘, $data."\0"); # 4 Bytes daraus machen

Da hätt ich erstmal drauf kommen müssen -

print $outfd pack(‚LX‘, $val); # ein Byte mit ‚X‘ weglassen

und darauf auch. Mein ersten Gehversuche mit (un)pack L sind aber wohl schon daran gescheitert, dass ich wieder zuviele Operationen in eine Zeile gepackt und dabei die operator precedence nicht beachtet hatte.

Vielen Dank
Schorsch