必须严格校验手机号为11位且以1开头,提取数字后双判;姓名需正则清理不可见字符再trim;CSV须先去BOM并检测编码;批量插入应通过临时表加事务保障原子性。

导入前必须校验手机号格式
班级通信录里常混入“138****1234”“138-1234-5678”甚至“手机:13812345678”这类脏数据,filter_var($phone, FILTER_SANITIZE_NUMBER_INT) 会直接删掉所有非数字字符,但可能留下11位以外的数字串(比如座机带区号或学生填了QQ号)。实际要的是严格11位纯数字且以1开头的手机号。
- 先用
preg_replace('/[^\d]/', '', $phone)提取全部数字 - 再用
strlen($cleaned) === 11 && $cleaned[0] === '1'做长度和首字符双判 - 遇到“138123456789”这种12位的,不能截断,得标为异常行并记录原始值,否则数据错位
姓名字段去重空格与不可见字符
Excel复制粘贴进CSV时,姓名常带全角空格、零宽空格(\u200b)、换行符,trim() 对它们完全无效。直接 mb_trim($name, ' \t\n\r\0\x0B\u{2000}-\u{200F}\u{2028}\u{2029}\u{202F}\u{2060}\u{FEFF}') 也不现实——PHP原生没 mb_trim。
- 用
preg_replace('/[\s\u{2000}-\u{200F}\u{2028}\u{2029}\u{202F}\u{2060}\u{FEFF}]+/u', ' ', $name)替换所有空白类字符为单个半角空格 - 再套一层
trim()去首尾 - 最后检查是否为空字符串或只剩一个空格,是则视为无效姓名,不能设默认值“未知”,得留空或报错
Excel读取后立刻转UTF-8并检测BOM
Windows下导出的Excel CSV常带UTF-8 BOM(\xEF\xBB\xBF),导致第一列字段名变成 "姓名",后续用 $row['姓名'] 取值失败。更隐蔽的是,BOM可能只出现在某几行,造成部分数据解析偏移。
- 读取每行原始字符串后,先用
ltrim($line, "\xEF\xBB\xBF")剥离BOM - 再用
mb_detect_encoding($line, ['UTF-8', 'GBK', 'BIG5'], true)判断编码,非UTF-8则强制mb_convert_encoding($line, 'UTF-8', 'GBK') - 别依赖
iconv('GBK', 'UTF-8//IGNORE', $line),它会静默丢字,而班级名“閔行區實驗中學”丢一个字就成“行區實驗中學”
批量插入前用临时表做原子性清洗
直接在目标表上 UPDATE ... SET name = TRIM(name) 风险太大——万一中间出错,脏数据已写入,回滚困难。正确做法是把清洗逻辑下沉到数据库层,靠事务+临时表隔离。
立即学习“PHP免费学习笔记(深入)”;
- 建临时表
CREATE TEMPORARY TABLE temp_contacts AS SELECT * FROM contacts WHERE 0 - 用
LOAD DATA INFILE或PDO::prepare()批量INSERT进临时表,过程中对每列加TRIM()、REPLACE()等函数 - 清洗完再用
INSERT INTO contacts SELECT * FROM temp_contacts一次性落库 - 这样即使清洗脚本中断,临时表自动销毁,主表毫发无损
清洗真正的难点不在正则或编码转换,而在如何让“错误可定位、过程可中断、结果可验证”。比如手机号校验失败,必须原样保留原始值并标记行号;姓名清洗后长度突变(如“张三 ”变“张三”),得记录变更日志供人工复核——这些细节不写进代码,光靠函数调用解决不了问题。











