断言

本文目的是讲解正则表达式之断言用法。目前互联网上有很多博文对断言讲解的并不透彻,如果您刚开始学习断言,相信此博文会对您有帮助。

本文谢绝转载,此文属原创文章,如有雷同,不胜荣幸。

https://cloud.tencent.com/developer/article/1149481

https://www.aqee.net/post/regular-expression-to-match-string-not-containing-a-word.html

1.2.3.1 情景导入

假设,我要获得一个字符串里面所有以空格开头的英文词语。

//检测由空格开始的词汇
Pattern pattern4 = Pattern.compile("\\s(?i)\\w+");
Matcher matcher3 = pattern4.matcher(" hello world");
while (matcher3.find()) {
String group = matcher3.group();
//可以用下面这种方法去除空格
System.out.println("group.trim() = " + group.trim());
// group.trim() = hello
// group.trim() = world
}

在使用上面的代码过程中存在一些问题,我获得的单词默认是前头带空格的,纵然我可以通过String类的trim来解决这些问题,可是这样用起来会很麻烦,有没有一种正则表达式的规则可以检测那些开头带空格的词汇但是适配到的词汇不带空格呢?答案是有的,那就是断言

什么是断言

在使用正则表达式时,有时我们需要捕获的内容前后必须是特定内容,但又不捕获这些特定内容的时候,零宽断言就起到作用了。

断言全称为零宽断言

断言的语法规则

(?=X) X, via zero-width positive lookahead 零宽前行正向断言
(?!X) X, via zero-width negative lookahead 零宽前行负向断言
(?<=X) X, via zero-width positive lookbehind 零宽后行正向断言
(?<!X) X, via zero-width negative lookbehind 零宽后行负向断言

零宽断言为什么叫零宽断言

理解这一点至关重要,拿零宽前行正向断言来举例

零宽

零宽的含义为断言部分不占据宽度,举个例子12(?=la)匹配12la匹配到的是12,含义是la前面的12。12la匹配12la匹配到的是12la,含义为匹配value为12la的字符串。不知道通过这个例子你能否看懂零宽的意义,不懂也没关系,看完接下来几章就会懂了。

前行

前行的含义我可以确定我说的是正确的,现在互联网上好多人对前行的理解是错误的,虽然按照他们的解释是可以解释的通大多数情况,但是我们还是要抱着实事求是的态度,还原前行真正的含义,参考网站:正则表达式零宽断言详解(?=,?<=,?!,?<!),Regular expression to match a line that doesn't contain a word

在正则表达式中,每个字符串都是有定位的,所谓定位,就是确定字符串中每个字符位置的数字,这个只可意会,我给您看几张图,通过图片,或许可以理解

看到没有,字符串每一个字符间隔就是一个个位置点,字符串第一个字符前为位置0,字符串第一个字符和第二个字符之间为位置1,字符串第一个字符位于位置0和位置1之间,以此类推……

前行的意义为位置之前,后行的意义为位置之后

如上图,位置5位于红色区域后行,位置5位于黄色区域前行

那么,在断言中前行和后行代表着什么呢?

比如说正则表达式12(?=la)在匹配字符串qwew12bb12la的时候,他会先,匹配到从左往右第一个12,如下图所示

因为(?=x)是前行匹配,这个时候12是占位置的,位置点走到了第一个12的后面,即位置6,。因为是零宽,(?=la)不占位置,接下来以位置6为前行,匹配位置6的后面,位置6的后面不是la,故不匹配位置6前面的12,接下来位置转到了第二个12后面,如下图所示

因为是前行匹配,位置10作为前行,匹配后面的字符,后面是la符合要求,因为零宽,匹配到的最终字符不包括零宽,如下图所示

不懂也没关系,看完接下来几章就会懂了。

负向

所谓负向,不符合为负向,如(?<!x)就是负向,该断言匹配的是不符合x标准的字符串作为目标字符串的后行

再来熟悉一下几个断言

(?=X) X, via zero-width positive lookahead 零宽前行正向断言
(?!X) X, via zero-width negative lookahead 零宽前行负向断言
(?<=X) X, via zero-width positive lookbehind 零宽后行正向断言
(?<!X) X, via zero-width negative lookbehind 零宽后行负向断言

断言DEMO

  • 后行正向断言,该断言匹配前面是aa的字符

    //后行正向断言
    Pattern compile = Pattern.compile("(?<=aa)\\w+");
    Matcher matcher = compile.matcher(" aadfds fdgs daafs");
    while (matcher.find()) {
    System.out.println(matcher.group());//dfds
    }

    运行结果为

    dfds
    fs
  • 后行负向断言,该断言匹配的是前面不是$的数字

    Pattern compile1 = Pattern.compile("(?<!\\$|\\d)\\d+");
    Matcher matcher1 = compile1.matcher("$14 23");//23
    while (matcher1.find()) {
    System.out.println("matcher1.group() = " + matcher1.group());
    }

    运行结果为

    matcher1.group() = 23

    我来解释一下,正则表达式(?<!\\$|\\d)\\d+,断言的意思是不匹配前面为$或数字的数字。

    模拟一下匹配的过程,也许会更加明白是怎么一回事儿,先说一下"$14 23",位置我就不在图中标了,自己数一下吧

    (?<!\\$|\\d)\\d+先匹配数字,匹配到了1,位置是1,因为断言是不占宽度的。因为是后行,检测到左边是\(,不符合断言要求,就来到了位置2(1和4之间),位置二前面是数字不符合要求,类推,位置来到了2的前面,前面是空格,既不是数字也不是\),符合要求,故匹配23

  • 先行正向断言

    Matcher matcher2 = Pattern.compile("(?i)windows(?=2000|xp|10)").matcher("windowsXp");
    while (matcher2.find()) {
    System.out.println("matcher2.group() = " + matcher2.group());
    }

    运行结果是

     matcher2.group() = windows

    我来稍微解释一下,"(?i)windows(?=2000|xp|10)" (?i)表明不区分大小写,Windows表明适配windows,(?=2000|xp|10)表明是零宽先行正向断言。我说一下匹配的过程,首先匹配windows,位置来到了windows后,零宽先行,后面是xp适配成功。

  • 先行负向断言

    Matcher matcher3 = Pattern.compile("(?i)Linux(?!12.3)").matcher("linux12.");
    while (matcher3.find()) {
    System.out.println("matcher3.group() = " + matcher3.group());
    }

    运行结果是

    matcher3.group() = linux

    这个就不解释了,自行理解吧

    以上四个demo是最最基础的demo,在很多教程上对断言的讲解止步于此(实际上,很短教程连最基本的匹配过程都没讲,只讲了匹配的结果,这个要出大问题的),务须掌握好上面的四个demo。

断言的基础应用和实际用处

放一张思维导图

这个我先说一下结论,在实际应用开发中,在应用诸如验证开头或结尾(只要求一行的开头或结尾)的实践中,大概有两种,一种是零宽,一种是非零宽。所谓零宽即匹配的数据不包括断言,非零宽反之,在某些情况下需要借助String方法的subString()方法。

一般情况下,使用断言,主要是用它零宽的属性,验证开头包含,结尾包含时一般不用断言,分组就能解决问题,当开头不包含或结尾不包含等问题时,使用断言是很好的办法。

演示三个

验证不包含

/*
不包含
*/
{
// 不包含
// regex用法
System.out.println("验证不包含");
Pattern compile = Pattern.compile("^((?!123).)*$");
Matcher matcherRight = compile.matcher("qwe123wew333");
Matcher matcherError = compile.matcher("wqeqr12eqr34512");
while (matcherRight.find()) {
//验证包含123的字符串
System.out.println("matcherRight.group() = " + matcherRight.group());
}
while (matcherError.find()) {
//验证不包含123的字符串
System.out.println("matcherError.group() = " + matcherError.group());//matcherError.group() = wqeqr12eqr34512
}
//使用String方法
System.out.println("!\"wqer12335\".contains(\"123\") = " + !"wqer12335".contains("123"));//!"wqer12335".contains("123") = false
}

运行结果如下

验证不包含
matcherError.group() = wqeqr12eqr34512
!"wqer12335".contains("123") = false

解析正则表达式里字符串"不包含"匹配技巧 | 外刊IT评论

验证开头包含

一行字符串,验证开头是否包含某字符串,若包含则输出匹配到的数据,此方法会包括断言。也就是说假如验证“123erre”的开头是否是123,匹配到的数据为123erre而不是erre。验证开头包含且匹配数据不包括断言请看下一章

{
System.out.println("验证开头包含");
Pattern compile = Pattern.compile("^(?=wer).*");
Matcher matcherError = compile.matcher("wwwdf werrfb wers");
Matcher matcherRight = compile.matcher("wercdv");
System.out.println(matcherError.find());//false
// System.out.println("matcherRight.find() = " + matcherRight.find());//true
while (matcherRight.find()) {
System.out.println("matcherRight.group() = " + matcherRight.group());//wercdv
}
}

结果如下

验证开头包含
false
matcherRight.group() = wercdv

解析:

^(?=wer).*匹配wercdv的时候,先因为^来到了位置0,位置0后面是符合.*的,遂开始匹配断言,注意这时候位置依然在位置0,因为是先行断言,位置0后面是wer符合断言要求,故匹配到了wercdv

验证开头包含且匹配到的数据不包括断言

假如说要匹配开头是wer的字符串,字符串是wercdv,匹配到的是cdv而不是wercdv

{
System.out.println("验证开头包含,匹配到的数据不包括断言");
Pattern compile = Pattern.compile("^wer(?<=wer)(.*)");
Matcher matcherError = compile.matcher("wwwdf werrfb wers");
Matcher matcherRight = compile.matcher("wercdv");
System.out.println(matcherError.find());//false while (matcherRight.find()) {
System.out.println("matcherRight.start() = " + matcherRight.start());//0
System.out.println("matcherRight.end() = " + matcherRight.end());//6
System.out.println("matcherRight.group() = " + matcherRight.group(1));//cdv
}
}

结果如下

验证开头包含,匹配到的数据不包括断言
false
matcherRight.start() = 0
matcherRight.end() = 6
matcherRight.group() = cdv

解析:

^wer(?<=wer)(.*)因为^所以先来到了位置0,然后匹配wer,来到了位置3,位置3后面符合.*的要求,因为后行匹配,前面是wer,符合要求,在取出的过程中,有一个分组,选取的是.*故最终取出cdv

验证结尾包含,且匹配到的数据不包括断言

举个例子,匹配以“元”结尾的词汇,且匹配到的数据不包括“元”,譬如“12元”,匹配到的数据是12。

//验证结尾包含
{ System.out.println("验证结尾包含,且获得结尾之前的数字");
Pattern compile = Pattern.compile("(\\d+)(?=元).$");
Matcher matcherRight = compile.matcher("12元");
Matcher matcherError = compile.matcher("12刀");
while (matcherRight.find()) {
System.out.println("matcherRight.group(1) = " + matcherRight.group(1));//12
}
while (matcherError.find()) {
System.out.println("matcherError.group() = " + matcherError.group());
}
}

运行结果是

验证结尾包含,且获得结尾之前的数字
matcherRight.group(1) = 12

解析:

(\\d+)(?=元).$因为$先来到位置3,如图所示

然后因为(\\d+)(?=元).$.,位置向前一步,来到了位置2,然后前行断言,位置2的后面是元,前面是数字符合要求,如下图所示

匹配到了“12元”,因为输出组①,故最终输出12。

断言基础应用总体代码

代码文件,运行结果写在了注释里面

/**
* 基础正则表达式实例
*/
@Test
public void testRegexOne() {
System.out.println(
"{\"name\": \"正则表达式相关断言有关的demo\",\n" +
" " +
"\"个数\": 2" +
"}");
/*
包含
*/
{
// 包含
// regex用法
System.out.println("验证包含");
Pattern compile = Pattern.compile(".+123.+");
Matcher matcher = compile.matcher("RIGHTdqwd123rget");
Matcher matcherError = compile.matcher("ERRORdfaf1353das");
while (matcher.find()) {
//测试正确的字符串
System.out.println("matcher.group() = " + matcher.group()); }
while (matcherError.find()) {
//测试错误的字符串
System.out.println("matcherError.group() = " + matcherError.group());//RIGHTdqwd123rget
}
// String用法
System.out.println("\"wqer\".contains(\"123\") = " + "wqer".contains("123"));//false }
/*
不包含
*/
{
// 不包含
// regex用法
System.out.println("验证不包含");
Pattern compile = Pattern.compile("^((?!123).)*$");
Matcher matcherRight = compile.matcher("qwe123wew333");
Matcher matcherError = compile.matcher("wqeqr12eqr34512");
while (matcherRight.find()) {
//验证包含123的字符串
System.out.println("matcherRight.group() = " + matcherRight.group());
}
while (matcherError.find()) {
//验证不包含123的字符串
System.out.println("matcherError.group() = " + matcherError.group());//matcherError.group() = wqeqr12eqr34512
}
//使用String方法
System.out.println("!\"wqer12335\".contains(\"123\") = " + !"wqer12335".contains("123"));//!"wqer12335".contains("123") = false
}
/*
验证开头
*/
//验证开头不包含
{
//验证开头不包括
System.out.println("验证开头不包括");
// regex方法
Pattern compile = Pattern.compile("^(?!ert).*");
Matcher matcherRight = compile.matcher("ert1234dsaf");
// System.out.println("matcherRight.find() = " + matcherRight.find());//false
while (matcherRight.find()) {
System.out.println("matcherRight.group() = " + matcherRight.group());
}
// System.out.println("compile.matcher(\"etrfgrte2134rwgert\").find() = " + compile.matcher("etrfgrte2134rwgert").find());//true
Matcher matcherError = compile.matcher("etrfgrte2134rwgert");
while (matcherError.find()) {
System.out.println("matcherError.group() = " + matcherError.group());//etrfgrte2134rwgert
}
}
{
System.out.println("验证开头包含");
Pattern compile = Pattern.compile("^(?=wer).*");
Matcher matcherError = compile.matcher("wwwdf werrfb wers");
Matcher matcherRight = compile.matcher("wercdv");
System.out.println(matcherError.find());//false
// System.out.println("matcherRight.find() = " + matcherRight.find());//true
while (matcherRight.find()) {
System.out.println("matcherRight.group() = " + matcherRight.group());//wercdv
}
}
//验证开头包含
{
System.out.println("验证开头包含,匹配到的数据不包括断言");
Pattern compile = Pattern.compile("^wer(?<=wer)(.*)");
Matcher matcherError = compile.matcher("wwwdf werrfb wers");
Matcher matcherRight = compile.matcher("wercdv");
System.out.println(matcherError.find());//false while (matcherRight.find()) {
System.out.println("matcherRight.start() = " + matcherRight.start());//0
System.out.println("matcherRight.end() = " + matcherRight.end());//6
System.out.println("matcherRight.group() = " + matcherRight.group(1));//cdv
}
}
//验证结尾不包含
{
System.out.println("验证结尾不包含");
Pattern compile = Pattern.compile(".+(?<!ing)$");
Matcher matcherError = compile.matcher("keepReading");
while (matcherError.find()) {
System.out.println("matcherError.group() = " + matcherError.group());
}
Matcher matcherRight = compile.matcher("keepHealthy");
while (matcherRight.find()) { System.out.println("matcherRight.group() = " + "\""+matcherRight.group()+"\"");//"keepHealthy"
} }
//验证结尾包含
{ System.out.println("验证结尾包含,且获得结尾之前的数字");
Pattern compile = Pattern.compile("(\\d+)(?=元).$");
Matcher matcherRight = compile.matcher("12元");
Matcher matcherError = compile.matcher("12刀");
while (matcherRight.find()) {
System.out.println("matcherRight.group(1) = " + matcherRight.group(1));//12
}
while (matcherError.find()) {
System.out.println("matcherError.group() = " + matcherError.group());
}
} }

不按套路出牌,帮你彻底理解断言

一般情况下,使用断言,主要是用它零宽的属性,向验证开头包含,结尾包含时一般不用断言,分组就能解决问题,当开头不包含或结尾不包含等问题时,使用断言是很好的办法。

@Test
public void testOther(){
System.out.println("不按套路来,帮您彻底理解断言");
Pattern compile = Pattern.compile("(?=re)\\w+");
Matcher matcher = compile.matcher("reading a book");
System.out.println("不按套路来的零宽先行正向断言");
while (matcher.find()) {
System.out.println("matcher.group() = " + matcher.group());//matcher.group() = reading
}
System.out.println("不按套路来的零宽后行正向断言");
Pattern compile1 = Pattern.compile("\\w+(?i)(?<=re)");
Matcher matcher1 = compile1.matcher("i want toReading a book");
while (matcher1.find()) {
System.out.println("matcher1.group() = " + matcher1.group());//matcher1.group() = toRe
}
System.out.println("不按套路来的零宽先行负向断言");
Pattern compile2 = Pattern.compile("(?i)ing(?!re)\\w+");
Matcher matcher2 = compile2.matcher("i am LovingReading a book and HatingBuy a book");
while (matcher2.find()) {
System.out.println("matcher2.group() = " + matcher2.group());//matcher2.group() = ingBuy
}
System.out.println("不按套路来的零宽后行负向断言");
Pattern compile3 = Pattern.compile("(?i)\\w+(?<!re)a");
Matcher matcher3 = compile3.matcher("llrea lla");
while (matcher3.find()) {
System.out.println("matcher3.group() = " + matcher3.group());//matcher3.group() = lla
} }

绝大多数网站在讲解断言问题的时候总是去引用断言demo一章里面那四个例子,这会给人造成一种“前行断言适配结尾,后行断言适配开头”的错觉,事实上并不是这个样子,重要的是要具体问题具体分析。如同上面的代码,不按所谓常理出牌,有助于正则表达式之理解。

上面取了四个例子,皆不按套路来,为方便理解,选一个细讲

System.out.println("不按套路来的零宽后行负向断言");
Pattern compile3 = Pattern.compile("(?i)\\w+(?<!re)a");
Matcher matcher3 = compile3.matcher("llrea lla");
while (matcher3.find()) {
System.out.println("matcher3.group() = " + matcher3.group());//matcher3.group() = lla
}

运行结果

不按套路来的零宽后行负向断言
matcher3.group() = lla

(?i)\\w+(?<!re)a的本意是不区分大小写地去适配结尾是a且a的前面不是re的字符。(?!)是不区分大小写的意思。正则发挥作用时,先找到a,位置来到a的前面,因为后行断言,位置的前面不能是re。因为\\w+,a的前面需要是字母或数字。综合看来,意思是a的前面是除re之外的字母或数字。

如果真的不理解,就死记下面的实例

  1. 不包含a

    Pattern compile = Pattern.compile("^((?!a).)*$");
    Matcher matcher = compile.matcher("ssdas");
    System.out.println("matcher.find() = " + matcher.find());//matcher.find() = false
  2. 开头不包含a

    Pattern compile1 = Pattern.compile("^(?!a).*");
    Matcher matcher1 = compile1.matcher("basa");
    System.out.println("matcher1.find() = " + matcher1.find());//matcher1.find() = true
  3. 结尾不包含a

    Pattern compile2 = Pattern.compile("\\w*(?<!a)$");
    Matcher matcher2 = compile2.matcher("qwerdsa");
    System.out.println("matcher2.find() = " + matcher2.find());//matcher2.find() = false
  4. 包含a且不包含b

    Pattern compile3 = Pattern.compile("^((?!b).)*a((?!b).)*$");
    Matcher matcher3 = compile3.matcher("qwbera");
    System.out.println("matcher3.find() = " + matcher3.find());//matcher3.find() = false
  5. 处于a和b之间

    Pattern compile4 = Pattern.compile("^.(?<=a)(.*)(?=b).$");
    Matcher matcher4 = compile4.matcher("asdbdb");
    System.out.println("matcher4.find() = " + matcher4.find());//matcher4.find() = true
    System.out.println("matcher4.group() = " + matcher4.group(1));//matcher4.group() = sdbd
  6. 适配字符串中所有不以b结尾的数字

    Pattern compile5 = Pattern.compile("\\d+(?!b|\\d+)");
    Matcher matcher5 = compile5.matcher("1b 123b 123 45");
    System.out.println("matcher5.find() = " + matcher5.find());//matcher5.find() = true
    System.out.println("matcher5.group() = " + matcher5.group());//matcher5.group() = 123
    matcher5.find();
    System.out.println("matcher5.group() = " + matcher5.group());//matcher5.group() = 45
  7. 适配字符串中所有以a开头的数字

    Pattern compile6 = Pattern.compile("(?<=a)\\d+");
    Matcher matcher6 = compile6.matcher("123 123 a231 a34 45a a23");
    while (matcher6.find()) {
    System.out.println("matcher6.group() = " + matcher6.group());
    //matcher6.group() = 231
    //matcher6.group() = 34
    //matcher6.group() = 23
    }

在某些时候亦可以用\b来实现某些可以用断言实现的正则表达,举个例子

获取所有以b结尾的数字

Pattern compile7 = Pattern.compile("(\\d+)b\\b");
Matcher matcher7 = compile7.matcher("1b 123b 123 45");
while (matcher7.find()) {
System.out.println("matcher7.group() = " + matcher7.group(1));
// matcher7.group() = 1
// matcher7.group() = 123
}

正则表达式断言精讲 Java语法实现的更多相关文章

  1. 【笔记0-开篇】面试官系统精讲Java源码及大厂真题

    背景 开始阅读 Java 源码的契机,还是在第一年换工作的时候,被大厂的技术面虐的体无完肤,后来总结大厂的面试套路,发现很喜欢问 Java 底层实现,即 Java 源码,于是我花了半年时间,啃下了 J ...

  2. 面试官系统精讲Java源码及大厂真题系列之Java线程安全的解决办法

    1. 背景 1.1 static修饰类变量.方法.方法块.  public + static = 该变量任何类都可以直接访问,而且无需初始化类,直接使用 类名.static 变量 1.2 多个线程同时 ...

  3. Java生鲜电商平台-SpringCloud微服务开发中的数据架构设计实战精讲

    Java生鲜电商平台-SpringCloud微服务开发中的数据架构设计实战精讲 Java生鲜电商平台:   微服务是当前非常流行的技术框架,通过服务的小型化.原子化以及分布式架构的弹性伸缩和高可用性, ...

  4. Gradle系列之一 Groovy语法精讲

    Gradle技术之一 Groovy语法精讲 gradle脚本是基于groovy语言开发的,想要学好gradle必须先要对groovy有一个基本的认识 1. Groovy特点 groovy是一种DSL语 ...

  5. SQL语法精讲(包括建库、建表、建视图、查询、增加、删除、)

    SQL语法精讲(包括建库.建表.建视图.查询.增加.删除.修改) SQL分类: DDL—数据定义语言(CREATE,ALTER,DROP,DECLARE) DML—数据操纵语言(SELECT,DELE ...

  6. 深入Java核心 Java内存分配原理精讲

    深入Java核心 Java内存分配原理精讲 栈.堆.常量池虽同属Java内存分配时操作的区域,但其适用范围和功用却大不相同.本文将深入Java核心,详细讲解Java内存分配方面的知识. Java内存分 ...

  7. iOS开发——语法篇OC篇&高级语法精讲二

    Objective高级语法精讲二 Objective-C是基于C语言加入了面向对象特性和消息转发机制的动态语言,这意味着它不仅需要一个编译器,还需要Runtime系统来动态创建类和对象,进行消息发送和 ...

  8. iOS开发——语法篇OC篇&高级语法精讲

    高级语法精讲 一.NSSet.NSMutableSet集合的介绍 1)NSSet.NSMutableSet集合,元素是无序的,不能有重复的值. 2)用实例方法创建一个不可变集合对象 例如: //宏定义 ...

  9. 总结:Java 集合进阶精讲2-ArrayList

    知识点:Java 集合框架图 总结:Java 集合进阶精讲1 总结:Java 集合进阶精讲2-ArrayList 初探: ArrayList底层结构是数组,是List接口的 可变数组的实现,所以会占用 ...

随机推荐

  1. PHP min() 函数

    实例 通过 min() 函数查找最小值: <?php高佣联盟 www.cgewang.comecho(min(2,4,6,8,10) . "<br>");echo ...

  2. PHP ignore_user_abort() 函数

    实例 设置为 false(默认)- 与客户机断开会终止脚本的执行: <?phpignore_user_abort();?>高佣联盟 www.cgewang.com 上面代码的输出如下: 0 ...

  3. Redis分布式限流器

    以下文章来源于微信公众号:程序员内点事 ,作者:程序员内点事 请大家关注原作者 1. 什么是限流?为什么要限流? 限流是保证系统高可用的重要手段!!!由于互联网公司的流量巨大,系统上线会做一个流量峰值 ...

  4. day16.内置方法与模块

    一.内置方法 1.abs 绝对值函数 res = abs(-100) print(res) 2.round 四舍五入 (n.5 n为偶数则舍去 n.5 n为奇数,则进一!) ""& ...

  5. IDEA插件配置推荐

    一.配置 [自动编译]如下图配置:推荐指数[***] [忽略大小写]说明:IDEA默认是匹配大小写,此开关如果未关,你输入字符一定要符合大小写.比如敲string是不会出现代码提示或只能补充.但是如果 ...

  6. Hive对字段进行urlDecode

    最近项目中需要对埋点日志hive表进行分析,并且按一定的规则统计出来满足要求的用户pin.本来以为是一件比较简单的事,结果在查看导出的词表时发现很多带有"%"的明显具有url en ...

  7. Vuex mapGetter的基本使用

    getter相当于Vuex中的计算属性 对 state 做处理再返回 mapGetters 把 Store 中的 getters 映射到组件中的计算属性中 Store文件 import Vue fro ...

  8. myBatis源码解析-反射篇(4)

    前沿 前文分析了mybatis的日志包,缓存包,数据源包.源码实在有点难顶,在分析反射包时,花费了较多时间.废话不多说,开始源码之路. 反射包feflection在mybatis路径如下: 源码解析 ...

  9. 树堆(Treap)学习笔记 2020.8.12

    如果一棵二叉排序树的节点插入的顺序是随机的,那么这样建立的二叉排序树在大多数情况下是平衡的,可以证明,其高度期望值为 \(O( \log_2 n )\).即使存在一些极端情况,但是这种情况发生的概率很 ...

  10. Windows下的Minio启动命令

    1.首先安装Minio到你的 Windows系统的盘符中,例如D盘 2. 打开dos命令行,进入存放minio.exe的文件夹下,输入 minio.exe server /data  命令,意味着启动 ...