在PHP中处理文本文件去重时,常规的字符串全等比对只能识别完全相同的行,无法处理词序不同但语义一致的情况,比如“苹果 香蕉 橘子”和“橘子 苹果 香蕉”属于同一语义内容,需要被判定为重复行。我们需要通过拆分、排序、标识生成的方式实现这类语义去重。

实现核心思路
按语义去重忽略词序的核心逻辑可以分为三步:
- 读取文本文件的每一行内容,对每行进行预处理,比如去除首尾空白、统一分隔符
- 将每行内容拆分为独立的词单元,对词单元进行排序,再拼接为固定格式的字符串作为该行的唯一语义标识
- 维护一个标识集合,若当前行的语义标识已存在于集合中则跳过,否则保留该行并将标识加入集合,最后输出去重后的内容
完整实现代码
以下是完整的PHP实现示例,假设文本文件每行以空格作为词的分割符,你可以根据实际需求调整分隔逻辑:
<?php
/**
* 按语义去重文本文件,忽略词序
* @param string $filePath 待处理文本文件路径
* @param string $outputPath 去重后输出文件路径
* @return bool 处理是否成功
*/
function semanticDeduplicateTextFile($filePath, $outputPath) {
// 检查文件是否存在
if (!file_exists($filePath)) {
return false;
}
// 读取文件所有行
$lines = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($lines === false) {
return false;
}
$seenIdentifiers = [];
$resultLines = [];
foreach ($lines as $line) {
// 去除首尾空白
$trimmedLine = trim($line);
if (empty($trimmedLine)) {
// 保留空行,可根据需求调整
$resultLines[] = $trimmedLine;
continue;
}
// 按空格拆分词,可根据实际需求替换为正则拆分多分隔符
$words = explode(' ', $trimmedLine);
// 过滤空词
$words = array_filter($words, function($word) {
return !empty(trim($word));
});
// 重新索引数组
$words = array_values($words);
// 对词进行排序,忽略词序
sort($words);
// 生成语义标识,拼接排序后的词
$identifier = implode(' ', $words);
// 检查标识是否已存在
if (!in_array($identifier, $seenIdentifiers)) {
$seenIdentifiers[] = $identifier;
$resultLines[] = $trimmedLine;
}
}
// 写入输出文件
$writeResult = file_put_contents($outputPath, implode(PHP_EOL, $resultLines) . PHP_EOL);
return $writeResult !== false;
}
// 使用示例
$sourceFile = 'source.txt';
$targetFile = 'deduplicated.txt';
$success = semanticDeduplicateTextFile($sourceFile, $targetFile);
if ($success) {
echo '文本去重完成,结果已保存到' . $targetFile;
} else {
echo '文本去重处理失败';
}
?>
代码说明与调整建议
上述代码中使用了explode(' ', $trimmedLine)拆分词,仅支持空格作为分隔符,如果你的文本使用逗号、制表符等其他分隔符,可以替换为preg_split('/s+/', $trimmedLine)来按任意空白字符拆分,或者根据实际分隔符调整正则规则。
如果需要忽略大小写,可以在拆分词之后对每个词执行strtolower($word)处理,再参与排序和标识生成,这样“Apple Banana”和“apple banana”也会被判定为重复内容。
注意事项
- 处理大文件时,一次性读取所有行可能会占用大量内存,可以改为逐行读取的方式,每次读取一行处理后再读取下一行
- 如果文本中包含标点符号,需要提前做清洗处理,比如去除标点后再拆分词,避免“苹果,”和“苹果”被判定为不同词
- 生成的语义标识集合如果过大,可以考虑使用
array_flip将标识作为键存储,提升存在性判断的效率
该方法适用于词序不同但词集合完全相同的场景,若需要处理近义词、同义表述的语义去重,需要额外引入分词和语义相似度计算的扩展,复杂度会更高。