You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP str_ireplace如何实现重音不敏感匹配替换保留原文

问题说明

需要实现PHP端重音不敏感的搜索词高亮效果,匹配规则和MySQL设置utf8字符集后的重音不敏感查询对齐:

  • 文本中出现带重音的Français时,替换为<mark>Français</mark>
  • 文本中出现无重音的Francais时,替换为<mark>Francais</mark>
  • 全程保留文本原始拼写,不得修改原有重音字符。

之前尝试的两种方案都存在问题:

  • 原生str_ireplace函数为重音敏感匹配,无法识别带/不带重音的同根词,比如Francais匹配不到Français
  • 用重音映射表直接转换原文本去重音后匹配,会破坏原文本的重音格式,无法满足保留原始拼写的要求。
实现方案

核心逻辑:不修改原始文本,单独生成一份去除所有重音的小写「对照文本」,在对照文本中定位所有匹配位置,再根据位置从原文本中截取对应长度的原始字符串套高亮标签,从后往前替换避免位置偏移。

完整代码

首先写去重音的工具函数,优先用PHP国际化扩展的转写器覆盖全量重音字符,无扩展时用手写映射表兜底:

function removeAccents(string $str): string
{
    if (extension_loaded('intl')) {
        $transliterator = Transliterator::create('NFD; [:Nonspacing Mark:] Remove; NFC;');
        return $transliterator->transliterate($str);
    }
    $accentMap = [
        'Š'=>'S', 'š'=>'s', 'Ž'=>'Z', 'ž'=>'z', 'À'=>'A', 'Á'=>'A', 'Â'=>'A', 'Ã'=>'A', 'Ä'=>'A', 'Å'=>'A', 'Æ'=>'A', 'Ç'=>'C', 'È'=>'E', 'É'=>'E',
        'Ê'=>'E', 'Ë'=>'E', 'Ì'=>'I', 'Í'=>'I', 'Î'=>'I', 'Ï'=>'I', 'Ñ'=>'N', 'Ò'=>'O', 'Ó'=>'O', 'Ô'=>'O', 'Õ'=>'O', 'Ö'=>'O', 'Ø'=>'O', 'Ù'=>'U',
        'Ú'=>'U', 'Û'=>'U', 'Ü'=>'U', 'Ý'=>'Y', 'Þ'=>'B', 'ß'=>'Ss', 'à'=>'a', 'á'=>'a', 'â'=>'a', 'ã'=>'a', 'ä'=>'a', 'å'=>'a', 'æ'=>'a', 'ç'=>'c',
        'è'=>'e', 'é'=>'e', 'ê'=>'e', 'ë'=>'e', 'ì'=>'i', 'í'=>'i', 'î'=>'i', 'ï'=>'i', 'ð'=>'o', 'ñ'=>'n', 'ò'=>'o', 'ó'=>'o', 'ô'=>'o', 'õ'=>'o',
        'ö'=>'o', 'ø'=>'o', 'ù'=>'u', 'ú'=>'u', 'û'=>'u', 'ý'=>'y', 'þ'=>'b', 'ÿ'=>'y'
    ];
    return strtr($str, $accentMap);
}

再写高亮逻辑,所有字符串操作使用mb_*多字节函数避免utf8乱码:

function accentInsensitiveHighlight(string $fullText, string $wordToSearch): string
{
    $searchLen = mb_strlen($wordToSearch);
    if ($searchLen === 0) return $fullText;

    // 生成无重音的小写对照版本,用于匹配定位
    $normalizedText = mb_strtolower(removeAccents($fullText));
    $normalizedSearch = mb_strtolower(removeAccents($wordToSearch));

    // 收集所有匹配位置
    $matches = [];
    $offset = 0;
    while (($pos = mb_strpos($normalizedText, $normalizedSearch, $offset)) !== false) {
        $matches[] = $pos;
        $offset = $pos + $searchLen;
    }

    // 从后往前替换,避免插入标签导致的位置偏移
    foreach (array_reverse($matches) as $pos) {
        // 直接截取原文本对应位置的内容,完全保留原始拼写
        $originalSegment = mb_substr($fullText, $pos, $searchLen);
        $fullText = mb_substr($fullText, 0, $pos) 
            . '<mark>' . $originalSegment . '</mark>' 
            . mb_substr($fullText, $pos + $searchLen);
    }
    return $fullText;
}

使用方式

直接替换原有str_ireplace的逻辑即可:

// 原有逻辑
// $markReplace = "<mark>" . $wordToSearch . "</mark>";
// $fullText = str_ireplace($wordToSearch, $markReplace, $fullText);

// 替换为
$fullText = accentInsensitiveHighlight($fullText, $wordToSearch);
echo $fullText;

效果验证

传入原文本J'aime le Français, je préfère ça au francais de base.,搜索词为francais时,输出结果为:
J'aime le <mark>Français</mark>, je préfère ça au <mark>francais</mark> de base.
两个不同拼写的匹配项都被正确高亮,原有重音字符完全保留,没有被修改。

注意事项

  • 运行环境需要开启mbstring扩展,该扩展为PHP默认内置扩展,绝大多数生产环境都已默认开启
  • 建议安装intl扩展,转写器支持的重音字符覆盖范围远大于手写映射表,能匹配更多特殊重音组合

内容的提问来源于stack exchange,提问作者user14337309

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 11:06:24