PHP 處理 JSON 大檔案,如果直接將整個檔案讀入記憶體,很容易遇到 Allowed memory exhausted 問題。除了解決網站伺服器遇到 Allowed memory exhausted 問題提到使用 jq 將大檔案切割成小檔案的方法、或依照檔案內容結構,逐行使用 PHP Generator (產生器) 處理。也可以使用 JSON Streaming Parser 直接處理。
Purple PHP Women elephpant by Philip Sharp 使用創用 CC by-sa 授權 |
問題狀況
JSON 檔案約 250 MB,使用 json_decode 函數處理,遭遇記憶體耗盡的錯誤訊息
PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 20480 bytes)
問題解決
方法 1: 使用 jq - 處理 JSON 檔案的命令列工具
步驟 1: 安裝 jq
前往 jq 網站,根據作業系統版本,安裝對應的 jq。支援的作業系統有 Windows, Linux 與 OSX。根據個人經驗,Windows 版本 jq 建議透過 Cygwin 安裝 jq package。
步驟 2: 瞭解 JSON 內容結構
檔案太大時無法用一般文字編輯器打開。可以透過指令看部分檔案內容,例如逐頁看檔案內容
cat 檔名 | more
cat example.json | more
按 space 按鍵換頁,再按 q 就可以離開逐頁觀看模式
步驟 3: 依照 JSON 內容結構,決定處理方式
格式 I 完整的 JSON 格式
例如:projects 下有上千多筆的專案資料,example.json 檔案內容像:
{
"projects": [
{
"id": 1,
"name": "編號1專案"
},
{
"id": 2,
"name": "編號2專案"
}
]
}
請前往 解決網站伺服器遇到 Allowed memory exhausted 問題,了解將大檔案切割成小檔案的方法
步驟 3-1: 計算記錄筆數
jq '結構樹 | length' 檔名
jq '.projects | length' example.json
步驟 3-2: 預覽第一筆記錄
第一筆從 0 開始,結構樹語法需要標記 [0]
jq '結構樹' 檔名
jq '.projects[0]' example.json
範例檔案的結果是
{
"id": 1,
"name": "編號1產品"
}
步驟 3-3: 逐筆拆解紀錄,另存檔案
jq '結構樹' 檔名 > 輸出檔名
jq '.projects[0]' example.json > 0.json
jq '. projects[1]' example.json > 1.json
jq '. projects[2]' example.json > 2.json
因為檔案變小,後續就很容易解析小檔案內容。
格式 II 每行一筆 JSON 格式
例如:每行以 { 符號開始、} 符號結束。檔案內容看起來像:
{"id":1, "name":"編號1專案"}
{"id":2, "name":"編號2專案"}
步驟 3-1: 計算記錄筆數
wc -l 檔名
wc -l example.json
步驟 3-2: 預覽第一筆記錄
head -n 1 檔名 | jq .
head -n 1 example.json | jq .
上方指令先使用 head 指令,輸出第一行的檔案內容。再使用 | 符號 (pipe),當作 jq 的輸入資料,顯示比較好閱讀的 JSON 結構
使用 PHP Generator (產生器) 逐行讀取檔案內容即可。
方法 2: 使用 JSON Streaming Parser
步驟 1: 安裝 sergiorodenas/stream-parser 套件
composer require rodenastyle/stream-parser
步驟 2: 解析 JSON 內容
require_once __DIR__ . '/vendor/autoload.php';
use Rodenastyle\StreamParser\StreamParser;
use Tightenco\Collect\Support\Collection;
StreamParser::json($file_path)->each(function(Collection $row){
var_dump($row, true);
});
參考資料
- jq Manual (development version)
- StarTutorial: Php Generator Reading File Content
- php - 'safe' json_decode( ,,, ) to prevent exhausting memory - Stack Overflow 提到將檔案變小以及如何觀察記憶體變化
- Very easy to use and memory efficient drop-in replacement for inefficient iteration of big JSON...
- JSON Streaming Parser for PHP
- php - How to properly iterate through a big json file - Stack Overflow
- [扩展推荐] PHP 7 stream-parser 支持多格式的文件流解析器(超大文件 xml/JSON/CSV 读取解析的方案) | Laravel优质外文翻译 | Laravel China 社区
- JSON streaming - Wikipedia
- Pipe, Grep and Sort Command in Linux/Unix with Examples
JSON Streaming Parser
- sergiorodenas/stream-parser: ⚡ PHP7 / Laravel Multi-format Streaming Parser
- pcrov/JsonReader: A JSON pull parser for PHP
- salsify/jsonstreamingparser: A JSON streaming parser implementation in PHP
無效的嘗試
% jq --compact-output . test.json
留言
張貼留言