PHP如何判断文件是否含BOM头_PHP识别BOM头法【编码】

3次阅读

php应使用fopen(‘rb’) + fread(4) + bin2hex()读取文件前4字节并比对十六进制值判断bom,而非mb_detect_encoding或file_get_contents;常见BOM为’efbbbf’(UTF-8)、’fffe’(UTF-16 LE)等。

PHP如何判断文件是否含BOM头_PHP识别BOM头法【编码】

PHP如何读取文件前3字节判断BOM

直接读取文件开头几个字节,比用 mb_detect_encoding 可靠得多——后者根本不识别BOM,且对UTF-8无BOM和GBK混杂的情况极易误判。

关键不是“检测编码”,而是“确认有没有BOM”。BOM是硬编码在文件头的字节序列,只需比对:

  • EF BB BF → UTF-8 BOM
  • FF FE → UTF-16 LE BOM(小端)
  • FE FF → UTF-16 BE BOM(大端)
  • FF FE 00 00 → UTF-32 LE
  • 00 00 FE FF → UTF-32 BE

实操建议:用 fopen + fread 读前4字节(覆盖所有常见BOM长度),再用 bin2hex 转成小写十六进制字符串比对。避免用 file_get_contents 全量读取大文件,也别依赖 mb_check_encoding——它不看BOM。

为什么 file_get_contents($f, false, NULL, 0, 3) 不够用

这个写法看似省事,但有两处隐患:file_get_contents 默认按当前脚本编码解析内容,若文件含BOM而PHP内部处理时做了隐式转换(比如开启 default_charset 或用了输出缓冲),前三字节可能被截断或变形;更严重的是,它无法区分 xEFxBBxBF 和普通中文字符的UTF-8三字节序列(如“一”是 xE4xB8x80),纯靠长度不够。

立即学习PHP免费学习笔记(深入)”;

必须绕过字符层,走二进制流:

  • fopen($f, 'rb') 强制二进制模式
  • fread($fp, 4) 读4字节(UTF-32 BOM需4字节)
  • unpack('H*', $bytes)bin2hex($bytes) 获取原始十六进制
  • 注意:strlen($bytes) 必须 ≥ 3 才能判断UTF-8 BOM,空文件或只读到1–2字节时不能贸然断言“无BOM”

实际处理时容易漏掉的边界情况

BOM不一定在文件最开头——如果文件被追加写入、或用某些编辑器“保存为带BOM”时中间插入了不可见字符,^xEFxBBxBF 正则会失效。所以严格来说,BOM只应出现在文件起始位置,其他位置的相同字节不算。

还有几个易错点:

  • windows记事本另存为UTF-8时,一定带BOM;vs code默认不带,但用户可手动开启——不能假设编辑器行为一致
  • PHP CLI下读取文件,$_SERVER['SCRIPT_FILENAME'] 指向的文件本身若有BOM,会导致“Cannot modify header information”错误,但错误提示里完全不提BOM
  • curlfile_get_contents 请求远程URL返回的内容,即使响应头声明 Content-Type: text/html; charset=utf-8,也不能反推它是否含BOM——BOM是文件/响应体字节层面的事,和http头无关

封装一个可靠的 is_file_has_bom 函数

不要每次重复写 fopen/fread/bin2hex,封装成函数更稳妥。注意它只负责判断,不负责去除:

function is_file_has_bom(string $path): bool {     if (!is_readable($path)) {         return false;     }     $fp = fopen($path, 'rb');     if (!$fp) {         return false;     }     $bytes = fread($fp, 4);     fclose($fp);     $hex = bin2hex($bytes);     return in_array($hex, [         'efbbbf',    // UTF-8         'fffe',      // UTF-16 LE         'feff',      // UTF-16 BE         'fffe0000',  // UTF-32 LE         '0000feff',  // UTF-32 BE     ], true); }

这个函数不依赖扩展,不触发任何编码转换,也不加载整个文件。真正麻烦的是后续动作:发现BOM后,你是跳过它解析、还是用 file_put_contents 去除、还是报错中断?这些决策得结合业务场景来定——比如配置文件含BOM可能导致 parse_ini_file 解析失败,但日志文件里的BOM通常可以忽略。

text=ZqhQzanResources