【问题标题】:Accent-insensitive substring matching不区分重音的子字符串匹配
【发布时间】:2010-08-27 09:36:00
【问题描述】:

我有一个搜索功能,它从 InnoDB 表 (utf8_spanish_ci collat​​ion) 中获取数据并将其显示在 HTML 文档 (UTF-8 charset) 中。用户键入一个子字符串并获得一个匹配列表,其中第一个子字符串出现突出显示,例如:

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

【问题讨论】:

    标签: php utf-8 collation


    【解决方案1】:

    您可以使用Normalizer 将字符串规范化为字符被分解的Normalization Form KD (NFKD),因此Á (U+00C1) 被分解为字母A (U+0041) 的组合) 和组合标记́ (U+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));
    }
    

    【讨论】:

    • 不错的方法,谢谢你的想法。不幸的是,它的可用性是 (PHP 5 >= 5.3.0, PECL intl >= 1.0.0) 并且生产服务器运行 PHP/5.2.14,因此使用它意味着安装相应的 PECL 库: (
    【解决方案2】:

    使用 PEAR I18N_UnicodeNormalizer-1.0.0

    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 + `
    );
    

    → AEIOUaeiou

    【讨论】:

      猜你喜欢
      • 2014-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-16
      • 2011-03-20
      • 2014-10-26
      • 2017-12-09
      • 2018-04-28
      相关资源
      最近更新 更多