PHP如何检测文件开头字符 兼容处理UTF8与UTF8 BOM格式CSV
兼容带BOM/无BOM UTF-8格式CSV的PHP实现
核心实现逻辑
UTF-8编码的BOM头是文件起始位置固定的3个字节,对应十六进制值为 0xEF 0xBB 0xBF。不需要固定执行跳过3字节的操作,打开文件后先读取前3字节做匹配判断:
- 若读取到的3字节和BOM标识完全一致,说明文件带BOM,当前文件指针已经停在BOM之后的有效内容起始位,直接开始读取CSV内容即可
- 若读取到的3字节不符合BOM特征,说明是无BOM的普通UTF-8文件,将文件指针重置到文件起始位置(偏移0位),再开始读取内容即可
替换原有固定跳过BOM的代码
把原代码中写死的fseek($handle, 3);替换为以下检测逻辑:
// 自动检测UTF-8 BOM头,兼容两种格式文件 $bomCheck = fread($handle, 3); if ($bomCheck !== "\xEF\xBB\xBF") { // 未检测到BOM,指针回到文件开头 rewind($handle); }
这里直接用双引号字符串写入转义的BOM字节比拼接chr()写法更简洁,
rewind($handle)等价于fseek($handle, 0),是PHP专门提供的重置文件指针到开头的函数,可读性更好。
修改后的完整业务代码
<?php include("connexion.php"); $dir = '//server/d$/ftp/GET/'; $allFiles = scandir($dir); $dest = '//server/d$/ftp/GET/COPIES/'; foreach($allFiles as $file) { if (!in_array($file,array(".",".."))) { $file = $dir.$file; $filename = basename( $file ); if ( strpos( $filename, 'BI1_' ) === 0 ) { if (($handle = fopen($file, "r")) !== false) { // 自动检测UTF-8 BOM,兼容带BOM/无BOM文件 $bomCheck = fread($handle, 3); if ($bomCheck !== "\xEF\xBB\xBF") { rewind($handle); } $bi1_values = array(); while (($data = fgetcsv($handle, 9000000, ";")) !== false) { $bi1_values[] = "('$data[0]', '".str_replace("'", "''",$data[1])."','$date1','$date2','$data[2]','$data[4]','".str_replace("'", "''",$data[5])."','".str_replace("'", "''",$data[6])."')"; if (count($bi1_values) == 1000) { $query = "insert into dbo.Sales (storenumber, storename, date, time, TransRef, stylecode, color, size) values " . implode(',', $bi1_values); $stmt = $conn->query( $query ); if (!$stmt) { $file1 = "D:/xampp/htdocs/errors/erreur_BI1.txt"; file_put_contents($file1, $query . PHP_EOL, FILE_APPEND | LOCK_EX); } $bi1_values = array(); } } // 补充:处理最后一批不足1000条的剩余数据 if (!empty($bi1_values)) { $query = "insert into dbo.Sales (storenumber, storename, date, time, TransRef, stylecode, color, size) values " . implode(',', $bi1_values); $stmt = $conn->query( $query ); if (!$stmt) { $file1 = "D:/xampp/htdocs/errors/erreur_BI1.txt"; file_put_contents($file1, $query . PHP_EOL, FILE_APPEND | LOCK_EX); } } fclose($handle); // 处理完成后移动文件到备份文件夹 if(!rename($file, $dest . $filename)) { echo "error"; } } } } } ?>
额外注意事项
- 该检测逻辑仅针对UTF-8编码的BOM做判断,不会影响GBK、UTF-16等其他编码文件的读取逻辑
- 现有代码通过字符串拼接生成SQL语句,即使做了单引号转义依然存在注入风险,生产环境建议改用PDO预处理语句绑定参数实现批量插入,安全性和稳定性更高
- 原代码逻辑中如果CSV最后一批数据不足1000条不会执行入库操作,上述完整代码已经补充了剩余数据的入库逻辑,避免尾部数据丢失。
内容的提问来源于stack exchange,提问作者Eric27
相关产品推荐
相关产品推荐

