正则表达式 - 选择、分组和向后引用
目录
一、选择操作
1. 选项和修饰符
2. 统计单词出现的行数
二、子模式
三、捕获分组和后向引用
命名分组
四、非捕获分组
1. 原子分组
2. 回溯
(1)量词导致回溯
(2)分支导致回溯
(3)回溯与性能
参考:
一、选择操作
简单地说,选择操作可在多个可选模式中匹配一个。例如想找出 the 出现过多少次,包括THE、The 和 the 等形式。为此就可以使用选择操作 (the|The|THE) :
mysql> select regexp_like('the','(the|The|THE)');
+------------------------------------+
| regexp_like('the','(the|The|THE)') |
+------------------------------------+
| 1 |
+------------------------------------+
1 row in set (0.00 sec)
1. 选项和修饰符
可以使用一个选项来使分组更简短。借助选项,可以指定查找模式的方式。例如 (?i) 选项让模式不再区分大小写,因此原来带选择操作的模式可以简写成 (?i)the :
mysql> select regexp_like('THE','(?i)the');
+------------------------------+
| regexp_like('THE','(?i)the') |
+------------------------------+
| 1 |
+------------------------------+
1 row in set (0.00 sec)
此时即便使用 regexp_like 函数的选项 c 也不会起到区分大小写的效果,因为正则表达式中的 (?i) 会匹配所有大小写组合。
mysql> select regexp_like('THE','the'),regexp_like('THE','the','c'),regexp_like('THE','(?i)the','c')\G
*************************** 1. row ***************************regexp_like('THE','the'): 1regexp_like('THE','the','c'): 0
regexp_like('THE','(?i)the','c'): 1
1 row in set (0.00 sec)
下表中列出了各种选项和修饰符,MySQL支持除 (?J) 和 (?U) 以外的其它选项。
选项 |
描述 |
支持平台 |
(?d) |
Unix中的行 |
Java |
(?i) |
不区分大小写 |
PCRE、Perl、Jave |
(?J) |
允许重复的名字 |
PCRE* |
(?m) |
多行 |
PCRE、Perl、Java |
(?s) |
单行(dotall) |
PCRE、Perl、Java |
(?u) |
Unicode |
Java |
(?U) |
默认最短匹配 |
PCRE |
(?x) |
忽略空格和注释 |
PCRE、Perl、Java |
(?-…) |
复原或关闭选项 |
PCRE |
*参见http://www.pcre.org/pcre.txt中的“Named Subpatterns”(命名子模式)。
2. 统计单词出现的行数
要对单词 the 出现一次或多次的行的数目进行统计,且不区分大小写。为了更切合数据库的实际应用场景,先给原表增加一个ID主键列。
alter table t_regexp add id int auto_increment primary key first;
统计单词出现的行数的需求实现如下。
select id,sum(regexp_like(b,'\\bthe\\b(?i)'))from (
select id,substring_index(substring_index(a,char(10),lv),char(10),-1) b, lvfrom t_regexp,
(with recursive tab1(lv) as (select 1 lv union all select t1.lv + 1 from tab1 t1 where lv < 20 )select lv from tab1) t where t.lv <= (length(a) - length(replace(a,char(10),''))) + 1) tgroup by id;
MySQL正则表达式虽然提供了“多行模式”,但只能用于匹配时的比较,并未提供按行统计的接口。对于数据库来说,\n 只是一个普通字符,整个字符串还是一行。因此要实现 \n 分割的多行统计,需要将单行拆分为多行,再进行后续的匹配和汇总求和。按固定分隔符将一行分成多行的经典方法,就是先使用笛卡尔积连接制造出行,再用两个嵌套的substring_index函数取子串。MySQL 8提供了递归查询,可以轻松构造序列表,以进行笛卡尔积连接。
最内层查询构造出1-20的数字序列表,其中的20是变量,为预估的最大 \n 个数:
mysql> with recursive tab1(lv) as (-> select 1 lv union all select t1.lv + 1 from tab1 t1 where lv < 20 )-> select lv from tab1;
+------+
| lv |
+------+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
| 10 |
| 11 |
| 12 |
| 13 |
| 14 |
| 15 |
| 16 |
| 17 |
| 18 |
| 19 |
| 20 |
+------+
20 rows in set (0.00 sec)
中间层的查询,将一行按换行符 \n 分割成多行;
mysql> select id,substring_index(substring_index(a,char(10),lv),char(10),-1) b, lv-> from t_regexp,-> (with recursive tab1(lv) as (-> select 1 lv union all select t1.lv + 1 from tab1 t1 where lv < 20 )-> select lv from tab1) t -> where t.lv <= (length(a) - length(replace(a,char(10),''))) + 1;
+----+---------------------------------------------------------------------------------+------+
| id | b | lv |
+----+---------------------------------------------------------------------------------+------+
| 1 | THE RIME OF THE ANCYENT MARINERE, IN SEVEN PARTS. | 1 |
| 1 | ARGUMENT. | 2 |
| 1 | How a Ship having passed the Line was driven by Storms to the cold Country | 3 |
| 1 | towards the South Pole; and how from thence she made her course to the tropical | 4 |
| 1 | Latitude of the Great Pacific Ocean; and of the strange things that befell; | 5 |
| 1 | and in what manner the Ancyent Marinere came back to his own Country. | 6 |
| 1 | I. | 7 |
| 1 | 1 It is an ancyent Marinere, | 8 |
| 1 | 2 And he stoppeth one of three: | 9 |
| 1 | 3 "By thy long grey beard and thy glittering eye | 10 |
| 1 | 4 "Now wherefore stoppest me? | 11 |
| 2 | THE RIME OF THE ANCYENT MARINERE, IN SEVEN PARTS. | 1 |
| 3 | ARGUMENT. | 1 |
| 4 | How a Ship having passed the Line was driven by Storms to the cold Country | 1 |
| 5 | towards the South Pole; and how from thence she made her course to the tropical | 1 |
| 6 | Latitude of the Great Pacific Ocean; and of the strange things that befell; | 1 |
| 7 | and in what manner the Ancyent Marinere came back to his own Country. | 1 |
| 8 | I. | 1 |
| 9 | 1 It is an ancyent Marinere, | 1 |
| 10 | 2 And he stoppeth one of three: | 1 |
| 11 | 3 "By thy long grey beard and thy glittering eye | 1 |
| 12 | 4 "Now wherefore stoppest me? | 1 |
+----+---------------------------------------------------------------------------------+------+
22 rows in set (0.00 sec)
最外层按行ID分组求和,统计每数据库行中,单词 the 出现的以 \n 分割的行数:
mysql> select id,sum(regexp_like(b,'\\bthe\\b(?i)'))-> from (-> select id,substring_index(substring_index(a,char(10),lv),char(10),-1) b, lv-> from t_regexp,-> (with recursive tab1(lv) as (-> select 1 lv union all select t1.lv + 1 from tab1 t1 where lv < 20 )-> select lv from tab1) t -> where t.lv <= (length(a) - length(replace(a,char(10),''))) + 1) t-> group by id;
+----+-------------------------------------+
| id | sum(regexp_like(b,'\\bthe\\b(?i)')) |
+----+-------------------------------------+
| 1 | 5 |
| 2 | 1 |
| 3 | 0 |
| 4 | 1 |
| 5 | 1 |
| 6 | 1 |
| 7 | 1 |
| 8 | 0 |
| 9 | 0 |
| 10 | 0 |
| 11 | 0 |
| 12 | 0 |
+----+-------------------------------------+
12 rows in set (0.00 sec)
二、子模式
多数情况下,提到正则表达式中的子模式(subpattern),就是指分组中的一个或多个分组。子模式就是模式中的模式。多数情况下,子模式中的条件能得到匹配的前提是前面的模式得到匹配,但也有例外。子模式的写法可以有很多种,这里我们主要关注括号中的子模式,如之前所见的模式 (the|The|THE) 有三个子模式:the 是第一个子模式,The 是第二个,而 THE 是第三个。但是这种情况下,匹配第二个子模式不依赖于是否匹配第一个。(最左边的模式会首先匹配。)而在以下的模式中,子模式依赖于前面的模式:
(t|T)h(e|eir)
这个模式会匹配字面值 t 或 T,然后是一个 h,接下来就是一个 e 或者是 eir。相应地,这个模式会匹配以下四种情况:the、The、their、Their。
在以上情况中,第二个子模式 (e|eir) 依赖于第一个子模式 (t|T)。括号对于子模式不是必需的。下面是一个使用字符组的子模式示例:
\b[tT]h[ceinry]*\b
这个模式会匹配 the 或 The 还有th、thee、thy 以及 thence 等单词。两个单词边界(\b)表示该模式只匹配整个单词,而不会匹配单词中的某几个字母。
mysql> set @r:='\\b[tT]h[ceinry]*\\b';
Query OK, 0 rows affected (0.00 sec)mysql> select regexp_like('th',@r),regexp_like('thee',@r),regexp_like('thy',@r),regexp_like('thence',@r);
+----------------------+------------------------+-----------------------+--------------------------+
| regexp_like('th',@r) | regexp_like('thee',@r) | regexp_like('thy',@r) | regexp_like('thence',@r) |
+----------------------+------------------------+-----------------------+--------------------------+
| 1 | 1 | 1 | 1 |
+----------------------+------------------------+-----------------------+--------------------------+
1 row in set (0.00 sec)
以下是对这个模式的解析。
- \b匹配单词起始边界。
- [tT] 是字符组,它匹配小写字母 t 或者大写字母 T。可以将其看做是第一个子模式。
- 然后匹配(或尝试匹配)小写字母 h。
- 第二个也就是最后一个子模式也表示为字符组 [ceinry],其后用量词 * 表示零个或多个。
- 最后,该模式以另外一个 \b 结束。
正则表达式的术语经常是含义相近但使用范围迥异,也有人认为字符组不是子模式。这里的观点是它们与子模式起到的作用一样,所以两者可以归为一类。
三、捕获分组和后向引用
当一个模式的全部或者部分内容由一对括号分组时,它就对内容进行捕获并临时存储于内存中。可以通过后向引用引用捕获的内容,形式为 \1 或 $1 。这里 \1 或 $1 引用的是第一个捕获的分组,而 \2 或 $2 引用第二个捕获的分组,以此类推。MySQL只接受 $1 这种形式。
下面再展示一下后向引用的使用方法。我们将使用它来重新排序诗文中的一行词。
mysql> select regexp_replace('It is an ancyent Marinere','(It is) (an ancyent Marinere)','\\2 \\1') c1,-> regexp_replace('It is an ancyent Marinere','(It is) (an ancyent Marinere)','$2 $1') c2;
+------+---------------------------+
| c1 | c2 |
+------+---------------------------+
| 2 1 | an ancyent Marinere It is |
+------+---------------------------+
1 row in set (0.00 sec)
使用 regexp_replace 正则替换函数,将原文分成两个捕获分组用作正则表达式,替换字符串引用两个捕获分组并颠倒顺序。要用sed得到相同结果,可以这样做
sed -En 's/(It is) (an ancyent Marinere)/\2 \1/p' rime.txt
输出为:
1 an ancyent Marinere It is,
下面我们来分析一下这个 sed 命令。
- -E选项仍然是调用ERE(扩展的正则表达式),因此,括号可以直接当成字面值来使用了。
- -n选项覆盖打印每一行的默认设置。
- 替换命令搜索与文本“It is an ancyent Marinere”匹配的内容,再将其捕获放入两个分组中。
- 替换命令还将捕获的文本重排为先是后向引用 \2 的内容再是 \1 的内容,再将匹配的文本替换为重排后的内容并输出。
- 替换命令结尾处的 p 表示要打印该行。
命名分组
命名分组(named group)就是有名字的分组。这样,就可以通过名字(而不是数字)来引用分组。下面展示一下MySQL中如何使用命名分组:
mysql> select regexp_replace('It is an ancyent Marinere','(?<one>It is) (?<two>an ancyent Marinere)','${two} ${one}') c2;
+---------------------------+
| c2 |
+---------------------------+
| an ancyent Marinere It is |
+---------------------------+
1 row in set (0.00 sec)
在括号内添加 ?<one> 和 ?<two> 将分组分别命名为 one 和 two ;${one} 引用名为 one 的分组,而 ${two} 则引用名为 two 的分组。如果在一个模式中有分组被命名,那么还可以重用该命名分组。这里说的是“重用”而不是前面所说的“引用”,简单说重用指的是在一个正则表达式中的引用。解释一下这句话的意思,假设要查找含有连续六个0的字符串:
mysql> select regexp_like('000000','(?<z>0{3})\\k<z>');
+------------------------------------------+
| regexp_like('000000','(?<z>0{3})\\k<z>') |
+------------------------------------------+
| 1 |
+------------------------------------------+
1 row in set (0.00 sec)
用 (?<z>0{3}) 对连续三个0的分组命名为 z ,然后可以用 \k<z> 再次使用该分组,MySQL只支持 \k<分组名> 这一种重用捕获分组的语法。在一个正则表达式中不能使用 ${分组名} 进行引用。
mysql> select regexp_like('000000','(?<z>0{3})${z}');
ERROR 3692 (HY000): Incorrect description of a {min,max} interval.
四、非捕获分组
还有一种分组是非捕获分组(Non-Capturing Group)。非捕获分组不会将其内容存储在内存中,即非捕获分组根本不保存匹配项的值。在并不想引用分组的时候,可以使用它。由于不存储内容,非捕获分组就会带来较高的性能。非捕获分组的语法是在分组中加上 ?: 前缀。
mysql> select regexp_like('the','(?:the|The|THE)'), -> regexp_like('the','(?i)(?:the)'),-> regexp_like('the','(?:(?i)the)'),-> regexp_like('the','(?i:the)')\G
*************************** 1. row ***************************
regexp_like('the','(?:the|The|THE)'): 1regexp_like('the','(?i)(?:the)'): 1regexp_like('the','(?:(?i)the)'): 1regexp_like('the','(?i:the)'): 1
1 row in set (0.00 sec)
上面语句中四个 regexp_like 中的正则表达式等价,都是匹配不区分大小写的单词 the 。注意最后一种写法,选项 i 可以放在问号和冒号之间。
1. 原子分组
另一种非捕获分组是原子分组(atomic group),原子分组禁用回溯。如果使用的正则表达式引擎进行回溯操作,这种分组就可以将回溯操作关闭,但它只针对原子分组内的部分,而不针对整个正则表达式。其语法为 (?>the):
mysql> select regexp_like('THE','(?>the)');
+------------------------------+
| regexp_like('THE','(?>the)') |
+------------------------------+
| 1 |
+------------------------------+
1 row in set (0.00 sec)
2. 回溯
正则表达式匹配目标字符串时,它从左到右逐个测试表达式的组成部分,看是否能找到匹配项。在遇到量词时,需要决定何时尝试匹配更多字符。在遇到分支时,必须从可选项中选择一个尝试匹配。每当正则做类似的决定时,如果有必要,都会记录其他选择,以便匹配不成功时进行回溯,到最后一个决策点,再重新进行匹配。
(1)量词导致回溯
考虑正则表达式 ab?c 匹配字符串 ac。
- 首先从 a 开始,匹配到了,跳到 a 后面的位置(即一个零宽断言的位置)。
- 然后字符 b 后面有一个量词修饰符 ?,代表0或1次,匹配 b,没有出现 b 的情况先记录下来,放到备用状态里面。如果后面的匹配不成功,那么就回溯,到备用状态里面选择一个重新匹配。现在我们可以暂时认为这个量词要匹配,那么便是出现 1 次,此时的正则其实相当于 abc,b和c不一样,匹配失败。
- 注意,现在就要回溯了,到备用状态里面拿出来,此时正则表达式相当于ac,匹配ac,c和c一样,匹配成功。
mysql> select regexp_like('ac','ab?c');
+--------------------------+
| regexp_like('ac','ab?c') |
+--------------------------+
| 1 |
+--------------------------+
1 row in set (0.00 sec)
使用原子分组时,如果完整的正则表达式与给定的字符串不匹配,则正则表达式引擎不会回退进一步的排列。还以本例来分析。
- 首先从 a 开始,匹配到了,跳到 a 后面的位置(即一个零宽断言的位置)。
- 然后字符 b 后面有一个量词修饰符 ?,代表0或1次,匹配 b,如果后面的匹配不成功,则匹配失败。现在我们可以认为这个量词要匹配,那么便是出现 1 次,此时的正则其实相当于 abc,b和c不一样,匹配失败。
mysql> select regexp_like('ac','ab?>c');
+---------------------------+
| regexp_like('ac','ab?>c') |
+---------------------------+
| 0 |
+---------------------------+
1 row in set (0.00 sec)
(2)分支导致回溯
考虑正则表达式 a(bc|b)c 匹配字符串 abc。
- 首先从 a 开始,匹配到了,跳到 a 后面的位置(即一个零宽断言的位置)。
- 继续走遇到了分支,先用左边的(分支选择是从左到右),abbc 与 abc 匹配失败。
- 要回溯,从开始的那个 a 后面的位置,abc 与 abc 匹配成功。
如果使用原子分组:
- 首先从 a 开始,匹配到了,跳到 a 后面的位置(即一个零宽断言的位置)。
- 继续走遇到了分支,先用左边的(分支选择是从左到右),abbc 与 abc 匹配失败,则整个匹配失败,不再回溯其他分支。
mysql> select regexp_like('abc','a(bc|b)c'),-> regexp_like('abc','a(?:bc|b)c'),-> regexp_like('abc','a(?:b|bc)c'),-> regexp_like('abc','a(?>bc|b)c'),-> regexp_like('abc','a(?>b|bc)c')\G
*************************** 1. row ***************************regexp_like('abc','a(bc|b)c'): 1
regexp_like('abc','a(?:bc|b)c'): 1
regexp_like('abc','a(?:b|bc)c'): 1
regexp_like('abc','a(?>bc|b)c'): 0
regexp_like('abc','a(?>b|bc)c'): 1
1 row in set (0.00 sec)
当使用分支(也叫替换)时,如果匹配成功,则正则表达式将立即尝试匹配表达式的其余部分,但会跟踪可能进行其他替换的位置。如果表达式的其余部分不匹配,则正则表达式将返回到先前记录的位置并尝试其他组合。如果使用了原子分组,则正则表达式引擎将不会跟踪先前的位置,而只会放弃匹配。
(3)回溯与性能
上面的示例清楚地表明原子分组消除了回溯,并可能改变匹配的结果,但并没有真正说明使用原子分组的目的。什么时候会想使用原子分组呢?正则表达式处理过程缓慢的一个因素就是回溯操作。其原因就是回溯操作会尝试每一种可能性,这会消耗时间和计算资源,有时它会占用大量时间。回溯有可能产生巨大的负面效应,这被称为灾难性回溯。来看下面的例子。
mysql> select regexp_like('11ab11111111111111111111111','(a(.+)+b)');
ERROR 3699 (HY000): Timeout exceeded in regular expression match.
查询直接报错退出。MySQL 8用两个系统变量控制匹配引擎的资源消耗。regexp_stack_limit 控制匹配使用的最大内存,缺省为 8000000 字节。regexp_time_limit 控制引擎执行的最大步骤数,缺省为 32。因为这个限制是用步骤数表示的,所以它只会间接影响执行时间。通常匹配引擎执行时间的数量级为毫秒。
mysql> show variables like 'regexp%';
+--------------------+---------+
| Variable_name | Value |
+--------------------+---------+
| regexp_stack_limit | 8000000 |
| regexp_time_limit | 32 |
+--------------------+---------+
2 rows in set (0.00 sec)
在贪婪量词模式下,正则表达式会尽可能长地去匹配符合规则的字符串,且会回溯。这种情况下原子分组会显著提高查询性能。
mysql> select regexp_like('11ab11111111111111111111111','(a(.+)+b)');
+----------------------------------------------------------+
| regexp_like('11ab11111111111111111111111','(a(?>.+)+b)') |
+----------------------------------------------------------+
| 0 |
+----------------------------------------------------------+
1 row in set (0.00 sec)
参考:
https://www.codenong.com/6488944/
https://zhuanlan.zhihu.com/p/52889281
https://dev.mysql.com/blog-archive/new-regular-expression-functions-in-mysql-8-0/
https://stackoverflow.com/questions/56858176/mysql-error-3699-timeout-exceeded-in-regular-expression-match/58404086
https://www.regular-expressions.info/atomic.html
https://learnbyexample.github.io/python-regex-possessive-quantifier/
正则表达式 - 选择、分组和向后引用相关推荐
- 【C#进阶二】C#中的正则表达式知识总结(字符转义/字符类/ 定位点/ 分组构造 /数量词/反向引用构造/替换构造/替代/正则表达式选项)(理论篇)
文章目录 0. 正则表达式网站推荐 1.字符转义 2.字符类 3. 定位点 4. 分组构造 5.数量词 6.反向引用构造 7.替换构造 8.替代 9.正则表达式选项 正则表达式是对字符串操作的一种逻辑 ...
- Python 正则表达式(分组)
正则表达式分组 分组就是用一对圆括号"()"括起来的正则表达式,匹配出的内容就表示一个分组.从正则表达式的左边开始看,看到的第一个左括号"("表示第一个分组,第 ...
- 正则表达式的捕获性分组/反向引用
文章目录 分组 捕获性分组和反向引用 分组 正则的分组主要通过小括号来实现,括号包裹的子表达式作为一个分组,括号后可以紧跟限定词表示重复次数.如下,小括号内包裹的 abc 便是一个分组: // (ab ...
- python中分割字符串两种方法正则分组别名_Python 正则表达式(分组)
正则表达式分组 分组就是用一对圆括号"()"括起来的正则表达式,匹配出的内容就表示一个分组.从正则表达式的左边开始看,看到的第一个左括号"("表示第一个分组,第 ...
- 1.19.7.Table API、SQL、数据类型、保留关键字、查询语句、指定查询、执行查询、语法、操作符、无排名输出优化、去重、分组窗口、时间属性、选择分组窗口的开始和结束时间戳、模式匹配
1.19.7.Table API 1.19.8.SQL 1.19.8.1.概述 1.19.8.1.1.SQL 1.19.8.1.2.数据类型 1.19.8.1.3.保留关键字 1.19.8.2.查询语 ...
- JS正则表达式的分组匹配
原文地址: JS正则表达式的分组匹配 什么是分组 通俗来说,我理解的分组就是在正则表达式中用()包起来的内容代表了一个分组,像这样的: var reg = /(\d{2})/ reg.test('12 ...
- Python中的正则表达式(分组)
再接下来,就是正则表达式的分组了!!! 使用的还是最基础的字符串 '\d(.)(\d)':代表匹配的是两个数字之前加入一个任意类型的字符 当使用findall进行匹配的时候,只输出括号括出的字符,结果 ...
- dnf正在连接服务器然后选择角色卡,dnf选择角色卡死(选择角色进入游戏后卡住的解决方法)...
dnf选择角色卡死(选择角色进入游戏后卡住的解决方法) 我永远喜欢DNF 原创首发,禁止任何百思特网平台和个人以任何方式抄袭,违者必究 无论你是喜欢正经的职业百科还是搞笑的DNF趣事,点点关注不迷路 ...
- ESL3.3 子集的选择学习笔记(最优集选择,向前向后逐步选择等方法)
3.3 子集的选择 这是一篇有关<统计学习基础>,原书名The Elements of Statistical Learning的学习笔记,该书学习难度较高,有很棒的学者将其翻译成中文并放 ...
最新文章
- 如何搭建python框架_从零开始:写一个简单的Python框架
- Php开发官方IDE ZEND
- 有关接口 笔记 懒人版
- UA OPTI570 量子力学 公式与结论总结1 角动量基础
- linux yum安装mysql5.7_Linux安装MySQL5.7通过yum安装轻松搞定
- 部署DNS服务和管理DNS
- 网易创新企业大会倒计时2天 《企业数字化升级之路》白皮书将发布
- 每天接触大量论文,看看他们是怎样写笔记的 | PaperDaily #09
- VTK:Math之NormalizeVector
- python变量和常量_5、python的变量和常量
- 用户旅程图进阶:实操与模板
- 星淘惠:我国在国际大变革中迎来国际贸易的发展黄金时期
- 【原创】将RGB图像转换到CMY空间
- 为什么我星际争霸画面是窗口_为什么我喜欢看全球星际联盟
- 遥控汽车的编程c语言,智能小车循迹、避障、红外遥控C语言代码.docx
- 获取本电脑外网IP和内网IP
- 创建pv卷报错excluded by a filter的解决办法
- 混淆矩阵、召回率、精确率、正确率、F1、真阳性率、假阳性率、ROC、AUC
- U盘做为系统盘安装系统,出现start booting from usb device和boot failed解决方案
- 共享新风机未来家居生活必备品新鲜空气齐分享
热门文章
- c语言|||博客作业03
- STL容器-Map and Pair(一)
- MacBook pro电脑制作系统u盘
- Windows操作快捷键大全
- CSS浮动(float,clear)通俗讲解- 杨元- 博客园
- VS 出现错误“MSB3644 找不到 .NETFramework,Version=v4.6.X 的引用程序集。”的解决方案
- FIO入门到删盘 -- 恢复磁盘数据
- H5测试点总结-UI测试、功能测试、兼容性测试、体验相关(弱网、资源、手机操作等)、安全性测试、性能测试...
- Unity Ecs源码分析
- 如何利用python的opencv,在图片上添加一个有背景颜色的文本框,并在文本框内添加文字?