Java 正则表达式【匹配与分组基本原理】
简介
我们一般使用正则表达式是用来处理字符串的,不管是实际的开发中还是我们的算法竞赛中,使用正则表达式绝对可以大大提升我们的效率。
正则表达式(regular expression)其实就是对字符串进行模式匹配的技术。
快速入门
我们这里演示一个案例,使用正则表达式匹配下面字符串中的所有英文单词:
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class RegexDemo01 {public static void main(String[] args) {String content = "1995年,互联网的蓬勃发展给了Oak机会。业界为了使死板、单调的" +"静态网页能够“灵活”起来,急需一种软件技术来开发一种程序,这种程序可以通过" +"网络传播并且能够跨平台运行。于是,世界各大IT企业为此纷纷投入了大量的人力" +"、物力和财力。这个时候,Sun公司想起了那个被搁置起来很久的Oak,并且重新审" +"视了那个用软件编写的试验平台,由于它是按照嵌入式系统硬件平台体系结构进行编" +"写的,所以非常小,特别适用于网络上的传输系统,而Oak也是一种精简的语言,程" +"序非常小,适合在网络上传输。Sun公司首先推出了可以嵌入网页并且可以随同网页" +"在网络上传输的Applet(Applet是一种将小程序嵌入到网页中进行执行的技术)" +",并将Oak更名为Java。5月23日,Sun公司在Sun world会议上正式发布Java和" +"HotJava浏览器。IBM、Apple、DEC、Adobe、HP、Oracle、Netscape和微软" +"等各大公司都纷纷停止了自己的相关开发项目,竞相购买了Java使用许可证,并为自" +"己的产品开发了相应的Java平台。";// 提取文章中所有英文单词Pattern pattern = Pattern.compile("[a-zA-Z]+");Matcher matcher = pattern.matcher(content);while (matcher.find()){// 匹配到的内容都会放到 matcher.group(0)里面System.out.println(matcher.group(0));}}
}
运行结果:
Oak
IT
Sun
Oak
Oak
Sun
Applet
Applet
Oak
Java
Sun
Sun
world
Java
HotJava
IBM
Apple
DEC
Adobe
HP
Oracle
Netscape
Java
Java
同理,如果我们想要获取字符串中所有的数字、获取字符串中所有字符串或者数字,只需要这样修改:
Pattern pattern = Pattern.compile("[0-9]+");Pattern pattern = Pattern.compile("([0-9]+)|([a-zA-Z]+)");
要知道,我们自己实现的话会很复杂,我自己尝试过,比如实现提取所有英文字符,我们需要这样来设计:
- 把字符串转为 char 数组
- 遍历数组,判断字符的 ASCII码是否在 [a,z] , [A,Z]范围内
- 判断英文字符前的字符是否为非英文字符,如果是在字符前添加空格(防止结果连成一片)
- 判断英文字符后的字符是否为非英文字符,如果是在字符后添加空格(防止结果连成一片)
可以看到,我们自己编写程序去实现确实是十分复杂,所以为什么不学习正则表达式呢,取英文字符是比较简单的案例,如果遇到验证邮箱、手机号、身份证号、ip地址、提取字符串等需要各种字符串处理算法的时候,手写算法是十分烧脑的,最好的办法就是找到规律使用正则表达式,提高开发效率!
除此之外,我们学习的网络爬虫在做数据处理的时候,比如各种新闻标题、产品标题、商品评论,这些文本通常都是在超链接或者一些特殊标签内部,这时候我们直接使用正则表达式就可以很轻松地实现标签内文本的提取,这样,我们只需要专心爬虫的代码,而不需要过于担心爬到数据后的数据处理问题了。
底层实现
我们这里主要讨论一下 Java正则表达式中,matcher.find() 和 matcher.group(int group) 的底层是怎么实现的。
案例
找出四个数字连在一起的子串
1998年12月8日,第二代Java平台的企业版J2EE发布。1999年6月,Sun公司发布了第二代Java平台(简称为
Java2)的3个版本:J2ME(Java2 Micro Edition,Java2平台的微型版),应用于移动、无线及有限资源的
环境;J2SE(Java 2 Standard Edition,Java 2平台的标准版),应用于桌面环境;J2EE(Java 2Enterprise Edition,Java 2平台的企业版),应用3443于基于Java的应用服务器。Java 2平台的发布
,是Java发展过程中最重要的一个里程碑,标志着Java的应用开始普及9889
代码:
// 1. 找出四个数字连在一起的子串// \\d 代表数字String regex = "\\d\\d\\d\\d";Pattern pattern = Pattern.compile(regex);Matcher matcher = pattern.matcher(content);while (matcher.find()){System.out.println(matcher.group()); // 无参时默认就是 group(0)}
输出结果:
1998
1999
3443
9889
我们开始分析代码以及通过debug分析源码:
matcher.find() & matcher.group() 原理
matcher.group(0)
- 首先,根据我们给它的规则去匹配,定位到满足要求的子字符串位置(比如1998)
- 然后,将子字符串的开始的索引记录到 matcher 对象的属性中去(int[] groups)。
- groups[0] = 0 , 把该子字符串结束的索引+1的值记录到 groups[1]中去,groups[1] = 4。

我们Matcher类的属性 int[] groups 的初始大小为 20 ,初始值均为 -1.
- 同时记录属性oldLast 的值为 该子字符串结束的索引+1的值, 即 4,即下次执行matcher.find() 的时候从 4 开始。
- 接下来我们分析 matcher.group(0) 的源码:
public String group(int group) {if (first < 0)throw new IllegalStateException("No match found");if (group < 0 || group > groupCount())throw new IndexOutOfBoundsException("No group " + group);if ((groups[group*2] == -1) || (groups[group*2+1] == -1))return null;return getSubSequence(groups[group * 2], groups[group * 2 + 1]).toString();}
- 此时我们调用 mactcher.group(0) 的话,很明显会返回 getSubSequence(groups[0],groups[1]).toString(); 相当于根据 groups[0]=0 和 groups[1]=4 记录的位置来截取字符串 ,注意是左闭右开的 [0,4)。 此时,输出 1998.
继续下一次 matcher.find() 方法,这次定位到了 1999 这个位置:
- 此时,groups[0] = 31, groups[1] = 35 ,oldLast = 35 。
- 同样再次调用 matcher.group(0) , 输出 1999。
到这里,我们基本了解了 group(0) 的含义,每次调用matcher.group(0) 它都会去查找字符串中的子串首尾下标,这些下标存在 groups数组中 ,而且首下标永远都是 groups[0] ,尾下标永远都是 groups[1] 。
但是如果我们调用的是 group(1)、或者group(n) 又会是怎样的情况呢?
matcher.group(n)
其实,group(n) 涉及到的是一个分组的概念,体现在代码中的匹配语句上就是括号,我们对上面的匹配语句做一个修改:
String regex = "(\\d\\d)(\\d\\d)";Pattern pattern = Pattern.compile(regex);Matcher matcher = pattern.matcher(content);while (matcher.find()){System.out.println(matcher.group()); // 无参时默认就是 group(0)System.out.println(matcher.group(1));System.out.println(matcher.group(2));}
输出结果:
group(0) = 1998
group(1) = 19
group(2) = 98
group(0) = 1999
group(1) = 19
group(2) = 99
group(0) = 3443
group(1) = 34
group(2) = 43
group(0) = 9889
group(1) = 98
group(2) = 89
此时,我们再从 matcher.find() 开始分析:
- 首先,根据我们给它的规则去匹配,定位到满足要求的子字符串位置(比如(19)(98))
- 然后,将子字符串的开始的索引记录到 matcher 对象的属性中去(int[] groups)。
- groups[0] = 0 , 把该子字符串结束的索引+1的值记录到 groups[1]中去,groups[1] = 4。
- 记录1组()匹配到的子串下标到 groups[2] = 0 , groups[3] = 2。
- 记录2组()匹配到的子串下标到 groups[4] = 2 , groups[5] = 4。
- 如果还有更多组,以此类推...

- 到这里,我们基本了解了分组的实现原理:groups数组负责存储子字符串的下标以及子字符串内每组子串的首尾下标,我们的 getSubSequence(groups[group * 2], groups[group * 2 + 1]) 方法会去根据 matcher.group(int group) 给的参数 group 去查找对应groups数组的首尾下标,从而调用String.substring(start,end) 截取出每组对应的子串。
不得不说,getSubSequence(groups[group * 2], groups[group * 2 + 1]),这个参数的设置确实十分巧妙!
总结
如果正则表达式中有() 即分组
取出匹配的字符串规则如下:
- group(0) 代表匹配到的子字符串,不分组
- group(1) 代表匹配到的子字符串第1组
- group(2) 代表匹配到的子字符串第2组
- ...
- 但是参数不能越界,不能超过分组数
相关文章:
Java 正则表达式【匹配与分组基本原理】
简介 我们一般使用正则表达式是用来处理字符串的,不管是实际的开发中还是我们的算法竞赛中,使用正则表达式绝对可以大大提升我们的效率。 正则表达式(regular expression)其实就是对字符串进行模式匹配的技术。 快速入门 我们这里…...
ThreadLocal源码解析
使用ThreadLocal可以为每个线程维护一个线程变量,使用场景为线程间隔离,线程内方法共享; 原理: Thread类中有一个实例属性ThreadLocalMap,ThreadLocalMap中存放的是Entry数组,Entry数组是ThreadLocal和Ob…...
RocketMQ 5.1.0 源码详解 | Producer 发送流程
文章目录 初始化DefaultMQProducer实例发送流程DefaultMQProducer#sendDefaultMQProducerImpl#sendMQClientInstance#updateTopicRouteInfoFromNameServer使用特定 topic 获取路由信息使用默认 topic 获取路由信息 DefaultMQProducerImpl#sendDefaultImpl发送流程总结 初始化De…...
电脑ip地址怎么改 ip地址怎么改到别的城市
一、ip地址怎么改到别的城市 1.ip地址怎么改到别的城市,1、重启WIFI路由设备 一般手机或电脑在家或公司上网时都是接入到路由器的WIFI网络,再由路由器分配上网IP地址,如果要更换上网IP那么重启路由器设备后,路由器会向网络运营商进行宽带的重新拨号,此时手机或电脑设…...
Android Studio实现列表展示图片
效果: MainActivity 类 package com.example.tabulation;import android.content.Intent; import android.os.Bundle; import android.view.View;import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.LinearLayoutManager; im…...
每天一道leetcode:300. 最长递增子序列(动态规划中等)
今日份题目: 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] …...
【无监督】2、MAE | 自监督模型提取的图像特征也很能打!(CVPR2022 Oral)
文章目录 一、背景二、方法三、效果 论文:Masked Autoencoders Are Scalable Vision Learners 代码:https://github.com/facebookresearch/mae 出处:CVPR2022 Oral | 何凯明 | FAIR 一、背景 本文的标题突出了两个词: masked…...
pytorch单机多卡后台运行
nohup sh ./train_chat.sh > train_chat20230814.log 2>1&参考资料 Pytorch单机多卡后台运行的解决办法...
linux配置上网 linux adsl拨号上网设置
Linux里面配置ADSL上网是件很麻烦的事。但配置完成之后就能开机自动拨号上网,可谓十分的方便。支持的系统有Redhat,CentOS,SuSE,FreeBSD,Ubuntu等常见的Linux。 工具/原料 ADSL网络,电信,网通,移动等常见宽带。 Linux系统的安装光…...
XML学习基础知识归纳(一)
一、XML基本概述 (1)概念:XML是可扩展的标记语言,xml文档的后缀名为 .xml (2)作用:用来用来传输和存储数据,不用于表现和展示数据,这点呢相比于HTML来说是不同的&#…...
2023.8.14论文阅读
文章目录 ESPNet: Efficient Spatial Pyramid of Dilated Convolutions for Semantic Segmentation摘要本文方法实验结果 DeepFusion: Lidar-Camera Deep Fusion for Multi-Modal 3D Object Detection摘要本文方法实验结果 ESPNet: Efficient Spatial Pyramid of Dilated Convo…...
FL Studio for Windows-21.1.0.3713中文直装版功能介绍及系统配置要求
FL Studio 21简称FL水果软件,全称是:Fruity Loops Studio编曲,由于其Logo长的比较像一款水果因此,在大家更多的是喜欢称他为水果萝卜,FL studio21是目前最新的版本,这是一款可以让你的计算机就像是一个全功能的录音室&…...
基于网格变形的二维图像变形算法:C++实现与应用
在计算机图形学中,图像变形是一种常见的技术,它可以改变图像的形状和结构,以满足特定的视觉效果或者应用需求。本文将介绍一种基于网格变形的二维图像变形算法,并使用C进行实现。 一、算法原理 网格变形是一种基于网格的图像变形…...
【数据结构】八大排序详解
🚀 作者简介:一名在后端领域学习,并渴望能够学有所成的追梦人。 🐌 个人主页:蜗牛牛啊 🔥 系列专栏:🛹数据结构、🛴C 📕 学习格言:博观而约取&…...
VSCode如何设置高亮
一、概述 本文主要介绍在 VSCode 看代码时,怎样使某个单词高亮显示,主要通过以下三步实现: 安装 highlight-words 插件 配置 highlight-words 插件 设置高亮快捷键F8 工作是嵌入式开发的,代码主要是C/C的,之前一直用…...
密钥大全ubuntu
VMware Workstation Tech Preview 20H2 GG1JR-APD1P-0857Q-DQQN9-PU2CA VMware Workstation v16 Pro for Windows(反馈失效) ZF3R0-FHED2-M80TY-8QYGC-NPKYF YF390-0HF8P-M81RQ-2DXQE-M2UT6 ZF71R-DMX85-08DQY-8YMNC-PPHV8 VMware Workstation v15 f…...
Spring Task入门案例
Spring Task 是Spring框架提供的任务调度工具,可以按照约定的时间自动执行某个代码逻辑。 定位:定时任务框架 作用:定时自动执行某段Java代码 强调:只要是需要定时处理的场景都可以使用Spring Task 1. cron表达式 cron表达式…...
针对Android项目蓝牙如何学习
一、概述(Overview) 蓝牙是一种专有的开放式无线技术标准,用于在固定和移动设备之间进行短距离数据交换(使用2400–2480 MHz ISM波段的短波长无线电传输),从而创建具有高度安全性的个人局域网(PANs)。由电信供应商爱立信(telecoms vendor Ericsson)于1994年创建,[1…...
C++学习笔记总结练习:内存分配器编程实现
内存分配器练习 C内存分配器是用于管理程序运行时内存的工具。它负责分配和释放内存,以满足程序在运行过程中的动态内存需求。在C中,有几种内存分配器可供选择,包括操作系统提供的默认分配器、自定义分配器和第三方库提供的分配器。 默认分配…...
【uniapp】使用Vs Code开发uniapp:
文章目录 一、使用命令行创建uniapp项目:二、安装插件与配置:三、编译和运行:四、修改pinia: 一、使用命令行创建uniapp项目: 二、安装插件与配置: 三、编译和运行: 该项目下的dist》dev》mp-weixin文件导入微信开发者…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...
C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...
【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...
算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...
