PHP文本短语频率统计:解决标点与子串误匹配问题
解决停用词间短语统计的子串匹配问题
嘿,这个问题我太有共鸣了——之前做短语统计的时候也踩过substr_count的坑!想要准确统计停用词之间的完整短语,又不想傻兮兮遍历一堆标点,正则表达式绝对是最优解,而且比你想的简单。
问题根源拆解
你遇到的核心问题:
substr_count是纯子串匹配,它完全不考虑上下文,只要文本里出现目标字符序列就计数,所以会把“stage”里的“age”误判成独立短语- 手动给短语加空格的方式,没法覆盖短语前后是标点(逗号、句号等)或在文本首尾的场景
- 遍历标点数组不仅繁琐,还容易遗漏特殊标点(比如分号、冒号、引号等)
最优实现方案
用正则的边界匹配逻辑,一次性覆盖所有合法的短语分隔场景:
- 用
\W匹配所有非单词字符(包括空格、逗号、句号、感叹号等所有你需要的分隔符) - 用
^和$覆盖短语在文本开头或结尾的情况 - 用
preg_match_all统计准确的匹配次数,而非子串出现次数
修改后的完整代码
if (in_array($word, $this->stopwords)) { $cleanPhrase = preg_replace("/[^A-Za-z ]/", '', $currentPhrase); $cleanPhrase = trim($cleanPhrase); if($cleanPhrase != "" && strlen($cleanPhrase) > 2) { // 构建正则匹配模式:确保短语是独立的,而非子串 $pattern = '/(^|\W)' . preg_quote($cleanPhrase, '/') . '(\W|$)/'; // 统计匹配次数 preg_match_all($pattern, $normalisedText, $matches); $this->Phrases[$cleanPhrase] = count($matches[0]); $currentPhrase = ""; } continue; } else { $currentPhrase = $currentPhrase . $word . " "; }
关键细节说明
\W的作用:等价于[^a-zA-Z0-9_],能匹配所有非单词类字符,完美替代你要遍历的标点数组,覆盖所有可能的短语分隔场景^和$的作用:确保短语出现在文本最开头(比如“Wuthering Heights is...”里的短语)或最结尾(比如“...love Wuthering Heights”)时也能被正确统计preg_quote的作用:虽然你的cleanPhrase已经过滤成只有字母和空格,但加上它能防止未来规则变更时,短语里出现正则特殊字符(比如+、*)破坏正则结构,是代码健壮性的保障- 大小写处理:如果你的
normalisedText已经做了大小写归一化(比如全转小写),可以去掉正则里的i修饰符,提升一点匹配性能
内容的提问来源于stack exchange,提问作者Dan Hastings
相关产品推荐
相关产品推荐

