2017-07-28 116 views
2

我想解析一個PHP中的二進制文件,它是NoSQL DB中文檔的附件。但是,在我的測試中,如果文件大小爲1MB,則解包將持續大約12-15秒。該文件包含有關傳感器速度的信息。在PHP中解壓二進制文件

轉換成十六進制二進制文件的結構如下:

BB22 1100 0015 XXXX ... 
BB22 1300 0400 20FB 5900 25FB 5910 ... 20FB 5910 
BB22 1100 0015 ... 
BB22 1300 0400 20FB 5700 25FB 5810 ... 20FB 5912 
BB22 1300 0400 20FB 5700 25FB 5810 ... 20FB 5912 
... 

標記BB22 1100包含傳感器說明書中,而0015是指該信息的大小。

標記BB22 1300包含其他數據加上傳感器的實際速度。接下來的兩個字節0400表示該塊的長度,它是1024字節。

我只對這些值的速度感興趣,例如5900 5910 5910 5700 5810 ...

我的方法是如下:

$file = fopen($url, 'r', false, authenticationContext($url)); 
$result = stream_get_contents($file, -1); 
fclose($file); 

$hex_result = bin2hex($result); 

$markerData = 'bb2213'; 
$sensorDataUnpack= "sspeed"; // signed int16 

while(($pos = strpos($hex_result, $markerData, $pos)) !== FALSE){ 
    $pos=$pos+4; 
    for ($j=4; $j<1028; $j=$j+4) { 
     $d = unpack($sensorDataUnpack, substr($result, $pos/2+$j+2));  
     $sensorData[] = $d; 
    } 
} 

我轉換的結果從二元到十六進制的,因爲它不是爲我工作正常獲取的位置。無論如何,我相信這個代碼可以有很大的改進,有什麼想法?

+1

你應該真的考慮創建一個C或C++程序來完成這項工作並收集'sensorData'。然後,您只需在PHP腳本中使用適當的參數運行該程序並收集結果。它需要更多的工作,但是你的結果要快得多。當然,這需要PHP具有不被服務器提供者阻止的shell函數才能運行外部程序。您也可以嘗試切換到PHP 7.x.它比舊的PHP版本要快得多。 –

+0

@ChristosLytras,可能是一個很好的方法可能是你的建議。 @CrouchingKitten,對不起。我只是遵循函數包的文檔 - 有符號短(總是16位,機器字節順序)'。你用'p'表示什麼意思? –

+0

啊我現在看到了,對不起,我錯過了解壓縮格式代碼中的命名部分。一個問題:從數據中你總是希望每秒只有一個小的int值?爲什麼這些被跳過? 「20FB」「25FB」 –

回答

0

這應該很快,但沒有測試數據,我無法測試它。

的關鍵點是這些:

  • 打開網址爲二進制,並使用FREAD()的定位,並在數據切片多達部分,以幫助。
  • 使用unpack解析標題和條目的主體。
  • 使用星號*中繼器來快速解析簽名短褲的大型身體。
  • 使用array_values()將關聯數組轉換爲帶數字鍵的簡單數組(如:0,1,2,...)。

更新:我通過使用「H4」包格式以大端順序得到一個hexa字符串來解決標記比較周圍的字節順序和比特性問題。

$sensorData = array(); 
    $file = fopen($url, 'rb', false, authenticationContext($url)); 

    while (($header = fread($file, 6)) !== false) { 
     $fields = unpack("H4marker/ssize", $header); 

     $body = fread($file, $fields["size"] * 2); 

     if ($body === false) { 
      throw new Exception("import: data stream unexpectedly ended."); 
     } 

     if ($fields["marker"] == "BB221300") { 
      $data = array_values(unpack("s*", $body)); 

      // Store only every second value. 
      for ($i = 1; $i < count($data); $i+=2) { 
       $sensorData[] = $data[$i]; 
      } 
     } 
    } 

    fclose($file);