PHP: извлечение упакованных шестнадцатеричных чисел из строки

Я пытаюсь извлечь упакованные шестнадцатеричные числа из строки. Мое приложение связывается с сервером, который отправляет строку с заголовком, за которым следуют шестнадцатеричные числа, упакованные в 2 байта. В этой строке тысячи чисел.

Я хочу извлечь каждое сжатое 2-байтовое число и преобразовать его в число, которое можно использовать для выполнения вычислений.

Пример: string = "info:\x00\x00\x11\x11\x22\x22" выдаст три числа 0x0000 (десятичный 0), 0x1111 (десятичное число 4369), 0x2222 (десятичное 8738)

У меня есть работающее решение (см. Ниже), но оно работает слишком медленно, когда я пытаюсь обработать несколько тысяч номеров, которые отправляет сервер. Пожалуйста, предоставьте некоторые рекомендации, чтобы ускорить мой подход.

//Works but is too slow!
//$string has the data from the server
$arrayIndex = 0;
for($index = [start of data]; $index < strlen($string); $index+=2){
    $value = getNum($string, $index, $index+1);
    $array[$arrayIndex++] = $value;
}
function getNum($string, $start, $end){
    //get the substring we're interested in transforming
    $builder = substr($string, $start, $end-$start+1);  

    //convert into hex string
    $array = unpack("H*data", $builder);
    $answer = $array["data"];

    //return the value as a number
    return hexdec($answer);
}

Я также пытался извлечь числа в одной команде распаковки, но это не помогло (у меня возникли проблемы с пониманием используемой строки формата)

//Not working alternate method
//discard the header (in this case 18 bytes) and put the rest of the
//number values I'm interested in into an array
$unpacked = unpack("c18char/H2*data", $value);
for($i = 0; $i < $size; $i+=1){
    $data = $unpacked["data".$i];
    $array[$i] = $data;
}

3 ответа

Решение
$array = array();
$len = strlen($string);
for($index = [start of data];          $index < $len;               $index+=2){
    $d = unpack("H*data", substr($string, $index, 2));
    $array[] = hexdec($d["data"]);
}

Единственными существенными вещами, которые я сделал, было кэширование значения strlen и сокращение вызовов функций.

Вы также можете попробовать это

foreach (str_split(substr($string, [start of data]), 2) as $chunk) {
    $d = unpack("H*data", $chunk);
    $array[] = hexdec($d["data"]);
}

Одна вещь, которую я могу предложить, это передача строки, содержащей тысячи шестнадцатеричных чисел, по ссылке, а не по значению. Если есть, скажем, 3k числа, строка будет длинной 12k символов, с множеством вызовов функции 3k приводит к ~36M (если один байт используется на символ, ~72M, если utf8) ненужной выделенной памяти в стеке:

$arrayIndex = 0;
for($index = [start of data]; $index < strlen($string); $index+=2){
    $value = getNum($string, $index, $index+1);
    $array[$arrayIndex++] = $value;
}
 //pass by reference rather than value
function getNum(&$string, $start, $end){
    //get the substring we're interested in transforming
    //$builder = substr($string, $start, $end-$start+1);  
    //not sure if substr takes reference or value, so implementing this way, just in case it's by value
      $builder = $string[$start] . $string[$start + 1] ;
    //convert into hex string
    $array = unpack("H*data", $builder);
    $answer = $array["data"];

    //return the value as a number
    return hexdec($answer);
}

Не уверен, насколько это ускорится (выделение памяти точно), но определенно стоит попробовать.

Почему бы не попробовать что-то вроде:

$string = "info:\x00\x00\x11\x11\x22\x22";

$ret = array();
preg_match_all('#\\x(\d{2})#', $string, $items);
if(isset($items[1]) && count($items[1])>0)
{
     for($i=0;$i<count($items[1]);$i+=2)
     {
            if(isset($items[1][$i]) && isset($items[1][$i+1]))
            {
                    $ret[] = '0x' . $items[1][$i] . $items[1][$i+1];
                    unset($items[1][$i]);
                    unset($items[1][$i+1]);
            }
     }
}
Другие вопросы по тегам