MySQL 正则表达式单词边界 [[:<:]] [[:>:]] 和双引号

22

我正在尝试使用MySQL REGEXP函数匹配一些整词表达式。问题在于当涉及到双引号时会出现问题。

MySQL文档表示:"要在正则表达式中使用特殊字符的文字,请在其前面加上两个反斜杠("\\\\")来转义它。"

但是这些查询语句都返回0:

SELECT '"word"' REGEXP '[[:<:]]"word"[[:>:]]';             -> 0
SELECT '"word"' REGEXP '[[:<:]]\"word\"[[:>:]]';           -> 0
SELECT '"word"' REGEXP '[[:<:]]\\"word\\"[[:>:]]';         -> 0
SELECT '"word"' REGEXP '[[:<:]] word [[:>:]]';             -> 0
SELECT '"word"' REGEXP '[[:<:]][[.".]]word[[.".]][[:>:]]'; -> 0

我还能尝试什么来得到1分吗?或者这是不可能的?


可能涉及字符集,它是否以“单词”形式保存在数据库中? - Mihai
是的,所有可能的设置(我所知道的)都已设置为utf-8... - henk
为了澄清我的意图:我需要一个表达式,例如匹配'"This" is what I need'但不匹配'"This" is what I need today'。我不确定我表达得够清楚吗? - henk
我知道我来晚了,但是考虑使用全文索引如何?它应该可以解决所有这些问题。自MySQL 5.5起,它也支持INNODB表。 - Carlos
4个回答

39

让我先引用文档:

[[:<:]], [[:>:]]

这些标记代表单词边界。它们分别匹配单词的开头和结尾。单词是一系列未被单词字符前后跟随的单词字符。单词字符是 alnum 类中的字母数字字符或下划线(_)。

从文档中我们可以看出您的问题背后的原因,而这并不是由转义引起的。问题在于您试图在字符串开头匹配单词边界[[:<:]],这是行不通的,因为如您从文档中所见,单词边界将单词字符与非单词字符分隔开,但在您的情况下,第一个字符是",它不是一个单词字符,因此没有单词边界,最后一个"[[:>:]]也是同样的情况。

为使其有效,您需要稍微更改表达式以使用以下表达式:

"[[:<:]]word[[:>:]]"
 ^^^^^^^    ^^^^^^^

注意单词边界如何将字符串开头的非单词字符"和单词字符w以及字符串末尾的"d分隔开。

编辑:如果您想始终在不知道是否存在实际边界的情况下在字符串的开头和结尾使用单词边界,则可以使用以下表达式:

([[:<:]]|^)"word"([[:>:]]|$)
这将匹配单词边界的开头或字符串的开头^,同样也适用于单词边界或字符串结尾的结束。我真的建议您研究要匹配的数据并寻找常见模式,如果正则表达式不是正确的工具,则不要使用它们。

SQL Fiddle演示


感谢您的解释,但请继续阅读:“要在正则表达式中使用特殊字符的文字实例,请在其前面加上两个反斜杠(\)字符。MySQL解析器会解释其中一个反斜杠,而正则表达式库会解释另一个反斜杠。例如,要匹配包含特殊+字符的字符串1+2,只有以下正则表达式中的最后一个是正确的。” ->SELECT '1+2' REGEXP '1\+2'; -> 1 但您是对的:它不起作用的位置是短语的开头或结尾,就在边界处... - henk
很遗憾,你的建议对我不起作用,因为我必须匹配更长的表达式,例如REGEXP '[[:<:]]"This is" what I need[[:>:]]'...非单词字符必须能够出现在表达式的任何位置,甚至在开头或结尾。这些字符串在数据库中,所以我对它们没有任何影响力。 - henk
@Heiko 我知道转义,但在你的情况下 " 不被视为特殊字符,因为你正在使用 '' 来定界字符串,正如我发布的演示链接所示。另一方面,我已经稍微修改了答案以涵盖你的第二条评论,请检查编辑。 - Ibrahim Najjar
2
我不认为我是第一个想知道为什么键入mysql的正则表达式边界如此麻烦... 真的吗?一个简单的边界需要7个字符? - frosty

9

4

在MySQL 8及以上版本中

继续沿用Oleksiy Muzalyev的答案

https://dev.mysql.com/doc/refman/8.0/en/regexp.html#regexp-compatibility

在MySQL 8.04及以上版本中,必须使用:

\bword\b

\b代表ICU单词边界的变体。先前的Spencer库使用[[:<:]]代表单词边界。

当将其作为查询的一部分实际使用时,我必须转义转义字符\,使我的查询实际上看起来像

SELECT * FROM table WHERE field RLIKE '\\bterm\\b'

在使用PHP进行查询时,请使用单引号来执行相同的操作。

$sql = 'SELECT * FROM table WHERE field RLIKE ?';
$args = ['\\bterm\\b'];
...

2

您需要更加精细化:

SELECT '"word"' REGEXP '"word"';                                      --> 1
SELECT '"This is" what I need' REGEXP '"This is" what I need[[:>:]]'; --> 1

也就是说,

如果测试字符串以字母开头/结尾,则在字符串之前/之后加上[[:<:]]/[[:>:]]

这与盲目地将它们附加到字符串不同。毕竟,您已经检查了搜索字符串中的特殊正则表达式字符以转义它们。这只是该任务中的另一个任务。 “字母”的定义应与单词边界标记所寻找的内容相匹配。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接