[php] data lezen uit binaire file (big/little endian gedoe)

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Explore
  • Registratie: Maart 2001
  • Laatst online: 08-04-2011

Explore

Op zoek naar werk

Topicstarter
Ik heb een file met gegevens in binair formaat waar little endian en big endian door elkaar worden gebruikt.

Er worden integers en doubles gebruikt, die als volgt zijn gedefinieerd:

Integer: Signed 32-bit integer (4 bytes)
Double: Signed 64-bit IEEE double precision integer (8 bytes)

Nou wil ik die gegevens in kunnen lezen in normale menselijk leesbare getallen en heb ik tot nu toe het volgende stukje code in elkaar geflanst:

PHP:
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
// define types
define('_INT', 4);
define('_DOUBLE', 8);

// define byte orders (litte/big endian)
define('_BIG', 1);
define('_LITTLE', 2);

// reads a piece of binary data from a file and returns a value
function read_data($fh, $type, $order) {
    $c = fread($fh, $type);
    $a = unpack('C*', $c);

    $n = 0;
    if ($order == _BIG) {
        // MSB left, LSB right
        $p = $type-1;
        foreach ($a as $v) {
            $n += $v*pow(2, 8*$p--);
        }
    } else if ($order == _LITTLE) {
        // MSB right, LSB left
        $p = 0;
        foreach ($a as $v) {
            $n += $v*pow(2, 8*$p++);
        }
    }

    // DEBUG!!!
    echo "<p>".bin2hex($c)." ($type/$order)<br>";
    var_dump($a); echo "<br>";
    echo "result: $n<br>";

    return $n;
}


De output van dit progje, bij de file die ik aan het bewerken ben, is nu:

code:
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
0000270a (4/1)
array(4) { [1]=> int(0) [2]=> int(0) [3]=> int(39) [4]=> int(10) }
result: 9994

002fa4ee (4/1)
array(4) { [1]=> int(0) [2]=> int(47) [3]=> int(164) [4]=> int(238) }
result: 3122414

03000000 (4/2)
array(4) { [1]=> int(3) [2]=> int(0) [3]=> int(0) [4]=> int(0) }
result: 3

00000000005efa40 (8/2)
array(8) { [1]=> int(0) [2]=> int(0) [3]=> int(0) [4]=> int(0) [5]=> int(0) [6]=> int(94) [7]=> int(250) [8]=> int(64) }
result: 4.6821581166981E+18

0000000000cf1c41 (8/2)
array(8) { [1]=> int(0) [2]=> int(0) [3]=> int(0) [4]=> int(0) [5]=> int(0) [6]=> int(207) [7]=> int(28) [8]=> int(65) }
result: 4.6918525107202E+18

0000000000940141 (8/2)
array(8) { [1]=> int(0) [2]=> int(0) [3]=> int(0) [4]=> int(0) [5]=> int(0) [6]=> int(148) [7]=> int(1) [8]=> int(65) }
result: 4.6841878151629E+18

0000000000461e41 (8/2)
array(8) { [1]=> int(0) [2]=> int(0) [3]=> int(0) [4]=> int(0) [5]=> int(0) [6]=> int(70) [7]=> int(30) [8]=> int(65) }
result: 4.6922648275806E+18


De eerste 3 results kloppen, maar over de volgende 4 heb ik zo m'n twijfels. De waardes zijn veel te groot. PHP heeft, zover ik kan nagaan, niet echt support voor dit soort dingen dus ik kan me voorstellen dat de Signed 64-bit IEEE double precision integer compleet verkeerd wordt behandeld.

Kan iemand me hier mee helpen?

[ specs ] [ Tweaker gallery ]


Acties:
  • 0 Henk 'm!

  • Explore
  • Registratie: Maart 2001
  • Laatst online: 08-04-2011

Explore

Op zoek naar werk

Topicstarter
Aangezien ik zo veel reacties kreeg.... :Y) ben ik zelf natuurlijk verder gegaan. hier het ietwat omslachtige, maar werkende resultaat (voor eenieder die hierop vast komt te zitten):

PHP:
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
// convert binary data (byte values in range 0-255) to decimal number
// understands different data types and little/big endian order
function bin2dec($val, $type = _INT, $order = _BIG) {

    // convert to hexadecimal
    $val = bin2hex($val);

    // reverse byte order, if needed
    if ($order == _LITTLE) {
        $tmp = '';
        for ($i = $type-1; $i >= 0; $i--)
            $tmp .= substr($val, $i*2, 2);
        $val = $tmp;
    }

    // done, when a double is not requested
    if ($type != _DOUBLE) return hexdec($val);

    // double precesion integer coding - go get the bits!
    $bits = '';
    for ($i = 0; $i < $type; $i++)
        $bits .= str_pad(base_convert(substr($val, 2*$i, 2),
            16, 2), 8, '0', STR_PAD_LEFT);

    // get sign
    $sign = (int) substr($bits, 0, 1);

    // get (un)biased exponent
    $exp = bindec(substr($bits, 1, 11))-1023;

    // get fraction
    $frac = 1;
    $tmp = substr($bits, 12, 52);
    for ($i = 0; $i < 52; $i++)
        $frac += ((int) substr($tmp, $i, 1)) * pow(2, -1-$i);

    // calculate final value
    $val = pow(-1, $sign) * pow(2, $exp) * $frac;

    // return as result
    return $val;

}


have fun! :7

Edit: revisie, code is stuk netter en tikkie sneller. nu hoor ik graag van iemand: wat doe je moeilijk, gebruik gewoon standaard ingebouwde functie X die 't zelfde doet...

[ Voor 47% gewijzigd door Explore op 08-10-2004 12:49 ]

[ specs ] [ Tweaker gallery ]


Acties:
  • 0 Henk 'm!

  • Explore
  • Registratie: Maart 2001
  • Laatst online: 08-04-2011

Explore

Op zoek naar werk

Topicstarter
hm... unpack werkt ook op doubles zie ik nu. 8)7 wellicht wat sneller.... :P

[ specs ] [ Tweaker gallery ]


Acties:
  • 0 Henk 'm!

  • Postman
  • Registratie: Februari 2000
  • Laatst online: 18-09 19:05
Is dit niet exact hetzelfde (vanaf hex waarde):
PHP:
1
2
3
4
5
6
7
<?
$input = '0000000000940141'; //deze krijg je dus door bin2hex($waarde) te gebruiken

$output = hexdec($input);

echo $output; //geeft 9699649 weer
?>

[ Voor 11% gewijzigd door Postman op 14-10-2004 01:27 ]


Acties:
  • 0 Henk 'm!

  • Explore
  • Registratie: Maart 2001
  • Laatst online: 08-04-2011

Explore

Op zoek naar werk

Topicstarter
Ehm, ja, maar het probleem was dat er doubles zijn opgeslagen in de binare file. Ik zocht naar iets wat die doubles 'decodeert'. De manual schiet een beetje te kort bij de uitleg van 'unpack' - althans, ik begreep 'm niet en las ook over 't feit heen dat unpack ook doubles kan uitpakken. Je moet dan alleen nog even rekening houden met het little/big endian gebeuren, maar dat is niet zo'n probleem.

Maargoed, leuk om een keer gemaakt te hebben, dat double decoderen, maar unpack is natuurlijk wat makkelijker (als je eenmaal doorhebt hoe 't werkt).

[ specs ] [ Tweaker gallery ]