正则表达式 regular expression

前言

文章为作者根据网络零散知识及部分技术文章整理所得,以供参考查阅、快速入门,可供匿名片段引用或转载,but~谢绝整篇抄袭。能力有限,也许此文无法像四平八稳的学术论文一样权威可靠/逻辑严谨但却晦涩难懂、无法像娱乐杂文一样诙谐幽默但却逻辑混乱,还请见谅。欢迎交流讨论、学习互助。

一、文章概览

(一)正则表达式定义及意义

1、定义(它是谁/谁是它)

略。

2、意义(它是吃哪碗饭的~/为什么要认识它~)

略。

(二)正则表达式语法三模块(协同实现匹配工作的三兄弟~)

1、匹配字符(1个字符/字符串)

普通字符、特殊字符、标准字符集合、自定义字符集合

2、匹配数量(多个字符/字符串)

量词表达式

3、匹配相对位置

零宽表达式

(三)练习

1、常见使用场景及其正则表达式

2、开发环境使用正则表达式(Java)

二、普通字符和特殊字符本身的匹配

普通字符本身的匹配

普通字符,用于匹配字符、匹配数量。

字母、数字、汉字、下划线、以及没有特殊定义的标点符号,都是普通字符。表达式中的普通字符,在匹配字符串的时候,匹配字符本身,匹配字符数量为一个

(二)特殊字符本身的匹配

简单的转义字符组合:转义字符+特殊字符,用于匹配字符、匹配数量。

\n

匹配一个换行符行首,一行开始的位置

\r

匹配一个回车符(光标下移一行

\t

匹配一个制表符

\ ^      \ $

\ (       \ )

\ [       \ ]

\ {       \ }

\|        \.      \\

\?       \+     \*

匹配转义字符\之后的那个特殊字符本身,匹配一个。

(注意转义字符与特殊字符之间不含其它字符)

转义字符+特殊字符组合:匹配\之后的特殊字符本身,匹配字符数量为一个。

1、什么是特殊字符

正则表达式中的特殊字符在正则表达式中有特殊功能不匹配该字符本身在此场景下,若要匹配该字符本身,需要在其前加转义字符“\形成简单的转义字符组合再进行匹配

eg: 字符 - ,放在中括号内使用时表示连接其前后的两个的字符范围的并集,若在中括号内要表示 - 字符本身,需要加上转义字符“\”,反之在中括号外为普通字符、要表示 - 本身,则无需加转义字符。

2、\n \r \t 的区别

https://www.cnblogs.com/phpxuetang/p/6570838.html

\n是换行,英文是New line

\r是回车,英文是Carriage return,表示使光标下移一格。

\r\n表示回车换行。我们在平时使用电脑时,已经习惯了回车和换行一次搞定,敲一个回车键,即要回车,又要换行。

(1)\n 软回车:

在Windows 中表示换行且回到下一行的最开始位置。相当于Mac OS 里的 \r 的效果。

在Linux、unix 中只表示换行,但不会回到下一行的开始位置。

(2)\r 软空格:

在Mac OS 中表示换行且返回到下一行的最开始位置,相当于Windows 里的 \n 的效果。

在Linux、unix 中表示返回到当行的最开始位置

(3)\t 跳格(移至下一列)。

它们在双引号或定界符表示的字符串中有效,在单引号表示的字符串中无效。

\r\n 一般一起用,用来表示键盘上的回车键,也可只用 \n。

\t表示键盘上的“TAB”键。

就像你使用 enter和shift+enter的区别

(4)文件中的换行符号:

linux,unix: \r\n

windows : \n

Mac OS : \r

三、标准字符集合

标准字符集合,用于匹配字符、匹配数量。

四种(七个)标准字符集合,大写、小写匹配的集合为两互斥的集合。

注意:一个标准字符集合是多个匹配条件的并集,匹配满足条件集的字符,匹配字符数量为一个。

  • \d与\D

\d,匹配0-9中的任意一个数字。

\D,匹配与\d互斥的字符集合,匹配字符数量为一个

  • \w与\W

\w,匹配任意一个字母、数字、下划线,即匹配A-Z a-z 0-9 _ 组成的字符并集内的任意一个字符。

\W,匹配与\w互斥的字符集合,匹配字符数量为一个

  • \s与\S

\s,匹配空白字符集合中的任意一个空白字符,即匹配空格、制表符、换行符并集内的任意一个字符。

\S,匹配与\s互斥的字符集合,匹配字符数量为一个

注意:一般用[\s\S]匹配包含换行符在内的所有字符并集内的任意一个字符。

  • .(英文句点)

匹配除了换行符(\n)之外的任意一个字符。

四、自定义字符集合

自定义字符集合,用于匹配字符、匹配数量。

自定义字符集合的关键表示符号为中括号:[ ]

(一)什么是自定义字符集合

用户自己指定的匹配条件的并集就是自定义字符集合,自定义字符集合具体条件表达式(匹配条件)用中括号[ ]括起。

(二)匹配逻辑

匹配字符时,对中括号[ ]内的字符取并集,即“”的运算逻辑。

(三)匹配表达式注意事项

1、普通字符与特殊字符

中括号内[ ],^ 表示中括号整体条件并集合的互斥集合,在中括号内要表示 ^ 本身,同样需要加转义字符即 \^。除此之外,在本文二(二)中列出的特殊字符放在中括号[ ]内时成为普通字符

中括号内[ ],当 - 前后连接的两个字符同时为小写字母、同为大写字母、同时为数字时,表示连接其前后的两个字符范围的并集,否则在中括号内[ ] - 表示其符号本身,保守起见,在中括号内要匹配-字符本身都加上转义字符。

中括号内[ ],写自定义表达式时,大写、小写子母要分开写,大写字母与小写子母不能合并写[a-Z],而应该分开写成:[a-zA-Z]

  1. 标准字符集合

中括号内[ ] . (英文句点)成为普通字符而非标准字符集。在本文第三章列出的七个标准字符集中,除了 . (英文句点)剩下6个标准字符集合如果被包含于中括号[ ],自定义字符集合将包含该标准字符集合

eg:[\d.+\-]将匹配数字、 . (英文句点)、+、- 中的任意一个字符。

(四)匹配数量

匹配数量时,匹配满足中括号[ ]内给定条件的字符并集中的任意一个字符。

五、量词表达式

量词表达式,用于匹配数量。

量词的关键表示符号为花括号:{ }

  •  什么是量词

量词,是修饰字符匹配次数的特殊字符。当进行字符匹配时,用于进一步指定对应字符连续出现的次数。具体数量表达式用花括号{ }括起。

(二)量词表达式规则

量词可以修饰单个字符、多个字符、单个字符集、多个字符集。多个字符或多个字符集用小括号括起再紧跟量词表达式,则多个字符或者多个字符集同时被一个量词表达式修饰。

{n}

达式连续出现n

{mn}

表达式最少连续出现m次,最多连续出现n次

{m,}

表达式至少连续出现m次

?

表达式连续出现0次或者1次,相当于{0,1}

+

表达式至少连续出现1次,相当于{1,}

*

表达式连续出现任意次,相当于{0,}

 + 在中括号内表示普通字符,在中括号外为量词表达式。

{N}限定固定的数量,匹配时,数量不可浮动。

{m,n}限定浮动数量,匹配时,数量在指定范围内浮动。其中n可以缺省,m不可缺省,逗号不能省略,限定最少次数、不限定最多次数。最少出现次数值不可缺省,最多出现次数值可以缺省。

(三)量词匹配的两种模式

1、贪婪模式

匹配字符越多越好,优先匹配量词表达式的最大值,默认的匹配模式。

2、勉强模式

匹配字符越少越好,优先匹配量词表达式的最小值,在修饰匹配次数的特殊符号后再加上一个 ? 号。

3、量词匹配模式例子

(1)\d{6}

表示匹配6个连续数字的字符串

(2)\d\d{6}

表示匹配7个连续数字的字符串(1+6=7)

(3)(\d\d){6}

表示匹配12个连续数字的字符串(2X6=12)

(4)\d{3,6}等于\d{3,6}+

表示匹配3到6个(即:3个或4个或5个或6个)连续数字的字符串,优先匹配连续出现次数较多的字符串。默认情况下就是贪婪模式

(5)\d{3,6}? 实际使用时相当于\d{3}

表示匹配3到6个(即:3个或4个或5个或6个)连续数字的字符串,优先匹配连续出现次数较小的字符串。在量词表达式之后加 ? 号,即所谓的“非贪婪模式”或叫做“勉强模式”。

六、零宽表达式

零宽表达式,用于匹配相对位置。

零宽表达式:可用于匹配相对位置或者匹配内容不计入最终结果的正则表达式。

(一)什么是零宽表达式

正则表达式匹配过程中,如果子表达匹配到的是字符内容,而非位置,并被保存到最终的匹配结果中,那么认为这个子表达式是占有字符的;如果子表达式匹配的仅仅是相对位置或者匹配的内容并不保存到最终的匹配结果中,那么就认为这个子表达式是零宽度的,该表达式称为零宽表达式。一个正则表达式是占有字符还是零宽度,是针对匹配的内容是否为相对位置、或者是否保存到最终的匹配结果中而言的。

1、匹配的内容不保存

只进行子表达式的匹配,匹配内容只作为中转引用、不计入最终的匹配结果,是零宽度。

2、匹配结果是相对位置而非占位符

是相对位置的匹配,这个位置应该符合某个条件:当前被匹配字符位置的前、后第一个字符/字符串,应该符合零宽表达式指定的条件,但最终结果不匹配(不计入)其前、后的字符。

(二)三种边界字符

^或\A

与字符串开始的位置匹配

$或\Z

与字符串结束的位置匹配

\b

所匹配字符串需满足的条件:边界上的第一个字符不是\w(字母、数字、下划线)

1、什么是零宽

边界字符匹配的结果不是字符或字符串,而是一个满足条件的相对位置。因此,匹配结果是非占位符,零宽度的,仅标识匹配字符开始位置或结束位置。

2、作用场景

边界字符需放在中括号[ ]外面才能起匹配相对位置的作用,例如:^ 如果放在中括号[ ]里面则表示取反/互斥的功能,则无法起到匹配相对位置的作用。

3、用法举例

(1)\blove\b

表示匹配一个字符串love且该单词的开头和结尾的第一个字符都不是\w(字母、数字、下划线)的字符串love。

(三)预搜索(零宽断言/环视)

expression(?=expression1

断言expression出现的位置的后面能匹配表达式expression1

匹配结果:expression

(?<=expression1)expression

断言目标expression出现的位置的前面能匹配表达式expression1

匹配结果:expression

expression(?!expression1

断言自身出现的位置的后面不能匹配表达式expression1

匹配结果:expression

(?<!expression1)expression

断言自身出现的位置的前面不能匹配表达式expression1

匹配结果:expression

(?=expression1)expression

语法合法性待核实(java可行)

断言目标expression出现的位置的前面能匹配表达式expression1

匹配结果:expression + expression1

expression(?<=expression1

语法合法性待核实(java可行)

断言expression出现的位置的后面能匹配表达式expression1

匹配结果:expression + expression1

实例如下

待匹配字符串:adfasdfingadadaldkfjasingaskfljas

1、[a-z]+(?=ing),不含尾,贪婪

匹配结果:       adfasdfingadadaldkfjas

2、(?<=ing)[a-z]+,不含头,贪婪

匹配结果:                       adadaldkfjasingaskfljas

(四)匹配模式

1、IGNORECASE

匹配时忽略大小写,case insensitive。默认情况下,正则表达式是要区分大小写的。

2、SINGLELINE 单行模式

整个文本看作一个字符串进行匹配,只有一个开头和一个结尾。此时符号 . (英文句点)可以匹配包含换行符(\n)在内的任意一个字符。

3、MULTILINE多行模式

每行都是一个字符串,都有开头和结尾,遇到换行符即表示另外一行字符串,此时符号 . (英文句点)无法匹配换行符(\n)。

在多行模式下 \AXXX 表示在整个文本的开头行(第一行)匹配XXX字符串,等价于单行模式下的 ^XXX

在多行模式下 XXX\Z 表示在整个文本的结尾行(最后一行)匹配XXX字符串,等价于单行模式下的 XXX$

^ $ 用于单行模式的开头结尾,\A \Z 用于多行模式整篇文章的开头结尾。

(五)分组捕获与反向引用

1、分组捕获

表达式

作用

|

分支结构

左右两边表达式之间“或”关系,匹配左边或者右边。

()

捕获组

1、在被修饰匹配次数的时候,括号中的表达式可以作为整体被修饰

2、取匹配结果的时候,括号中的表达式匹配到的内容可以被单独得到

3、每一对括号会分配一个编号,使用小括号( )的捕获根据左括号的顺序从1开始自动编号。捕获元素编号为0的第一个捕获是由整个正则表达式模式匹配的文本。

?:Expression

非捕获组

一些表达式中,不得不使用小括号(),但又不需要保存小括号()中子表达式匹配的内容(保存是为了用于反向引用),这时可以用非捕获组来抵消使用()带来的副作用(小括号()内的正则表达式的匹配内容会保存至内存)。

2、反向引用

反向引用表达式:((expression1)(expression2))\n\i\j,其中n,i,j为数字,表示引用的内容出现的次数。此表达式中,编号为0和1的分组所捕获的内容一样为整个表达式所捕获匹配的最终内容、expression1expression2整体出现n次,编号为2的分组捕获的内容为连续出现i次的expression1,编号为3的分组捕获的内容为:连续出现j次的expression2。

每一对小括号()会分配一个编号,使用小括号()的捕获根据左小括号的顺序从1开始自动编号。

通过反向引用可以对分组已捕获的字符串进行引用再匹配。

3、用法举例

(1)([a-z]{2})\3

表示匹配2个a~z字符的字符串,并且该字符串连续出现3。详细分析:即小括号(Expression)匹配的结果作为一个整体先存到内存中,后被“\3”进行引用修饰再进行二次匹配最终得到想要的结果。如:go其中的go两个字符为一个整体被“\3”引用修饰再匹配,结果匹配字符串为gogogo。

(2)(?:[a-z]{2})\3

无法匹配任何结果,因为小括号内加了?:抵消了分组捕获得到的结果的保存,即没有对分组得到的结果进行保存,所以“\3”没有修饰引用的对象,所以无法匹配到任何结果。符号“?:”不对(expression)匹配结果进行产生影响,但是会让(expression)匹配的结果不保存到内存中,无法再次被引用匹配。

(3)((Expression1)(Expression2))(Expression3)\i\j\k\m

使用小括号()分组捕获然后进行反向引用,根据左括号的顺序从1开始自动编号,对已分组捕获的字符串进行引用。

A、\i修饰的对象是Expression1和Expression2匹配的结果的整体重复出现i次

B、\j修饰的对象是Expression1匹配的结果的整体重复出现j次

C、\k修饰的对象是Expression2匹配的结果的整体重复出现k次

D、\m修饰的对象是Expression3匹配的结果的整体重复出现m次

七、练习

(一)电话号码验证

1、思路分析

(1)电话号码由数字和符号 - 构成

(2)电话号码为7到8位

(3)如果电话号码中包含有区号,那么区号为三位或四位,首位是0

(4)区号用符号 - 和其他部分隔开

(5)移动电话号码为11位

(6)11位移动电话号码的第一位和第二位为"13","15","17","18","19"

2、最终表达式

(1)固话号码:0\d{2,3}-\d{7,8}

区号以0开头,区号与号码用 - 连接,号码为7~8位

(2)移动电话号码:1[35789]\d{9}

以1开头,第二位为35789中的任意一个数字,总共有11位数字。

(3)电话号码验证最终正则表达式:(0\d{2,3}-\d{7,8})|(1[35789]\d{9}) 

综合匹配固话或者移动电话,逻辑或运算。

(二)电子邮箱验证

1、思路分析

(1)用户名∶字母、数字、下划线、中划线、英文句点组成。

(2)@

(3)网址∶字母、数字组成。

(4)小数点∶ .

(5)组织域名∶2-4位字母组成。

(6)不区分大小写

2、步骤结果

(1)用户名:字母、数字、下划线、中划线、英文句点,长度不定,至少一个字符,字母不区分大小写:[\w.\-]+

(2)网址:字母、数字,长度不定,至少一个字符,字母不区分大小写:[a-zA-Z0-9]+

(3)组织域名:由2~4位字母组成,不区分大小写:(\.[a-zA-Z]){2,4}

3、最终结果

电子邮箱验证最终正则表达式:[\w.\-]+@[a-zA-Z0-9]+(\.[a-zA-Z]{2,4}){1,2}

eg:linhei2356hei@sina.com.cn 

dasif234@163.com

(三)常用正则表达式列表

匹配中文字符

[\u4e00-\u9fa5]

匹配空白行

\n\s*\r

匹配HTML标记

<[a-zA-Z]+.*?>([\s\S]*?)</[a-zA-Z]*?>|<.*? />

匹配首尾空白字符

^\s*|\s*$

匹配Email地址

[\w.\-]+@[a-zA-Z0-9]+(\.[a-zA-Z]{2,4}){1,2}

匹配网址URL

[a-zA-z]+://[^\s]*

匹配国内电话号码

(0\d{2,3}-\d{7,8})|(1[35789]\d{9})

匹配腾讯QQ

[1-9][0-9]{4,}

匹配中国邮政编码

[1-9]\d{5}(?!\d)

匹配身份证

\d(15)|\d(18)

匹配ip地址(十进制以下表示的ip)

\d+\.\d+\.\d+\.\d+

注意:

<[a-zA-Z]+.*?>([\s\S]*?)</[a-zA-Z]*?>|<.*? />

匹配HTML标记的正则表达式中,Expression1|Expression2,Expression1表示搜索非自闭标签,Expression2 表示搜索自闭标签。注意表达式中使用的勉强模式

匹配中国邮政编码正则表达式中,(?!\d) 即零宽断言,表示匹配的结果字符串后面紧跟着的第一个字符为非数字。

总结:

再复杂的正则表达式均由~所列出的正则语法规则组成,所以重点是掌握上述所列的基本语法,基础要牢。

八、开发环境中使用正则表达式

(一)在文本编辑器或者开发工具软件中使用正则表达式

eclipse、Notepad++、Editplus、UltraEdit

(二)数据库中也可以使用正则

Mysql5.5以上、Oracle10g以上

例如∶

SELECT prod_name

FROM products

WHERE prod_name REGEXP '.000'

(三)在java程序代码中使用正则表达式

gitee地址:

相关类位于:java.util.regex包下,jdk自带jar包。

Pattern类:正则表达式的编译表示形式。

Pattern p = Pattern.compile(“expression”); // 建立正则表达式,并启用相应模式(单行、多行、是否忽略大小写)

Matcher类:执行匹配操作的引擎。

Pattern p = Pattern.compile(“expression”);

// 通过解释 Pattern 对 character sequence 执行匹配操作的引擎

Matcher m = p.matcher(string); //匹配string字符串

boolean flag=matcher.matches();

1、字符串操作

(1)测试正则表达式对象的基本用法及其三个常用方法测试

 package com.study.regex.test;
 ​
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 ​
 /**
  * @description: 测试正则表达式对象的基本用法及其三个常用方法测试:
  * matches();--判断给定字符串整体是否与该给定的正则表达式匹配
  *      返回布尔值
  * find();--判断给定字符串内是否包含与该给定的正则表达式匹配的下一个子序列
  *      返回布尔值
  * group();或者group(int N);--分组捕获功能,获得与给定正则表达式相匹配的字符串序列的对应分组字符串。其中分组规则为:将对应正则表达式的左括号从左往右自1开始进行编号进而对整个正则表达式进行相应分组获得对应子序列,其中group();与gourp(0);等价,表示整个正则表达式匹配的序列。
  *      返回匹配的分组子序列字符串。
  * @author: tiddler.lin
  * @date: 2020/8/13 001311:18
  */
 public class Demo01_Search {
     public static void main(String[] args) {
         // 创建pattern对象,并给定匹配模式 \w 字母、数字、下划线,匹配至少一个对应字符。
         Pattern p=Pattern.compile("\\w+");
         // 给定要匹配的字符串,并创建matcher对象
         String string="aslfdkja18483017";
         Matcher matcher=p.matcher(string);
         // mathces();--将整个字符串序列看成一个整体与该给定的正则表达式匹配
         boolean flag=matcher.matches();
         System.out.println(flag);
         String string2="aslfdkja&&**18483017";
         matcher=p.matcher(string2);
         boolean flag2=matcher.matches();
         System.out.println(flag2);
         System.out.println("==============");
         // find();--将整个字符串拆分成多个子序列,
         // 查找与该给定的正则表达式匹配的下一个子序列
         String string3="aslfdkja&&**18483017";
         matcher=p.matcher(string3);
         /**
          * 循环两次,打印结果为:
          *     aslfdkja
          *     18483017
          */
         while (matcher.find()){
             // group();--分组捕获功能,匹配整个表达式的子字符串。
             System.out.println(matcher.group());
         }
     }
 }

(2)测试正则表达式中分组的处理

 package com.study.regex.test;
 ​
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 ​
 /**
  * @description: 测试正则表达式中分组的处理
  *      可用于从邮箱地址中截取邮箱账号等场景。
  * @author: tiddler.lin
  * @date: 2020/8/13 001312:19
  */
 public class Demo02_Group {
     public static void main(String[] args) {
         // 创建pattern对象,并给定匹配模式
         // ([a-z]+)([0-9]+) 分组0:([a-z]+)([0-9]+) 分组1:[a-z]+  分组2:[0-9]+
         Pattern p=Pattern.compile("([a-z]+)([0-9]+)");
         // 创建matcher对象,并给定要匹配的字符串
         String string="aslfdkja&&18483017**fkj2382fdsg";
         Matcher matcher=p.matcher(string);
         while (matcher.find()){  // 循环1次
             System.out.println(matcher.group());//fkj2382
             System.out.println(matcher.group(1));//fkj
             System.out.println(matcher.group(2));//2382
         }
         System.out.println("=============");
         String string2="adfd324**asdf8592&&aklfjda89734";
         matcher=p.matcher(string2);
         /**
          * 循环3次
          * 第一次循环:
          *     adfd324--group();
          *     adfd   --group(1);
          *     324    --group(2);
          * 第二次循环:
          *     asdf8592
          *     asdf
          *     8592
          * 第三次循环:
          *       aklfjda89734
          *       aklfjda
          *       89734
          */
         while (matcher.find()){
             //([a-z]+)([0-9]+)
             System.out.println(matcher.group());
             //([a-z]+)
             System.out.println(matcher.group(1));
             //([0-9]+)
             System.out.println(matcher.group(2));
         }
 ​
     }
 }

(3)使用正则表达式实现字符串的替换功能

 package com.study.regex.test;
 ​
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 ​
 /**
  * @description: 使用正则表达式实现字符串的替换功能
  *      replaceAll();
  * @author: tiddler.lin
  * @date: 2020/8/13 001312:21
  */
 public class Demo03_Replace {
     public static void main(String[] args) {
         String string="adfd324**asdf8592&&aklfjda89734";
         Pattern pattern=Pattern.compile("[0-9]+");
         Matcher matcher=pattern.matcher(string);

         // 替换string字符串内,正则表达式匹配到的所有子序列
         String new_string = matcher.replaceAll("^_^");
         // adfd324**asdf8592&&aklfjda89734
         System.out.println(string);
         // adfd^_^**asdf^_^&&aklfjda^_^
         System.out.println(new_string);
     }
 }

(4)使用正则表达式实现字符串切割功能

 package com.study.regex.test;
 ​
 import java.util.Arrays;
 ​
 /**
  * @description: 使用正则表达式实现字符串切割功能
  *      string.split("regular expression");
  * @author: tiddler.lin
  * @date: 2020/8/13 001313:19
  */
 public class Demo04_Split {
     public static void main(String[] args) {
         String string="adfd324**asdf8592&&aklfjda89734";
         String[] split = string.split("[^a-z]+");
         System.out.println(string);
         // 3
         System.out.println(split.length);
         // [adfd, asdf, aklfjda],注意第二个、第三个元素含有空格。
         System.out.println(Arrays.toString(split));
         String[] split1 = string.split("[^a-z]");
         // 12
         System.out.println(split1.length);
         // [adfd, , , , , asdf, , , , , , aklfjda],注意空格数与元素个数。
         System.out.println(Arrays.toString(split1));
         System.out.println("============");
     }
 }

2、网络爬虫

(1)获取指定网页内所有链接网址

 package com.study.regex.test;
 ​
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 ​
 /**
  * @description: No more nonsense,show me the code!
  * @author: tiddler.lin
  * @date: 2020/8/13 001314:49
  */
 public class WebSpider {
     public static void main(String[] args) {
         String link="https://www.jd.com/";
         String htmlString = getHtmlString(link, "utf-8");
         // String regex="href=\"(http[\\w\\s./:]+?|//www[\\w\\s./:]+?)\"";
         String regex = "href=\"([\\w\\s./:]+?)\"";
         List<String> link_list = getMatcherLink(htmlString,regex);
         System.out.println(link_list.size());
         for (String link_string : link_list) {
             System.out.println(link_string);
         }
     }
     public static String getHtmlString(String urlstring,String charset){
         String htmlString=null;
         try {
             URL url=new URL(urlstring);
             InputStream inputStream = url.openStream();
             BufferedReader bufferedReader=new BufferedReader(

new InputStreamReader(inputStream, Charset.forName(charset)));
             StringBuffer stringBuffer=new StringBuffer();
             String read;
             while ((read=bufferedReader.readLine())!=null){
                 stringBuffer.append(read);
                 stringBuffer.append("\r\n");
             }
             bufferedReader.close();
             htmlString= stringBuffer.toString();
         } catch (MalformedURLException e) {
             // LOGGER.info("异常提示XXXX:{}",e);
         } catch (IOException e) {
             // LOGGER.info("异常提示XXXX:{}",e);
         }
         return htmlString;
     }
     public static List<String> getMatcherLink(String htmlString,String regex){
         // 获取整个超链接的内容
         // Pattern pattern=Pattern.compile("<a[\\s\\S]+?>[\\s\\S]+?</a>");
         // 获取超链接的href属性及其属性值
         Pattern pattern = Pattern.compile(regex);
         Matcher matcher = pattern.matcher(htmlString);
         List<String> link_list=new ArrayList<>();
         while (matcher.find()){
             // String regex = "href=\"([\\w\\s./:]+?)\"";
             link_list.add(matcher.group(1));  
         }
         return link_list;
     }
 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值