web-dev-qa-db-fra.com

Comment puis-je obtenir un vidage hexadécimal d'une chaîne en PHP?

J'étudie les encodages en PHP5. Existe-t-il un moyen d'obtenir un vidage hexadécimal brut d'une chaîne? c'est-à-dire une représentation hexadécimale de chacun des octets (pas des caractères) dans une chaîne?

63
Rory
echo bin2hex($string);

ou:

for ($i = 0; $i < strlen($string); $i++) {
    echo str_pad(dechex(ord($string[$i])), 2, '0', STR_PAD_LEFT);
}

$string est la variable qui contient l'entrée.

92
Stefan Gehrig

Pour déboguer le travail avec des protocoles binaires, j'avais besoin d'un vidage HEX plus traditionnel, j'ai donc écrit cette fonction:

function hex_dump($data, $newline="\n")
{
  static $from = '';
  static $to = '';

  static $width = 16; # number of bytes per line

  static $pad = '.'; # padding for non-visible characters

  if ($from==='')
  {
    for ($i=0; $i<=0xFF; $i++)
    {
      $from .= chr($i);
      $to .= ($i >= 0x20 && $i <= 0x7E) ? chr($i) : $pad;
    }
  }

  $hex = str_split(bin2hex($data), $width*2);
  $chars = str_split(strtr($data, $from, $to), $width);

  $offset = 0;
  foreach ($hex as $i => $line)
  {
    echo sprintf('%6X',$offset).' : '.implode(' ', str_split($line,2)) . ' [' . $chars[$i] . ']' . $newline;
    $offset += $width;
  }
}

Cela produit un vidage HEX plus traditionnel, comme ceci:

hex_dump($data);

=>

 0 : 05 07 00 00 00 64 65 66 61 75 6c 74 40 00 00 00 [.....default@...]
10 : 31 42 38 43 39 44 30 34 46 34 33 36 31 33 38 33 [1B8C9D04F4361383]
20 : 46 34 36 32 32 46 33 39 32 46 44 38 43 33 42 30 [F4622F392FD8C3B0]
30 : 45 34 34 43 36 34 30 33 36 33 35 37 45 35 33 39 [E44C64036357E539]
40 : 43 43 38 44 35 31 34 42 44 36 39 39 46 30 31 34 [CC8D514BD699F014]

Notez que les caractères non visibles sont remplacés par un point - vous pouvez modifier le nombre d'octets par ligne ($ width) et le caractère de remplissage ($ pad) selon vos besoins. J'ai inclus un argument $ newline, vous pouvez donc passer "<br/>" si vous devez afficher la sortie dans un navigateur.

J'espère que cela vous sera utile :-)

80
mindplay.dk

C'est des années plus tard, mais au cas où d'autres le chercheraient aussi, j'ai pris la liberté de modifier le code de mindplay.dk pour lui faire accepter diverses options et simuler la sortie du fichier hexdump -C de la commande BSD:

/**
* Dumps a string into a traditional hex dump for programmers,
* in a format similar to the output of the BSD command hexdump -C file.
* The default result is a string.
* Supported options:
* <pre>
*   line_sep        - line seperator char, default = "\n"
*   bytes_per_line  - default = 16
*   pad_char        - character to replace non-readble characters with, default = '.'
* </pre>
*
* @param string $string
* @param array $options
* @param string|array
*/
function hex_dump($string, array $options = null) {
    if (!is_scalar($string)) {
        throw new InvalidArgumentException('$string argument must be a string');
    }
    if (!is_array($options)) {
        $options = array();
    }
    $line_sep       = isset($options['line_sep'])   ? $options['line_sep']          : "\n";
    $bytes_per_line = @$options['bytes_per_line']   ? $options['bytes_per_line']    : 16;
    $pad_char       = isset($options['pad_char'])   ? $options['pad_char']          : '.'; # padding for non-readable characters

    $text_lines = str_split($string, $bytes_per_line);
    $hex_lines  = str_split(bin2hex($string), $bytes_per_line * 2);

    $offset = 0;
    $output = array();
    $bytes_per_line_div_2 = (int)($bytes_per_line / 2);
    foreach ($hex_lines as $i => $hex_line) {
        $text_line = $text_lines[$i];
        $output []=
            sprintf('%08X',$offset) . '  ' .
            str_pad(
                strlen($text_line) > $bytes_per_line_div_2
                ?
                    implode(' ', str_split(substr($hex_line,0,$bytes_per_line),2)) . '  ' .
                    implode(' ', str_split(substr($hex_line,$bytes_per_line),2))
                :
                implode(' ', str_split($hex_line,2))
            , $bytes_per_line * 3) .
            '  |' . preg_replace('/[^\x20-\x7E]/', $pad_char, $text_line) . '|';
        $offset += $bytes_per_line;
    }
    $output []= sprintf('%08X', strlen($string));
    return @$options['want_array'] ? $output : join($line_sep, $output) . $line_sep;
}

et ceci est un vidage hexadécimal d'un petit fichier:

00000000  89 50 4e 47 0d 0a 1a 0a  00 00 00 0d 49 48 44 52  |.PNG........IHDR|
00000010  00 00 00 10 00 00 00 10  02 03 00 00 00 62 9d 17  |.............b..|
00000020  f2 00 00 00 09 50 4c 54  45 04 04 04 99 99 cc d7  |.....PLTE.......|
00000030  d7 d7 2a 66 f6 6b 00 00  00 38 49 44 41 54 78 9c  |..*f.k...8IDATx.|
00000040  63 08 05 02 06 24 22 0b  44 24 01 89 ac a4 69 4b  |c....$".D$....iK|
00000050  19 1a 16 68 70 31 74 29  75 2c 42 22 1a 16 75 00  |...hp1t)u,B"..u.|
00000060  c5 22 33 96 32 74 86 46  4c 65 58 19 1a 35 15 61  |."3.2t.FLeX..5.a|
00000070  00 00 df be 19 a6 2e 62  80 87 00 00 00 00 49 45  |.......b......IE|
00000080  4e 44 ae 42 60 82                                 |ND.B`.|
00000086

et voici le test phpunit:

<?php
if (isset($argv)) {
    print "Running outside of phpunit. Consider using phpunit.\n";
    class PHPUnit_Framework_TestCase {}
}


class Test extends PHPUnit_Framework_TestCase
{
    const FUNCTION_NAME = 'hex_dump';
    const DATA_BASE64 = '
        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAACVBMVEUEBASZmczX19cqZvZrAAAA
        OElEQVR4nGMIBQIGJCILRCQBiaykaUsZGhZocDF0KXUsQiIaFnUAxSIzljJ0hkZMZVgZGjUVYQAA
        374Zpi5igIcAAAAASUVORK5CYII=';
    private $expect = array(
        '00000000  89 50 4e 47 0d 0a 1a 0a  00 00 00 0d 49 48 44 52  |.PNG........IHDR|',
        '00000010  00 00 00 10 00 00 00 10  02 03 00 00 00 62 9d 17  |.............b..|',
        '00000020  f2 00 00 00 09 50 4c 54  45 04 04 04 99 99 cc d7  |.....PLTE.......|',
        '00000030  d7 d7 2a 66 f6 6b 00 00  00 38 49 44 41 54 78 9c  |..*f.k...8IDATx.|',
        '00000040  63 08 05 02 06 24 22 0b  44 24 01 89 ac a4 69 4b  |c....$".D$....iK|',
        '00000050  19 1a 16 68 70 31 74 29  75 2c 42 22 1a 16 75 00  |...hp1t)u,B"..u.|',
        '00000060  c5 22 33 96 32 74 86 46  4c 65 58 19 1a 35 15 61  |."3.2t.FLeX..5.a|',
        '00000070  00 00 df be 19 a6 2e 62  80 87 00 00 00 00 49 45  |.......b......IE|',
        '00000080  4e 44 ae 42 60 82                                 |ND.B`.|',
        '00000086',
    );

    public function testRequire() {
        $file = __DIR__ . '/' . static::FUNCTION_NAME . '.php';
        $this->assertFileExists($file);
        include($file);
        $this->assertTrue(function_exists(static::FUNCTION_NAME));
    }

    public function testString() {
        $func = static::FUNCTION_NAME;
        $data = base64_decode(static::DATA_BASE64);
        if (!is_string($data)) {
            throw new Exception('Unable to decode base64 encoded test data');
        }
        $dump = $func($data);
        //var_export($dump);
        $this->assertTrue(is_string($dump));
        $this->assertEquals($dump, join("\n", $this->expect) . "\n");
    }

}


if (isset($argv)) {
    $func = Test::FUNCTION_NAME;
    require_once($func . '.php');
    if (count($argv) < 2) {
        print "Pass arguments file, from, length.\n";
    }
    else {
        $file = $argv[1];
        if (!file_exists($file)) {
            die("File not found: $file\n");
        }
        $from   = isset($argv[2]) && preg_match('/^\d{1,9}$/', $argv[2]) ? intval($argv[2]) : null;
        $len    = isset($argv[3]) && preg_match('/^\d{1,9}$/', $argv[3]) ? intval($argv[3]) : filesize($file);
        $h = fopen($file, 'r');
        if ($from) {
            fseek($h, $from);
        }
        $data = fread($h, $len);
        fclose($h);
        $dump = hex_dump($data);
        print $dump;
        //$dump = hex_dump($data, array('want_array' => true));
        //print_r($dump);
    }
}
5

Lors du débogage d'un protocole binaire, j'avais également besoin d'un hexdump (). J'ai décidé de publier ma solution en tant que package PEAR car elle est certainement utile. Vous pouvez également parcourir le code sur github.

POIRE: http://www.metashock.de/pear

GitHub: http://www.github.com/metashock/Hexdump

En plus de la solution mindplays, il prend en charge le rendu propper de la dernière ligne et des paramètres supplémentaires. Le paquet contient également un exécutable php nommé phphd pour hexdumps sur cmdline. Cela pourrait être utile sur les systèmes Windows :)

@ mindplay.dk: Merci pour l'idée strtr (). Il est apparu légèrement plus vite que ma précédente tentative. Intégré cela dans ma version. (Utilisation d'un tampon de traduction diminué) ..

S'amuser!

3
hek2mgl

Version "fonctionnelle":

$s = "\x04\x00\xa0\x00";
echo implode(' ', array_map(function($char) {
    # return sprintf('%02s', $char);
    return str_pad($char, 2, '0', STR_PAD_LEFT);
}, array_map('dechex', unpack('C*', $s))));

Empruntant à Ionuț G. Stan commentaire , la dernière ligne pourrait être la suivante:

}, array_map('dechex', array_map('ord', str_split($s)))));
1
x-yuri