CSV导入MySQL时UTF-8 BOM隐藏字符问题及非str_replace解决方案咨询
解决UTF-8 BOM导致MySQL插入语句的隐藏字符问题
嘿,这个UTF-8 BOM的坑我之前也踩过!你看到的就是UTF-8文件开头的字节顺序标记(BOM),它是三个不可见的字节(EF BB BF),很多编辑器默认保存UTF-8文件时会自动加上,但PHP的fgetcsv并不会自动处理它,所以第一行的第一个字段就会带上这个标记,最终导致SQL里出现奇怪的•或者直接破坏整数字段的取值。
下面给你几个不需要手动复制隐藏字符的可靠解决方案:
方案1:读取文件时自动检测并移除BOM
这是最彻底的方法,在打开文件后先检查开头是否有BOM,如果有就跳过它,再继续读取内容。修改你的原脚本如下:
if (($handle = fopen($_FILES['csvfile']['tmp_name'], "r")) !== FALSE) { // 新增:检测并移除UTF-8 BOM $bom = pack('H*','EFBBBF'); if (fread($handle, 3) !== $bom) { rewind($handle); // 如果没有BOM,回到文件开头继续读取 } $counter=0; while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) { $nullCol = "NULL"; $sql_q="INSERT INTO $dbupload ($db_cols) VALUES "; $sql_q2="('" . implode("','",$data) . "'),"; $sql_q2=rtrim($sql_q2,","); $sql_q2=str_replace("''",$nullCol,$sql_q2); $sql_q2=str_replace(' ','',$sql_q2); // 去掉无效的空字符替换代码 // $sql_q2=str_replace('','',$sql_q2); //There is a weird hidden character between '' $sql_q.=$sql_q2; $sql_q.=" ON DUPLICATE KEY UPDATE "; $col_ar=explode(",",$db_cols); foreach($col_ar as $col){ $sql_q.="$col=VALUES($col),"; } $sql_q=rtrim($sql_q,","); $insert_data=$connection->new_query($sql_q,true); $counter++; } fclose($handle); }
这段代码通过pack生成BOM的字节序列,然后读取文件前3个字节对比,如果匹配就跳过,不匹配就把文件指针移回开头,确保后续读取的内容是干净的。
方案2:处理每一行的第一个字段
如果不想修改文件读取逻辑,也可以在读取到每一行数据后,直接清除第一个字段里的BOM标记:
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) { // 新增:移除第一个字段的UTF-8 BOM $data[0] = preg_replace('/^\x{FEFF}/u', '', $data[0]); // 后续原有逻辑不变... $nullCol = "NULL"; $sql_q="INSERT INTO $dbupload ($db_cols) VALUES "; // ... }
正则表达式/^\x{FEFF}/u会匹配字符串开头的BOM字符(Unicode编码的U+FEFF),并替换为空,这样就能确保第一个字段的内容是纯净的。
方案3:从源头避免BOM生成
最根本的解决方式是保存CSV文件时选择“UTF-8无BOM”格式:
- 如果你用Notepad++,保存时选择「编码」→「UTF-8」(注意不是「UTF-8 BOM」);
- 其他编辑器比如VS Code,可以在右下角的编码选项里选择「UTF-8」,并确保没有勾选“带BOM”的选项。
这样生成的CSV文件本身就不会带BOM,后续的读取和处理自然不会有问题。
另外,你之前写的$sql_q2=str_replace('','',$sql_q2);其实是无效的——替换空字符串为空字符串不会有任何效果,那个隐藏字符是BOM,不是空字符,所以这个操作根本没法移除它。
内容的提问来源于stack exchange,提问作者Eduardo Ponce de Leon
相关产品推荐
相关产品推荐

