高亮文本(用<span>标签包裹)时保留重音字符的方法
解决PHP高亮带重音文本时保留重音的问题
你的代码里重音丢失的核心原因是:
- 使用
iconv('utf-8', 'ISO-8859-1//IGNORE')转换编码时,很多UTF-8重音字符在ISO-8859-1中没有对应,直接被忽略了 Normalizer::FORM_D会把重音字符分解成基础字符+单独的重音标记,后续替换时没有还原这些标记
要实现匹配带重音的内容但保留原始重音字符,正确的做法是:不修改原始文本的编码和结构,只在匹配逻辑中做无重音的等价匹配,替换时用原始文本的内容。
修正后的代码示例
echo highlightTerm("Would you like a café, Mister Kàpêk?", "kape caf"); function highlightTerm($text, $keyword) { // 创建转写器:将所有字符转为拉丁字母,再去除重音 $transliterator = Transliterator::create('Any-Latin; Latin-ASCII; [\u0300-\u036f] Remove'); // 处理关键词,得到无重音的基准版本 $normalizedKeyword = $transliterator->transliterate($keyword); $words = explode(" ", $normalizedKeyword); // 为每个关键词生成能匹配其重音变体的正则片段 $patternSegments = []; foreach ($words as $word) { $charPattern = ''; foreach (str_split($word) as $char) { // 匹配基础字符 + 任意重音标记的组合 $charPattern .= "(?:" . preg_quote($char, '/') . "|[\p{M}]+" . preg_quote($char, '/') . "|" . preg_quote($char, '/') . "[\p{M}]+)"; } $patternSegments[] = $charPattern; } $fullPattern = implode('|', $patternSegments); // 使用回调替换,确保插入的是原始文本中的带重音字符 return preg_replace_callback( "/\b($fullPattern)\b/ui", // 单词边界避免部分匹配(可根据需求移除) function($matches) { return '<span style="background:yellow;">' . $matches[1] . '</span>'; }, $text ); }
代码说明
- 转写器处理:借助
Transliterator将关键词转为无重音的形式,作为匹配的基准,同时原始文本保持不变。 - 正则匹配逻辑:为每个关键词的字符生成能匹配其所有重音变体的模式,确保
caf能匹配café,kape能匹配Kàpêk中的对应部分。 - 回调替换:
preg_replace_callback直接使用匹配到的原始文本片段进行高亮,完全保留重音字符。
更简化的替代方案
如果不需要太复杂的重音匹配逻辑,也可以利用preg_replace的u修饰符结合字符等价类:
echo highlightTerm("Would you like a café, Mister Kàpêk?", "kape caf"); function highlightTerm($text, $keyword) { // 生成无重音的关键词正则 $normalizedKeyword = preg_replace('/[\p{M}]/u', '', Normalizer::normalize($keyword, Normalizer::FORM_KD)); $words = array_map('preg_quote', explode(" ", $normalizedKeyword)); $pattern = implode('|', $words); // 匹配时忽略重音,替换用原始文本 return preg_replace_callback( "/(\b(?:$pattern)\b)/ui", function($matches) { return '<span style="background:yellow;">' . $matches[1] . '</span>'; }, $text ); }
内容的提问来源于stack exchange,提问作者user934820
相关产品推荐
相关产品推荐

