【问题标题】:Regex to match string with and without special/accented characters?正则表达式匹配带有和不带有特殊/重音字符的字符串?
【发布时间】:2013-09-26 08:31:31
【问题描述】:

是否有正则表达式可以匹配带有和不带特殊字符的特定字符串?可以这么说,对特殊字符不敏感。

就像céra 将匹配cera,反之亦然。

有什么想法吗?

编辑:我想匹配带有和不带有特殊/重音字符的特定字符串。不仅仅是任何字符串/字符。

测试示例:

$clientName   = 'céra';
$this->search = 'cera';

$compareClientName = strtolower(iconv('utf-8', 'ascii//TRANSLIT', $clientName));
$this->search      = strtolower($this->search);

if (strpos($compareClientName, $this->search) !== false)
{
    $clientName = preg_replace('/(.*?)('.$this->search.')(.*?)/iu', '$1<span class="highlight">$2</span>$3', $clientName);
}

输出:&lt;span class="highlight"&gt;céra&lt;/span&gt;

如您所见,我想突出显示特定的搜索字符串。但是,我仍然想显示匹配字符串的原始(重音)字符

我想我必须以某种方式将它与 Michael Sivolobov's answer 结合起来。

我想我必须使用单独的 preg_match()preg_replace(),对吧?

【问题讨论】:

  • é 不是特殊字符,而是重音字母。
  • 我正在尝试匹配特定的字符串,而不仅仅是任何字符串/字符。对困惑感到抱歉。我更新了我的问题。
  • 我也根据@Michael Sivolobov 的建议更新了我的答案。

标签: php regex


【解决方案1】:

您可以使用\p{L} 模式匹配任何字母。

Source

您必须在正则表达式后使用u 修饰符才能启用 unicode 模式。

例如:/\p{L}+/u

编辑:

试试这样的。它应该用重音符号替换包含重音字母(单字符和 unicode 双字符)和非重音字母的搜索模式的每个字母。然后,您可以使用更正后的搜索模式突出显示您的文本。

function mbStringToArray($string)
{
    $strlen = mb_strlen($string);
    while($strlen)
    {
        $array[] = mb_substr($string, 0, 1, "UTF-8");
        $string = mb_substr($string, 1, $strlen, "UTF-8");
        $strlen = mb_strlen($string);
    }
    return $array;
}

// I had to use this ugly function to remove accents as iconv didn't work properly on my test server.
function stripAccents($stripAccents){
    return utf8_encode(strtr(utf8_decode($stripAccents),utf8_decode('àáâãäçèéêëìíîïñòóôõöùúûüýÿÀÁÂÃÄÇÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝ'),'aaaaaceeeeiiiinooooouuuuyyAAAAACEEEEIIIINOOOOOUUUUY'));
}

$clientName = 'céra';

$clientNameNoAccent = stripAccents($clientName);

$clientNameArray = mbStringToArray($clientName);

foreach($clientNameArray as $pos => &$char)
{
    $charNA =$clientNameNoAccent[$pos];
    if($char != $charNA)
    {
        $char = "(?:$char|$charNA|$charNA\p{M})";
    }
}

$clientSearchPattern = implode($clientNameArray); // c(?:é|e|e\p{M})ra

$text = 'the client name is Céra but it could be Cera or céra too.';

$search = preg_replace('/(.*?)(' . $clientSearchPattern . ')(.*?)/iu', '$1<span class="highlight">$2</span>$3', $text);

echo $search; // the client name is <span class="highlight">Céra</span> but it could be <span class="highlight">Cera</span> or <span class="highlight">céra</span> too.

【讨论】:

  • OP 真的要匹配 any 字母吗?或者例如在仅指定e 时匹配é|e
  • @w3d 这个。我想匹配带有和不带有特殊(重音)字符的特定字符串。我更新了我的问题。
  • 我给出的例子将匹配 céra 和 cera。
  • @Kethryweryn 确实,但我正在搜索特定的字符串。就像substr(),如果这有意义的话。
  • @Kethryweryn:是的,但它也会匹配abcd,这是不可取的。据我了解,OP 只想要一种通用/简单的方式来匹配 c[ée]ra,但对于任何单词和任何口音 - 都不是那么简单。
【解决方案2】:

如果您想知道某个字母上是否有重音符号或其他标记,您可以通过匹配模式\p{M} 来检查它

更新

您需要将模式中的所有重音字母转换为一组替代项:

例如céra -&gt; c(?:é|e|e\p{M})ra

为什么我加了e\p{M}?因为您的字母 é 可以是 Unicode 中的一个字符,并且可以是两个字符的组合(e 和重音符号)。 e\p{M} 匹配带有重音符号的 e(两个单独的 Unicode 字符)

当您转换模式以匹配所有字符时,您可以在 preg_match 中使用它

【讨论】:

    【解决方案3】:

    正如您在其中一个 cmets 中标记的那样,您不需要正则表达式,因为目标是查找特定的字符串。为什么不使用explode?像这样:

    $clientName   = 'céra';
    $this->search = 'cera';
    
    $compareClientName = strtolower(iconv('utf-8', 'ascii//TRANSLIT', $clientName));
    $this->search      = strtolower($this->search);
    
    $pieces = explode($compareClientName, $this->search);
    
    if (count($pieces) > 1)
    {
        $clientName = implode('<span class="highlight">'.$clientName.'</span>', $pieces);
    }
    

    编辑:

    如果您的$search 变量也可能包含特殊字符,为什么不使用translit 它,而将mb_strpos$offset 一起使用?像这样:

    $offset = 0;
    $highlighted = '';
    $len = mb_strlen($compareClientName, 'UTF-8');
    while(($pos = mb_strpos($this->search, $compareClientName, $offset, 'UTF-8')) !== -1) {
        $highlighted .= mb_substr($this->search, $offset, $pos-$offset, 'UTF-8').
             '<span class="highlight">'.
             mb_substr($this->search, $pos, $len, 'UTF-8').'</span>';
        $offset = $pos + $len;
    }
    $highlighted .= mb_substr($this->search, $offset, 'UTF-8');
    

    更新 2:

    重要的是使用mb_ 函数而不是简单的strlen 等。这是因为重音字符使用两个或更多字节存储;还要始终确保使用正确的编码,例如:

    echo strlen('é');
    > 2
    
    echo mb_strlen('é');
    > 2
    
    echo mb_internal_encoding();
    > ISO-8859-1
    
    echo mb_strlen('é', 'UTF-8');
    > 1
    
    mb_internal_encoding('UTF-8');
    echo mb_strlen('é');
    > 1
    

    【讨论】:

    • 我认为使用字符串位置是维护原始字符的唯一简单方法。我会调查一下,谢谢!
    • 如果您认为这回答了您的原始问题,请不要犹豫,将其标记为已接受的答案 :)
    • 顺便说一句,我添加了另一个更新,涵盖与多字节字符相关的重要警告
    • 我会回到这个答案。 :) 感谢mb_ 上的信息!
    • 我目前正在使用Kethryweryn's updated answer,因为它的格式更好,更易于阅读/实现。但我也会尝试你的答案,看看哪种方法最有效。
    【解决方案4】:

    如您所见,herePOSIX equivalence class 用于匹配具有相同排序顺序的字符,可以通过以下正则表达式完成:

    [=a=]
    

    这将匹配 áä 以及 a,具体取决于您的语言环境。

    【讨论】:

    • 但是从 PHP 5.3 开始,POSIX 正则表达式语法(以及相关的ereg() 函数)不是已弃用吗?
    • @w3d 是的,但 PCRE 甚至还支持所有 POSIX 命名字符类。
    • 在什么版本的 PHP 中?启用此功能有诀窍吗?我得到:“警告:preg_match() 编译失败:不支持 POSIX 整理元素...”
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-26
    • 2011-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-22
    相关资源
    最近更新 更多