mysql支不支持正则表达式里的零宽断言
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mysql支不支持正则表达式里的零宽断言相关的知识,希望对你有一定的参考价值。
不支持
在mysql中就没有检查约束
你在访问数据库时可以对样例数据用正则表达式验证一下
确保可行再用,不行,就只好想其他办法了
参考资料:《Mysql中使用正则匹配》
使用REGEXP关键字
1、基本字符匹配
From Products
Where prod_name REGEXP'.000'
LIKE和REGEXP区别
LIKE会匹配这个列,而REGEXP会在列值内进行匹配
在MySQL中正则表达式不区分大小写,要区分需使用BINARY关键字,如Where prod_name REGEXP BINARY'Jet.000'
2、进行OR匹配
使用“|”
SELECT prod_nameFrom Products
Where prod_name REGEXP'1000|2000'
3、匹配几个字符之一
指定一组用[和]扩起来的字符
SELECT prod_nameFrom Products
Where prod_name REGEXP'[123]ton'
输出
1ton
2ton
4、匹配范围
[0-9]数字0到9
[a-z]a到z
[A-Z]A到Z
[^0-9] ^表示非,即匹配不是0-9
注意,后面的必须比前面大
SELECT prod_nameFrom Products
Where prod_name REGEXP'[0-9]ton'
5、匹配特殊字符
在特殊字符前加“\\\\”进行转义,注意在一般情况下正则表达式的转义加一个“\\”就可以了,在MySQL中需要加两个
SELECT prod_nameFrom Products
Where prod_name REGEXP'\\\\.000'
输出
1.000ton
6、匹配字符类(Posix字符类)
使用的时候需要外面加一层[],例如[[:digit:]]
类 说明
[:alnum:] 任意字母和数字(同[a-zA-Z0-9])
[:alpha:] 任意字母(同[a-zA-Z])
[:blank:] 空格和制表(同[\\\\t])
[:cntrl:] ASCII控制字符(ASCII0到31和127)
[:digit:] 任意数字(同[0-9])
[:graph:] 和[[:print:]]相同,但不包含空格
[:lower:] 任意小写字母(同[a-z])
[:print:] 任意可打印字符
[:punct:] 即不在[[:alnum:]]又不在[[:cntrl:]]中的字符
[:space:] 包括空格在内的任意空白字符(同[\\\\f\\\\n\\\\r\\\\t\\\\v])
[:upper:] 任意大写字母(同[A-Z])
[:xdigit:] 任意16进制数字(同[a-fA-F0-9])
SELECT * FROM `mytable`Where name REGEXP'name[[:digit:]]';
输出
name1
name6
7、匹配多个实例
元字符 说明
* 0个或多个匹配
+ 1个或多个匹配
? 0个或1个匹配
n 指定数目的匹配
n, 不少于指定数目的匹配
n,m 匹配数目的范围(m不超过255)
SELECT prod_name
From Products
Where prod_name REGEXP'[0-9]1,3'
输出
100
15
8、定位符
元字符 说明
^ 文本的开始
$ 文本的结尾
[[:<:]] 词的开始
[[:>:]] 词的结尾
注意,^有两个用法,一个是非,一个是文本的开始,用[]中表示非,否则是文本的开始。
参考技术AMySQL 一直以来都支持正则匹配,不过对于正则替换则一直到MySQL 8.0 才支持。对于这类场景,以前要么在MySQL端处理,要么把数据拿出来在应用端处理。
比如我想把表y1的列str1的出现第3个action的子 串替换成dble,怎么实现?
1. 自己写SQL层的存储函数。代码如下写死了3个,没有优化,仅仅作为演示,MySQL 里非常不建议写这样的函数。
mysql
DELIMITER $$
USE `ytt`$$
DROP FUNCTION IF EXISTS `func_instr_simple_ytt`$$
CREATE DEFINER=`root`@`localhost` FUNCTION `func_instr_simple_ytt`(
f_str VARCHAR(1000), -- Parameter 1
f_substr VARCHAR(100), -- Parameter 2
f_replace_str varchar(100),
f_times int -- times counter.only support 3.
) RETURNS varchar(1000)
BEGIN
declare v_result varchar(1000) default 'ytt'; -- result.
declare v_substr_len int default 0; -- search string length.
set f_times = 3; -- only support 3.
set v_substr_len = length(f_substr);
select instr(f_str,f_substr) into @p1; -- First real position .
select instr(substr(f_str,@p1+v_substr_len),f_substr) into @p2; Secondary virtual position.
select instr(substr(f_str,@p2+ @p1 +2*v_substr_len - 1),f_substr) into @p3; -- Third virtual position.
if @p1 > 0 && @p2 > 0 && @p3 > 0 then -- Fine.
select
concat(substr(f_str,1,@p1 + @p2 + @p3 + (f_times - 1) * v_substr_len - f_times)
,f_replace_str,
substr(f_str,@p1 + @p2 + @p3 + f_times * v_substr_len-2)) into v_result;
else
set v_result = f_str; -- Never changed.
end if;
-- Purge all session variables.
set @p1 = null;
set @p2 = null;
set @p3 = null;
return v_result;
end;
$$
DELIMITER ;
-- 调用函数来更新:
mysql> update y1 set str1 = func_instr_simple_ytt(str1,'action','dble',3);
Query OK, 20 rows affected (0.12 sec)
Rows matched: 20 Changed: 20 Warnings: 0
2. 导出来用sed之类的工具替换掉在导入,步骤如下:(推荐使用)
1)导出表y1的记录。
mysqlmysql> select * from y1 into outfile '/var/lib/mysql-files/y1.csv';Query OK, 20 rows affected (0.00 sec)
2)用sed替换导出来的数据。
shellroot@ytt-Aspire-V5-471G:/var/lib/mysql-files# sed -i 's/action/dble/3' y1.csv
3)再次导入处理好的数据,完成。
mysql
mysql> truncate y1;
Query OK, 0 rows affected (0.99 sec)
mysql> load data infile '/var/lib/mysql-files/y1.csv' into table y1;
Query OK, 20 rows affected (0.14 sec)
Records: 20 Deleted: 0 Skipped: 0 Warnings: 0
以上两种还是推荐导出来处理好了再重新导入,性能来的高些,而且还不用自己费劲写函数代码。
那MySQL 8.0 对于以上的场景实现就非常简单了,一个函数就搞定了。
mysqlmysql> update y1 set str1 = regexp_replace(str1,'action','dble',1,3) ;Query OK, 20 rows affected (0.13 sec)Rows matched: 20 Changed: 20 Warnings: 0
还有一个regexp_instr 也非常有用,特别是这种特指出现第几次的场景。比如定义 SESSION 变量@a。
mysqlmysql> set @a = 'aa bb cc ee fi lucy 1 1 1 b s 2 3 4 5 2 3 5 561 19 10 10 20 30 10 40';Query OK, 0 rows affected (0.04 sec)
拿到至少两次的数字出现的第二次子串的位置。
mysqlmysql> select regexp_instr(@a,'[:digit:]2,',1,2);+--------------------------------------+| regexp_instr(@a,'[:digit:]2,',1,2) |+--------------------------------------+| 50 |+--------------------------------------+1 row in set (0.00 sec)
那我们在看看对多字节字符支持如何。
mysql
mysql> set @a = '中国 美国 俄罗斯 日本 中国 北京 上海 深圳 广州 北京 上海 武汉 东莞 北京 青岛 北京';
Query OK, 0 rows affected (0.00 sec)
mysql> select regexp_instr(@a,'北京',1,1);
+-------------------------------+
| regexp_instr(@a,'北京',1,1) |
+-------------------------------+
| 17 |
+-------------------------------+
1 row in set (0.00 sec)
mysql> select regexp_instr(@a,'北京',1,2);
+-------------------------------+
| regexp_instr(@a,'北京',1,2) |
+-------------------------------+
| 29 |
+-------------------------------+
1 row in set (0.00 sec)
mysql> select regexp_instr(@a,'北京',1,3);
+-------------------------------+
| regexp_instr(@a,'北京',1,3) |
+-------------------------------+
| 41 |
+-------------------------------+
1 row in set (0.00 sec)
那总结下,这里我提到了 MySQL 8.0 的两个最有用的正则匹配函数 regexp_replace 和 regexp_instr。针对以前类似的场景算是有一个完美的解决方案。
老男孩带你了解perl正则表达式中的零宽断言
1.1 前言
本文只介绍perl语言正则表达式的零宽断言功能。
零宽断言实质:匹配文本里面的位置。
零宽断言叫zero-length assertions,也叫lookaround(这个更容易理解)。
包括:lookahead(向前看,零宽度正预测先行断言),lookbehind(向后看,零宽度正回顾后发断言)。
从左到右对文本进行匹配,判断是否符合exp表达式。
名字 | 表达式 | 如果子表达式成功则… |
positive lookahead (零宽度正预测先行断言) | (?=subexp) | 如果匹配到右边则成功 |
negative lookahead (零宽度负预测先行断言) | (?!subexp) | 如果没有匹配到右边则成功 |
注:成功就是找到对应的位置
1.2 lookahead 向前看-找到匹配内容前面的位置
如图所示:首先这个是lookahead(向前看)他匹配exp这个词(oldboy)之前的一个位置,同时是 positive lookahead是表示匹配到exp就成功的意思。
注:先理解lookahead的含义。然后对比理解positive lookahead和negative lookahead的区别。
1.3 lookbehind 向后看,找到匹配内容的后面的位置
lookbehind(向后看),从右向左匹配,判断是否符合exp表达式。
名字 | 表达式 | 如果子表达式成功则… |
positive lookbehind (零宽度正回顾后发断言) | (?<=subexp) | 如果匹配到左边则成功 |
negative lookbehind (零宽度负回顾后发断言) | (?<!subexp) | 如果没有匹配到左边则成功 |
#注:成功就是找到对应的位置
1.4 lookahead 与lookbehind区别
如图所示lookahead匹配的位置是在subexp之前,lookbehind匹配的位置是在subexp之后。
1.5 perl常用的转义字符
转义字符 | 含义 |
\b | 单词的边界。 |
\w | 表示[a-zA-Z0-9_]中任意一个字符 |
\W | 与\w相反表示不在[a-zA-Z0-9_]中的任意字符 |
\d | [0-9]中的一个数字 |
\D | 不在[0-9]中的任意字符 |
1.6 案例1:实战演示:取IP地址
基本格式,红色就是我们的目标。
[[email protected] oldboy]# ifconfig eth0 eth0 Link encap:Ethernet HWaddr 00:0C:29:3A:C5:88 inet addr:192.168.85.50 Bcast:192.168.85.255 Mask:255.255.255.0 inet6 addr: fe80::20c:29ff:fe3a:c588/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:81923 errors:0 dropped:0 overruns:0 frame:0 TX packets:26480 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:96099065 (91.6 MiB) TX bytes:3459424 (3.2 MiB) |
方法一:使用positive lookahead(正常向前看,零宽度正预测先行断言)
[[email protected] oldboy]# ifconfig eth0 | grep -oP "[0-9.]+(?= Bcast)" 192.168.85.50 [[email protected] oldboy]# ifconfig eth0 | grep -oP "[\d.]+(?= Bcast)" 192.168.85.50 |
说明:
1.这里使用lookahead的时候要注意,Bcast前面有两个空格,匹配时要注意他们。
2.前面已经说过,\d è[0-9]
方法二:使用positive lookbehind(正常向后看,零宽度正回顾后发断言)
[[email protected] oldboy]# ifconfig eth0 | grep -oP ‘(?<=\baddr:)[\d.]+‘ 192.168.85.50 |
方法三:使用零宽度负预测先行断言
[[email protected] oldboy]# ifconfig eth0|grep -oP ‘addr:[\d\.]+(?![\d\.])‘ addr:192.168.85.50 |
但是去掉"addr:"还是需要使用lookbehind(向后看,零宽度正回顾后发断言)
[[email protected] oldboy]# ifconfig eth0|grep -oP ‘(?<=addr:)[\d\.]+(?![\d\.])‘ 192.168.85.50 |
方法四:使用零宽度负向回顾后发断言
[[email protected] oldboy]# ifconfig eth0|grep -oP ‘(?<![\d\.])[\d\.]+(?) Bca‘ 192.168.85.50 Bca |
但是后面的去除需要使用使用零宽度正预测先行断言
[[email protected] oldboy]# ifconfig eth0|grep -oP ‘(?<![\d\.])[\d\.]+(?)(?= Bca)‘ 192.168.85.50 |
更多精彩关注老男孩linux培训
本文出自 “老男孩linux培训” 博客,请务必保留此出处http://oldboy.blog.51cto.com/2561410/1931019
以上是关于mysql支不支持正则表达式里的零宽断言的主要内容,如果未能解决你的问题,请参考以下文章