PHP号码前缀匹配逻辑异常:匹配号码被写入CSV文件求助
解决前缀匹配CSV号码时的错误写入问题
我看了你的代码和需求,问题出在循环逻辑的顺序完全搞反了,而且重复读取文件的操作不仅低效,还直接导致了错误的结果。
问题根源分析
你当前的逻辑是:拿一个前缀,然后把file2从头到尾读一遍,只要当前号码不匹配这个前缀,就写入file3。这就意味着:
- 一个号码哪怕匹配了某个前缀,只要它不匹配其他前缀,就会被反复写入
file3。比如3246611864654匹配324661这个前缀,但当遍历到324662、324663这些前缀时,它都会因为不匹配而被写入file3,这就是匹配前缀的号码也出现在file3里的核心原因。 - 每次循环前缀都重新打开
file2,频繁的文件IO操作非常影响性能,完全没必要。
修正后的解决方案
正确的逻辑应该是:先把所有前缀收集到一个数组里,然后遍历file2的每个号码,检查这个号码是否匹配任意一个前缀,如果都不匹配,再写入file3。
下面是修正后的代码:
<?php $file1 = "csv/file1.csv"; $file2 = "csv/file2.csv"; $file3 = "csv/file3.csv"; // 第一步:一次性收集所有有效的前缀到数组 $prefixes = []; // 读取file1并跳过空行和换行符 $prefixLines = file($file1, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); foreach ($prefixLines as $line) { // 提取每行开头的数字前缀 if (preg_match('/^([0-9]+)/', $line, $matches)) { $prefixes[] = $matches[1]; } } // 第二步:遍历file2的每个号码,检查是否匹配任意前缀 $outputFile = fopen($file3, "w"); if (!$outputFile) { die("无法打开输出文件 {$file3}"); } $phoneLines = file($file2, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); foreach ($phoneLines as $line) { $line = trim($line); if (empty($line)) continue; // 提取号码部分 if (preg_match('/([0-9]+)/', $line, $matches)) { $phoneNumber = $matches[1]; $isMatched = false; // 检查是否匹配任意一个前缀 foreach ($prefixes as $prefix) { // PHP 8+ 用str_starts_with更高效,低版本可以换成preg_match("/^{$prefix}/", $phoneNumber) if (str_starts_with($phoneNumber, $prefix)) { echo "匹配前缀 {$prefix} 的号码:{$line} <br>"; $isMatched = true; break; // 匹配到一个就停止检查,提升效率 } } // 只有完全不匹配任何前缀的号码,才写入file3 if (!$isMatched) { fputcsv($outputFile, [$line], ",", "'"); } } } fclose($outputFile); echo "处理完成!"; ?>
额外优化建议
如果你的CSV文件格式规范(有表头、字段用逗号分隔),建议用fgetcsv读取文件,比正则提取更准确,能避免引号包裹内容等特殊情况的错误:
// 用fgetcsv读取file1的前缀(跳过表头) $prefixFile = fopen($file1, "r"); if (!$prefixFile) die("无法打开 {$file1}"); fgetcsv($prefixFile); // 跳过表头行 while (($row = fgetcsv($prefixFile)) !== false) { $prefix = trim($row[0]); if (is_numeric($prefix)) { $prefixes[] = $prefix; } } fclose($prefixFile); // 用fgetcsv读取file2的号码(跳过表头) $phoneFile = fopen($file2, "r"); if (!$phoneFile) die("无法打开 {$file2}"); fgetcsv($phoneFile); // 跳过表头行 while (($row = fgetcsv($phoneFile)) !== false) { $phoneNumber = trim($row[0]); $isMatched = false; foreach ($prefixes as $prefix) { if (str_starts_with($phoneNumber, $prefix)) { echo "匹配前缀 {$prefix} 的号码:{$row[0]} {$row[1]}<br>"; $isMatched = true; break; } } if (!$isMatched) { fputcsv($outputFile, $row, ",", "'"); // 直接写入原数组,保持CSV格式 } } fclose($phoneFile);
另外,如果前缀数量较多,可以把前缀按长度从长到短排序,避免短前缀先匹配导致长前缀被忽略的问题(比如同时有34661和34661789时,先检查长前缀)。
内容的提问来源于stack exchange,提问作者Ervis Hysi
相关产品推荐
相关产品推荐

