重音不敏感的子串匹配

时间:2010-08-27 09:36:00

标签: php utf-8 collation

我有一个搜索功能,可以从InnoDB表(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 个答案:

答案 0 :(得分:5)

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

答案 1 :(得分:1)

使用 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