json lines文件不能用json_decode(file_get_contents($file)),因为其每行是独立json值而非单个json对象,整文件解析会语法错误;须逐行读取并解码。

JSON Lines文件读取时为什么不能用 json_decode(file_get_contents($file))
因为 JSON Lines(.jsonl)不是单个 JSON 对象,而是每行一个独立的 JSON 值(对象、数组、字符串等),整文件直接 json_decode() 会失败,报错类似 JSON_ERROR_SYNTAX 或只解析第一行就终止。
正确做法是逐行读取 + 逐行解码:
- 用
file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES)一次性读入所有非空行(适合小到中等文件) - 或用
fopen()+fgets()流式读取(推荐,内存友好,适合大文件) - 对每一行调用
json_decode($line, true);注意检查返回值是否为null,并用json_last_error()判断是否解析失败
示例片段:
if ($fp = fopen('data.jsonl', 'r')) {
while (($line = fgets($fp)) !== false) {
$line = trim($line);
if ($line === '') continue;
$data = json_decode($line, true);
if ($data === null && json_last_error() !== JSON_ERROR_NONE) {
error_log("JSON parse error on line: " . $line);
continue;
}
// 处理 $data...
}
fclose($fp);
}
写入 JSON Lines 文件时 json_encode() 的关键参数
每行必须是严格合法的 JSON 文本,且行尾不能有多余空格或换行符。常见错误是忘记 PHP_EOL 或误加逗号、括号。
要点:
- 每次写入前确保数据可被
json_encode()序列化(无资源、无循环引用) - 推荐加上
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES避免中文转义、URL 中斜杠被转义 - 必须手动追加
PHP_EOL(Windows 下是"\r\n",但用PHP_EOL更安全) - 不要用
file_put_contents($file, $json . PHP_EOL, FILE_APPEND)循环写——频繁磁盘 I/O 慢;改用fopen(..., 'a')+fwrite()
示例:
$fp = fopen('output.jsonl', 'a');
foreach ($rows as $row) {
$json = json_encode($row, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
if ($json === false) {
error_log("JSON encode failed: " . json_last_error_msg());
continue;
}
fwrite($fp, $json . PHP_EOL);
}
fclose($fp);
遇到中文乱码或特殊字符解析失败怎么办
JSON Lines 规范要求 UTF-8 编码,但 PHP 默认不校验输入编码。如果文件含 BOM 或混入 GBK 字节,json_decode() 会静默失败(返回 null)。
排查和修复步骤:
- 用
mb_detect_encoding($line, ['UTF-8', 'GBK'], true)检查单行编码;若非 UTF-8,先mb_convert_encoding($line, 'UTF-8', 'GBK') - 用
hexdump -C file.jsonl | head查看前几字节,确认无 EF BB BF(UTF-8 BOM);如有,需ltrim($line, "\xEF\xBB\xBF") -
json_last_error_msg()返回"Malformed UTF-8 characters"就基本锁定编码问题
大文件处理时性能瓶颈在哪、怎么绕过
真正卡住的往往不是 JSON 解析本身,而是 I/O 和内存:一次 file() 读几百 MB 文件会爆内存;反复 fgets() 在机械硬盘上慢;json_decode() 对超长嵌套结构(如深度 > 512)也会变慢。
实用优化点:
- 用
stream_set_read_buffer($fp, 0)关闭读缓冲(某些场景反而更快) - 避免在循环里做日志输出或数据库插入;攒批处理(例如每 100 行 commit 一次)
- 对已知结构的数据,考虑用
json_decode($line, false, 512, JSON_BIGINT_AS_STRING)控制深度和大整数处理 - 极端情况(GB 级)可结合
pcntl_fork()分块读取,但要注意进程间文件指针不共享,得按字节偏移切分
最常被忽略的是:没关掉 Xdebug。开发环境开着 Xdebug 解析 10 万行 JSONL,速度可能差 5–10 倍。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











