代码之家  ›  专栏  ›  技术社区  ›  Álvaro González

不区分重音的子串匹配

  •  2
  • Álvaro González  · 技术社区  · 15 年前

    utf8_spanish_ci 排序规则)并将其显示在HTML文档中( UTF-8 字符集)。用户键入子字符串并获得突出显示第一个子字符串出现的匹配列表,例如:

    Matches for "AL":
    
    Álava
    <strong>Al</strong>bacete
    <strong>Al</strong>mería
    Ciudad Re<strong>al</strong>
    Málaga
    

    从这个例子中可以看出,搜索忽略了大小写和重音的差异(MySQL会自动处理)。但是,我用于高亮匹配的代码无法执行后一种操作:

    <?php
    
    private static function highlightTerm($full_string, $match){
        $start = mb_stripos($full_string, $match);
        $length = mb_strlen($match);
    
        return
            htmlspecialchars( mb_substr($full_string, 0, $start)) .
            '<strong>' . htmlspecialchars( mb_substr($full_string, $start, $length) ) . '</strong>' .
            htmlspecialchars( mb_substr($full_string, $start+$length) );
    }
    
    ?>
    

    有没有一个明智的方法来解决这个问题,不意味着硬编码所有可能的变化?

    更新: 系统规范是PHP/5.2.14和MySQL/5.1.48

    2 回复  |  直到 15 年前
        1
  •  5
  •   Gumbo    15 年前

    你可以用这个 Normalizer 将字符串规范化为 Normalization Form KD (NFKD) Á (U+00C1)被分解成字母的组合 A (U+0041)和组合标记 ́ (电话+0301):

    $str = Normalizer::normalize($str, Normalizer::FORM_KD);
    

    然后修改搜索模式以匹配这些可选标记:

    $pattern = '/('.preg_replace('/\p{L}/u', '$0\p{Mn}?', preg_quote($term, '/')).')/ui';
    

    然后用 preg_replace :

    preg_replace($pattern, '<strong>$0</strong>', htmlspecialchars($str))
    

    所以完整的方法是:

    private static function highlightTerm($str, $term) {
        $str = Normalizer::normalize($str, Normalizer::FORM_KD);
        $pattern = '/('.preg_replace('/\p{L}/u', '$0\p{Mn}?', preg_quote($term, '/')).')/ui';
        return preg_replace($pattern, '<strong>$0</strong>', htmlspecialchars($str));
    }
    
        2
  •  1
  •   eleg    15 年前

    使用

    include('…');
    
    echo preg_replace(
     '/(\P{L})/ui', // replace all except members of Unicode class "letters", case insensitive
     '', // with nothing → drop accents
     I18N_UnicodeNormalizer::toNFKD('ÅÉÏÔÙåéïôù') // ù → u + `
    );
    

    推荐文章