当前位置: 首页 > news >正文

掌握正则表达式:从基础到实用示例

目录

一、简单谈谈正则

二、基础知识学习

(一)正则元字符

1.特殊单字符

2.空白符

3.量词

4.范围备和选项

综合练习

(二)贪婪、非贪婪与独占模式

1.贪婪模式

2.非贪婪模式(懒惰模式)

3.独占模式

(三)分组与引用

1.捕获分组(Capturing Group)

2.非捕获分组(Non-Capturing Group)

3.命名捕获分组(Named Capturing Group)

4. 引用(Backreference)

5. 重复分组

(四)匹配模式

1.不区分大小写

2.点号通配模式

3.多行模式

4.注释模式

(五)断言(Assertion)

1.单词的边界

2.行的开始或结束

3.环视(Lookaround)

(六)正则转义

三、总结


干货分享,感谢您的阅读!

在现代软件开发中,文本处理是一个不可或缺的技能。无论是从数据清洗、用户输入验证,还是在日志分析中,能够高效地匹配和操作文本都是程序员的基本要求。在这一背景下,正则表达式(Regular Expression)作为一种强大的文本模式匹配工具,逐渐成为编程语言中的核心组件之一。

Java 语言作为广泛应用的开发工具,其内置的正则表达式支持使得处理复杂字符串任务变得更加简便。通过正则表达式,我们不仅可以实现复杂的搜索和替换,还能够对用户输入进行有效的验证,确保数据的正确性与安全性。

本篇文章旨在为读者提供一个系统的正则表达式学习指南,从基础概念入手,逐步深入到 Java 中的实际应用和最佳实践。无论你是正则表达式的新手,还是希望深化理解的开发者,本文都将为你提供丰富的示例和实用技巧,助力你在日常开发中充分发挥正则表达式的威力。

一、简单谈谈正则

正则,也称为正则表达式(Regular Expression),是一种用于文本匹配和搜索的强大工具。它是一种由字符和特殊符号组成的字符串模式,用于描述和匹配一组文本字符串,而不是固定的字符串。正则表达式可以做以下事情:

正则表达式的语法和规则因编程语言和库的不同而有所差异,但它们在处理文本数据时都具有广泛的应用。学习正则表达式可以提高文本处理和数据提取的效率,但也需要花一些时间来掌握它们的复杂性。不同编程语言和工具提供不同的正则表达式支持,所以你需要查阅相应的文档来学习如何在特定环境中使用正则表达式。

当前写的正则可以直接在线验证:正则表达式在线测试 | 菜鸟工具

二、基础知识学习

(一)正则元字符

1.特殊单字符

当描述正则表达式中的特殊单字符元字符时,可以整合为一个表格,如下所示:

元字符描述示例
.匹配除换行符 \n 之外的任何单个字符。"c.t" 可以匹配 "cat"、"cut"、"c@t" 等
\d匹配任何单个数字(0-9)。"The answer is 42" 中的 \d 可以匹配 "4" 和 "2"
\w匹配任何单个字母、数字或下划线字符(单词字符)。"word_123" 中的 \w 可以匹配 "w"、"o"、"r"、"d"、"1"、"2"、"3" 和 "_"
\s匹配任何单个空白字符,包括空格、制表符、换行符等。"Hello\tworld\n" 中的 \s 可以匹配制表符和换行符
\D匹配任何非数字字符。"Hello, World!" 中的 \D 可以匹配 ","、"H"、"e"、"l"、"l"、"o"、" "、"W" 等
\W匹配任何非单词字符。"text-with-hyphen" 中的 \W 可以匹配 "-"
\S匹配任何非空白字符。"This is text" 中的 \S 可以匹配 "T"、"h"、"i"、"s"、"i"、"s"、"t"、"e"、"x"、"t"

这个表格提供了有关这些特殊单字符元字符的简明描述以及示例用法,有助于理解它们的用途和功能。在实际使用正则表达式时,可以根据需要结合这些元字符来构建匹配模式。

我们使用java验证以上内容如下:

package org.zyf.javabasic.regex;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @program: zyfboot-javabasic* @description: 特殊单字符元字符验证* @author: zhangyanfeng* @create: 2023-11-05 00:04**/
public class SpecialSingleCharRegexExample {public static void main(String[] args) {// 使用正则表达式验证特殊单字符元字符// 创建匹配器MatcherWithInfo("1. Matches for 'c.t'", "cat cut c@t", "c.t");MatcherWithInfo("2. Matches for '\\d'", "The answer is 42", "\\d");MatcherWithInfo("3. Matches for '\\w'", "word_123", "\\w");MatcherWithInfo("4. Matches for '\\s'", "Hello\tworld\n", "\\s");// 反义元字符MatcherWithInfo("5. Matches for '\\D'", "Hello, World!", "\\D");MatcherWithInfo("6. Matches for '\\W'", "text-with-hyphen", "\\W");MatcherWithInfo("7. Matches for '\\S'", "This is text", "\\S");}// 匹配器并打印结果private static void MatcherWithInfo(String header, String text, String regex) {Pattern pattern = Pattern.compile(regex);Matcher matcher = pattern.matcher(text);StringBuilder matches = new StringBuilder();while (matcher.find()) {matches.append(matcher.group()).append(" ");}System.out.println(header);System.out.println("   Original Text: " + text);System.out.println("   Matching Result: " + (matches.length() > 0 ? matches.toString().trim() : "No Match"));System.out.println();}
}

验证结果如下:

1. Matches for 'c.t'Original Text: cat cut c@tMatching Result: cat cut c@t2. Matches for '\d'Original Text: The answer is 42Matching Result: 4 23. Matches for '\w'Original Text: word_123Matching Result: w o r d _ 1 2 34. Matches for '\s'Original Text: Hello	worldMatching Result: 5. Matches for '\D'Original Text: Hello, World!Matching Result: H e l l o ,   W o r l d !6. Matches for '\W'Original Text: text-with-hyphenMatching Result: - -7. Matches for '\S'Original Text: This is textMatching Result: T h i s i s t e x t

2.空白符

在正则表达式中,空白符通常表示为转义序列,用于匹配文本中的空格、制表符、换行符等空白字符。以下是一些常见的空白符的表示方式和它们的含义:

元字符描述
\s匹配任何单个空白字符,包括空格、制表符、换行符、回车符和换页符等。
\t匹配制表符(Tab)字符。
\n匹配换行符(newline),用于表示文本中的新行。
\r匹配回车符(carriage return),通常用于控制光标的位置。
\f匹配换页符(form feed),通常用于分页打印。
\v匹配垂直制表符(vertical tab)。
\h匹配水平空白符(horizontal whitespace),包括空格和制表符。
\v匹配垂直空白符(vertical whitespace),包括换行符、回车符等。
\S匹配任何单个非空白字符,与\s 的作用相反。

这些转义字符可以用于构建正则表达式模式,以匹配特定类型的空白符或在文本中查找空白字符。根据需求,可以选择使用这些转义字符来处理文本中的空白符。

我们使用java验证以上内容如下:

package org.zyf.javabasic.regex;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @program: zyfboot-javabasic* @description: 验证和打印正则表达式空白符元字符* @author: zhangyanfeng* @create: 2023-11-05 12:21**/
public class WhitespaceRegexExample {public static void main(String[] args) {// 使用正则表达式验证空白符元字符// 创建匹配器并打印结果MatcherWithInfo("1. Matches for '\\s':", "This is some text with spaces and tabs.", "\\s");MatcherWithInfo("2. Matches for '\\t':", "Tab\tSeparated\tText", "\\t");MatcherWithInfo("3. Matches for '\\n':", "Newline\nSeparated\nText", "\\n");MatcherWithInfo("4. Matches for '\\r':", "Carriage\rReturn", "\\r");MatcherWithInfo("5. Matches for '\\f':", "Form\fFeed", "\\f");MatcherWithInfo("6. Matches for '\\v':", "Vertical\\vTab", "\\v");MatcherWithInfo("7. Matches for '\\h':", "Horizontal hWhitespace hExample", "\\h");MatcherWithInfo("8. Matches for '\\v':", "Vertical vWhitespace vExample", "\\v");MatcherWithInfo("9. Matches for '\\S':", "This_is_non-space_text", "\\S");}// 匹配器并打印结果private static void MatcherWithInfo(String header, String text, String regex) {Pattern pattern = Pattern.compile(regex);Matcher matcher = pattern.matcher(text);StringBuilder matches = new StringBuilder();while (matcher.find()) {matches.append(matcher.group()).append(" ");}System.out.println(header);System.out.println("   Original Text: " + text);System.out.println("   Matching Result: " + (matches.length() > 0 ? matches.toString().trim() : "No Match"));System.out.println();}
}

验证结果如下:

1. Matches for '\s':Original Text: This is some text with spaces and tabs.Matching Result: 2. Matches for '\t':Original Text: Tab	Separated	TextMatching Result: 3. Matches for '\n':Original Text: Newline
Separated
TextMatching Result: 4. Matches for '\r':
ReturnMatching Result: 5. Matches for '\f':Original Text: FormFeedMatching Result: 6. Matches for '\v':Original Text: Vertical\vTabMatching Result: No Match7. Matches for '\h':Original Text: Horizontal hWhitespace hExampleMatching Result: 8. Matches for '\v':Original Text: Vertical vWhitespace vExampleMatching Result: No Match9. Matches for '\S':Original Text: This_is_non-space_textMatching Result: T h i s _ i s _ n o n - s p a c e _ t e x t

3.量词

量词是正则表达式中的元字符,用于指定某个部分的重复次数。它们允许匹配单个字符或模式的重复出现,从零次到多次。以下是一些常见的量词元字符及其含义:

元字符描述示例
*匹配前面的元素零次或多次。a* 匹配 "a"、"aa"、"aaa" 等。
+匹配前面的元素至少一次或多次。b+ 匹配 "b"、"bb"、"bbb" 等。
?匹配前面的元素零次或一次。c? 匹配 "c" 或空字符串。
{m}匹配前面的元素精确 m 次。d{3} 匹配 "ddd"。
{m,}匹配前面的元素至少 m 次。e{2,} 匹配 "ee"、"eee" 等。
{m,n}匹配前面的元素 m 到 n 次,包括 m 和 n。f{1,3} 匹配 "f"、"ff"、"fff"。

这些量词元字符使你能够更灵活地定义正则表达式,以匹配不同数量的字符或模式。它们对于匹配重复出现的文本模式非常有用,如匹配电话号码、日期、电子邮件地址等。

我们使用java验证以上内容如下:

package org.zyf.javabasic.regex;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @program: zyfboot-javabasic* @description: 验证正则表达式量词元字符* @author: zhangyanfeng* @create: 2023-11-05 12:34**/
public class QuantifierRegexExample {public static void main(String[] args) {// 使用正则表达式验证量词元字符// 创建匹配器并打印结果MatcherWithInfo("1. Matches for 'a*':", "aaaabb", "a*");MatcherWithInfo("2. Matches for 'b+':", "aaaabb", "b+");MatcherWithInfo("3. Matches for 'c?':", "aaaabb", "c?");MatcherWithInfo("4. Matches for 'd{3}':", "aaadddbb", "d{3}");MatcherWithInfo("5. Matches for 'e{2,}':", "eeeefbb", "e{2,}");MatcherWithInfo("6. Matches for 'f{1,3}':", "ffbbfffb", "f{1,3}");}// 匹配器并打印结果private static void MatcherWithInfo(String header, String text, String regex) {Pattern pattern = Pattern.compile(regex);Matcher matcher = pattern.matcher(text);StringBuilder matches = new StringBuilder();while (matcher.find()) {matches.append(matcher.group()).append(" ");}System.out.println(header);System.out.println("   Original Text: " + text);System.out.println("   Matching Result: " + (matches.length() > 0 ? matches.toString().trim() : "No Match"));System.out.println();}
}

验证结果如下:

1. Matches for 'a*':Original Text: aaaabbMatching Result: aaaa2. Matches for 'b+':Original Text: aaaabbMatching Result: bb3. Matches for 'c?':Original Text: aaaabbMatching Result: 4. Matches for 'd{3}':Original Text: aaadddbbMatching Result: ddd5. Matches for 'e{2,}':Original Text: eeeefbbMatching Result: eeee6. Matches for 'f{1,3}':Original Text: ffbbfffbMatching Result: ff fff

4.范围备和选项

范围元字符用于匹配特定范围内的字符或数字。它们允许你指定一个字符或数字必须在某个范围内才能匹配成功。以下是常见的范围元字符及其含义:

范围元字符描述示例
|表示或的关系通常用于选择多个备选项之一"apple|banana|cherry"将匹配文本中包含 "apple"、"banana" 或 "cherry" 中的任何一个
[]方括号内放置字符范围,表示匹配方括号中列出的任何字符。[aeiou] 匹配元音字母 "a"、"e"、"i"、"o" 或 "u" 中的任何一个。
[a-z]在方括号内使用连字符 - 表示一个字符范围,匹配从 "a" 到 "z" 之间的任何小写字母。[a-z] 匹配任何小写字母。
[A-Z]类似地,匹配从 "A" 到 "Z" 之间的任何大写字母。[A-Z] 匹配任何大写字母。
[0-9]匹配从 "0" 到 "9" 之间的任何数字。[0-9] 匹配任何数字。
[^]在方括号内放置字符范围的开头使用脱字符 ^ 表示反义,即匹配不在方括号中列出的字符。[^aeiou] 匹配任何非元音字母。

这些范围元字符非常有用,因为它们允许你精确地定义要匹配的字符范围,从而更灵活地处理不同类型的文本。

添加管道符 | 表示或的关系通常用于选择多个备选项之一,而不是范围元字符的一部分。范围元字符主要用于指定字符范围,而 | 主要用于选择不同的模式或备选项。

String regex = "apple|banana|cherry";

这将匹配文本中包含 "apple"、"banana" 或 "cherry" 中的任何一个。

总结来说,[] 是用于定义字符范围的元字符,而 | 是用于选择多个备选项之一的元字符。这两者在正则表达式中有不同的用途。

我们使用java验证以上内容如下:

package org.zyf.javabasic.regex;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @program: zyfboot-javabasic* @description: 验证正则表达式中的范围元字符和备选项* @author: zhangyanfeng* @create: 2023-11-05 13:39**/
public class RangeRegexExample {public static void main(String[] args) {// 使用正则表达式验证范围元字符和备选项// 创建匹配器并打印结果MatcherWithInfo("1. Matches for '[aeiou]':", "Hello, world!", "[aeiou]");MatcherWithInfo("2. Matches for '[a-z]':", "The quick brown fox", "[a-z]");MatcherWithInfo("3. Matches for '[0-9]':", "12345 and 67890", "[0-9]");MatcherWithInfo("4. Matches for 'apple|banana|cherry':", "I like cherry pie.", "apple|banana|cherry");MatcherWithInfo("5. Matches for '[^aeiou]':", "This is a test.", "[^aeiou]");}// 匹配器并打印结果private static void MatcherWithInfo(String header, String text, String regex) {Pattern pattern = Pattern.compile(regex);Matcher matcher = pattern.matcher(text);StringBuilder matches = new StringBuilder();while (matcher.find()) {matches.append(matcher.group()).append(" ");}System.out.println(header);System.out.println("   Original Text: " + text);System.out.println("   Matching Result: " + (matches.length() > 0 ? matches.toString().trim() : "No Match"));System.out.println();}
}

验证结果如下:

1. Matches for '[aeiou]':Original Text: Hello, world!Matching Result: e o o2. Matches for '[a-z]':Original Text: The quick brown foxMatching Result: h e q u i c k b r o w n f o x3. Matches for '[0-9]':Original Text: 12345 and 67890Matching Result: 1 2 3 4 5 6 7 8 9 04. Matches for 'apple|banana|cherry':Original Text: I like cherry pie.Matching Result: cherry5. Matches for '[^aeiou]':Original Text: This is a test.Matching Result: T h s   s     t s t .

综合练习

当使用正则表达式来表示手机号时,可以按照你提供的规则,编写一个更严谨的正则表达式。根据你的规则,手机号的格式可以表示为 1[3456789]\d{9},其中:

  • 1 表示第1位固定为数字1。
  • [3456789] 表示第2位可以是数字3、4、5、6、7、8或9中的任何一个。
  • \d{9} 表示第3位到第11位可以是0-9中的任意数字,并且总共需要匹配9个数字。

下面是一个使用该正则表达式的 Java 示例:

package org.zyf.javabasic.regex;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @program: zyfboot-javabasic* @description: 手机号的格式可以表示为 1[3456789]\d{9}* @author: zhangyanfeng* @create: 2023-11-05 13:59**/
public class PhoneNumberValidation {public static void main(String[] args) {String regex = "1[3456789]\\d{9}";String[] phoneNumbers = {"13912345678","18887654321","12345678901","135","1891234","158888888888"};for (String phoneNumber : phoneNumbers) {boolean isMatch = validatePhoneNumber(phoneNumber, regex);System.out.println(phoneNumber + " is a valid phone number: " + isMatch);}}private static boolean validatePhoneNumber(String phoneNumber, String regex) {Pattern pattern = Pattern.compile(regex);Matcher matcher = pattern.matcher(phoneNumber);return matcher.matches();}
}

验证结果如下:

13912345678 is a valid phone number: true
18887654321 is a valid phone number: true
12345678901 is a valid phone number: false
135 is a valid phone number: false
1891234 is a valid phone number: false
158888888888 is a valid phone number: false

(二)贪婪、非贪婪与独占模式

在正则表达式中,贪婪、非贪婪和独占模式是用来控制量词(如 *+?{n,m} 等)的匹配方式的不同选项。它们影响了正则表达式的匹配行为,以及在发现多个匹配时如何选择。这些模式用于控制量词的匹配方式,根据不同的需求和性能考虑,可以选择使用贪婪、非贪婪或独占模式来匹配文本。通常,贪婪模式是默认的,非贪婪模式和独占模式用于更精确地控制匹配行为。

1.贪婪模式

婪模式是默认的模式,它会尽可能多地匹配文本,使量词匹配尽量多的字符。例如,正则表达式 a+ 贪婪地匹配尽可能多的连续字符 "a" 直到找不到更多的 "a" 为止。

贪婪模式通常在需要匹配尽可能多的文本时使用。以下是一些使用贪婪模式的实际案例分析:

package org.zyf.javabasic.regex;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @program: zyfboot-javabasic* @description: 使用贪婪模式的实际案例分析* @author: zhangyanfeng* @create: 2023-11-05 14:14**/
public class GreedyExample {public static void main(String[] args) {// 案例 1: 提取段落文本String paragraphText = "<p>段落 1</p> <p>段落 2</p> <p>段落 3</p>";Pattern paragraphPattern = Pattern.compile("<p>(.*?)</p>");Matcher paragraphMatcher = paragraphPattern.matcher(paragraphText);System.out.println("案例 1: 提取段落文本\n文本: " + paragraphText);int paragraphNumber = 1;while (paragraphMatcher.find()) {String paragraph = paragraphMatcher.group(1);System.out.println("段落 " + paragraphNumber + ": " + paragraph);paragraphNumber++;}// 案例 2: 提取注释内容String code = "/* 这是一个注释 */ int x = 10; /* 另一个注释 */";Pattern commentPattern = Pattern.compile("/\\*(.*?)\\*/");Matcher commentMatcher = commentPattern.matcher(code);System.out.println("\n案例 2: 提取注释内容\n代码: " + code);while (commentMatcher.find()) {String comment = commentMatcher.group(1);System.out.println("注释: " + comment);}// 案例 3: 匹配 HTML 标签String htmlText = "<div class=\"container\">这是一个 <span>示例</span> HTML 文档。</div>";Pattern htmlPattern = Pattern.compile("<(.*?)>");Matcher htmlMatcher = htmlPattern.matcher(htmlText);System.out.println("\n案例 3: 匹配 HTML 标签\nHTML 文本: " + htmlText);while (htmlMatcher.find()) {String htmlTag = htmlMatcher.group(1);System.out.println("HTML 标签: " + htmlTag);}}
}

验证结果展示:

案例 1: 提取段落文本
文本: <p>段落 1</p> <p>段落 2</p> <p>段落 3</p>
段落 1: 段落 1
段落 2: 段落 2
段落 3: 段落 3案例 2: 提取注释内容
代码: /* 这是一个注释 */ int x = 10; /* 另一个注释 */
注释:  这是一个注释 
注释:  另一个注释 案例 3: 匹配 HTML 标签
HTML 文本: <div class="container">这是一个 <span>示例</span> HTML 文档。</div>
HTML 标签: div class="container"
HTML 标签: span
HTML 标签: /span
HTML 标签: /div

贪婪模式通常用于需要匹配完整块的情况,它会尽可能多地匹配文本,以便提取完整的内容。然而,需要谨慎使用贪婪模式,以避免匹配过多的文本,导致匹配不符合预期。在特定情况下,非贪婪模式或独占模式可能更合适。

2.非贪婪模式(懒惰模式)

 非贪婪模式允许最小匹配,它会尽可能少地匹配文本。非贪婪模式使用 ? 后缀来表示。例如,正则表达式 a+? 非贪婪地匹配尽可能少的 "a"。

假设你有一个包含多个 HTML 链接的文本,例如:

<a href="https://example.com">Example 1</a> <a href="https://example2.com">Example 2</a>

你想提取每个链接的 URL 和链接文本。在这种情况下,非贪婪模式非常有用,因为每个链接的 URL 和文本都在一对标签中。

以下是一个示例 Java 代码,演示如何使用非贪婪模式提取链接:

package org.zyf.javabasic.regex;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @program: zyfboot-javabasic* @description: 使用非贪婪模式提取链接* @author: zhangyanfeng* @create: 2023-11-05 14:25**/
public class NonGreedyExample {public static void main(String[] args) {String htmlText = "<a href=\"https://example.com\">Example 1</a> <a href=\"https://example2.com\">Example 2</a>";Pattern pattern = Pattern.compile("<a href=\"(.*?)\">(.*?)</a>");Matcher matcher = pattern.matcher(htmlText);while (matcher.find()) {String linkUrl = matcher.group(1);String linkText = matcher.group(2);System.out.println("URL: " + linkUrl);System.out.println("Link Text: " + linkText);}}
}

在这个案例中,我们使用正则表达式<a href=\"(.*?)\">(.*?)</a>,其中 (.*?) 表示非贪婪模式。结果如下:

URL: https://example.com
Link Text: Example 1
URL: https://example2.com
Link Text: Example 2

非贪婪模式(懒惰模式)在正则表达式中的使用需要根据具体情况和需求来决定。以下是一些使用非贪婪模式的建议分析:

  • 提取最小单元:非贪婪模式适用于需要提取文本中的最小单元的情况。例如,提取 HTML 标签中的文本、提取链接、提取注释等。它会尽量匹配最短的文本片段。

  • 避免过度匹配:在某些情况下,贪婪模式可能会导致过度匹配,匹配整个文本而不是所需的部分。使用非贪婪模式可以避免这种问题,只匹配所需的最小部分。

  • 性能考虑:非贪婪模式可能需要更多的计算资源,因为它会不断尝试匹配更短的文本。在处理大量文本或具有重复模式的文本时,性能可能受到影响。要注意性能问题。

3.独占模式

独占模式是一种相对较新的正则表达式特性,用 ++ 表示。它会尽可能多地匹配文本,但不允许回溯。回溯是在匹配失败后,重新尝试不同的组合来找到匹配的一部分。独占模式不允许回溯,这使得匹配更加高效。例如,正则表达式 a++ 独占地匹配尽可能多的 "a",但不允许回溯。

对于 Java 正则表达式来说,它不支持独占模式,所以要实现非回溯的匹配,需要使用非贪婪模式。非贪婪模式可以帮助你实现不回溯的匹配。我深感抱歉之前的回答有误。

(三)分组与引用

分组与引用是正则表达式中的重要概念,它们允许你在正则表达式中标记、捕获和引用子表达式的匹配结果。

分组是正则表达式中的一个重要概念,它允许你将一个或多个子表达式组合在一起,并将它们视为一个整体。分组有多种用途,包括捕获子表达式的匹配结果、定义重复次数、应用修饰符等。

1.捕获分组(Capturing Group)

  • 定义:使用圆括号 () 可以创建捕获分组。
  • 作用:捕获分组允许你标记和捕获子表达式的匹配结果,以便后续操作使用。被捕获的内容可以在匹配后提取和引用。
  • 编号:每个分组都有一个编号,从左到右从 1 开始递增。你可以使用编号来引用和操作分组中的内容。

假设你有一串文本,其中包含日期,并且日期的格式为 "月/日/年",例如 "12/25/2022"。你想从文本中提取日期,并分别捕获月、日和年。

(\d{1,2})/(\d{1,2})/(\d{4})

在这个正则表达式中,我们使用了三个捕获分组,分别捕获月、日和年。下面是各个分组的说明:

  1. (\d{1,2}):第一个捕获分组,用于捕获月份。这里 \d{1,2} 匹配一个或两个数字,表示月份。
  2. /:匹配日期中的斜杠分隔符。
  3. (\d{1,2}):第二个捕获分组,用于捕获日期(日)。同样,\d{1,2} 匹配一个或两个数字。
  4. /:再次匹配斜杠分隔符。
  5. (\d{4}):第三个捕获分组,用于捕获年份。\d{4} 匹配四个数字,表示年份。

在正则表达式中,捕获分组用括号 () 创建。每个捕获分组都有一个编号,从左到右从 1 开始递增。在这个示例中,我们有三个捕获分组,它们分别捕获了月、日和年。验证如下:

package org.zyf.javabasic.regex;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @program: zyfboot-javabasic* @description: 使用捕获分组来提取日期信息* @author: zhangyanfeng* @create: 2023-11-05 14:57**/
public class CapturingGroupExample {public static void main(String[] args) {String text = "Today's date is 12/25/2022, and tomorrow is 01/01/2023.";// 定义匹配日期的正则表达式模式String pattern = "(\\d{1,2})/(\\d{1,2})/(\\d{4})";// 编译正则表达式模式Pattern regex = Pattern.compile(pattern);// 创建匹配器对象Matcher matcher = regex.matcher(text);// 循环查找匹配的日期while (matcher.find()) {// 使用捕获分组提取月、日和年String month = matcher.group(1);String day = matcher.group(2);String year = matcher.group(3);// 打印提取的日期信息System.out.println("Month: " + month);System.out.println("Day: " + day);System.out.println("Year: " + year);}}
}

2.非捕获分组(Non-Capturing Group)

  • 定义:使用 (?:...) 可以创建非捕获分组。
  • 作用:非捕获分组与捕获分组类似,但不会生成捕获的结果。它主要用于分组表达式,而不需要捕获匹配结果。
  • 语法(?:pattern),其中 pattern 是子表达式。

假设你有一个文本,其中包含电子邮件地址,你想匹配电子邮件地址,但只对域名部分感兴趣,而不关心用户名。你可以使用非捕获分组来实现这个目标。

package org.zyf.javabasic.regex;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @program: zyfboot-javabasic* @description: 使用非捕获分组* @author: zhangyanfeng* @create: 2023-11-05 15:01**/
public class NonCapturingGroupExample {public static void main(String[] args) {String text = "Contact us at support@example.com or info@company.com";// 定义匹配电子邮件地址域名的正则表达式模式String pattern = "(?<=@)(?:[a-zA-Z0-9.-]+\\.)+[a-zA-Z]{2,4}";// 编译正则表达式模式Pattern regex = Pattern.compile(pattern);// 创建匹配器对象Matcher matcher = regex.matcher(text);// 查找匹配的电子邮件地址域名while (matcher.find()) {String domainName = matcher.group();System.out.println("Found domain: " + domainName);}}
}

在这个示例中,我们使用了非捕获分组"(?<=@)(?:[a-zA-Z0-9.-]+\\.)+[a-zA-Z]{2,4}"来匹配电子邮件地址的域名部分。这个非捕获分组用于分组表达式,但不会捕获域名部分的匹配结果。因此,当我们查找匹配的电子邮件地址时,只会得到完整的电子邮件地址而不包括用户名。

3.命名捕获分组(Named Capturing Group)

  • 定义:一些正则表达式引擎支持命名捕获分组。它允许为分组指定名称,以便更容易引用和理解。
  • 语法(?<name>pattern),其中 name 是分组的名称,pattern 是子表达式。

假设你有一串文本,其中包含日期,并且日期的格式为 "月/日/年",例如 "12/25/2022"。你想从文本中提取日期,并使用命名捕获分组分别捕获月、日和年。

package org.zyf.javabasic.regex;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @program: zyfboot-javabasic* @description: 使用命名捕获分组分别捕获月、日和年* @author: zhangyanfeng* @create: 2023-11-05 15:08**/
public class NamedCapturingGroupExample {public static void main(String[] args) {String text = "Today's date is 12/25/2022, and tomorrow is 01/01/2023.";// 定义匹配日期的正则表达式模式,使用命名捕获分组String pattern = "(?<month>\\d{1,2})/(?<day>\\d{1,2})/(?<year>\\d{4})";// 编译正则表达式模式Pattern regex = Pattern.compile(pattern);// 创建匹配器对象Matcher matcher = regex.matcher(text);// 查找匹配的日期while (matcher.find()) {// 使用命名捕获分组提取月、日和年String month = matcher.group("month");String day = matcher.group("day");String year = matcher.group("year");// 打印提取的日期信息System.out.println("Month: " + month);System.out.println("Day: " + day);System.out.println("Year: " + year);}}
}

在这个示例中,我们使用了命名捕获分组 (?<name>pattern) 来为捕获的结果指定名称。这里分别使用了 "month"、"day" 和 "year" 作为名称。然后,我们可以使用 matcher.group("name") 来获取相应命名捕获分组的结果。

4. 引用(Backreference)

  • 定义:引用允许你在正则表达式中引用之前捕获的内容。
  • 语法:使用 \n 来引用分组的编号,其中 n 是分组的编号。
  • 作用:引用的主要作用是在正则表达式中重用相同的文本。它通常用于查找重复出现的文本或确保两个地方的文本匹配相同。

假设你想匹配连续出现的相同单词。例如,你希望找到文本中连续重复的单词,如 "apple apple"、"cat cat cat" 等。

package org.zyf.javabasic.regex;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @program: zyfboot-javabasic* @description: 找到文本中连续重复的单词* @author: zhangyanfeng* @create: 2023-11-05 15:13**/
public class BackreferenceExample {public static void main(String[] args) {String text = "This is an example of repeated words: apple apple, cat cat cat, and dog dog dog dog dog.";// 定义匹配连续重复单词的正则表达式模式String pattern = "\\b(\\w+)(?: \\1)+\\b";// 编译正则表达式模式Pattern regex = Pattern.compile(pattern);// 创建匹配器对象Matcher matcher = regex.matcher(text);// 查找匹配的连续重复单词while (matcher.find()) {String repeatedWords = matcher.group();System.out.println("Found repeated words: " + repeatedWords);}}
}

在这个示例中,正则表达式 \\b(\\w+) \\1\\b 的解释如下:

  • \\b 表示单词边界,确保只匹配整个单词。
  • (\\w+) 是第一个捕获分组,匹配一个或多个单词字符。
  • \\1 表示引用第一个捕获分组,用来匹配与第一个捕获分组相同的文本。

这个示例会匹配文本中连续重复的单词,并输出它们,例如 "apple apple"、"cat cat cat" 和 "dog dog dog dog dog"。

这个示例会匹配文本中连续重复的单词,并输出它们。使用引用可以轻松处理这种情况,而不需要显式编写每个可能的单词。这在查找重复文本或特定模式的重复出现时非常有用。

5. 重复分组

  • 你可以使用分组来定义重复出现的子表达式。例如,(abc){3} 匹配 "abcabcabc"。
package org.zyf.javabasic.regex;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @program: zyfboot-javabasic* @description: 重复分组* @author: zhangyanfeng* @create: 2023-11-05 15:19**/
public class RepeatedGroupExample {public static void main(String[] args) {String text = "rfABCDGHabcabcabcJMMMKJBGHJNhgjjhkj";// 定义匹配重复的子表达式的正则表达式模式String pattern = "(abc){3}";// 编译正则表达式模式Pattern regex = Pattern.compile(pattern);// 创建匹配器对象Matcher matcher = regex.matcher(text);// 查找匹配的文本while (matcher.find()) {String matchedText = matcher.group();System.out.println("Found: " + matchedText);}}
}

(四)匹配模式

1.不区分大小写

匹配模式中的不区分大小写模式允许你创建正则表达式,它会匹配文本时不区分字符的大小写。这在需要匹配大小写不敏感的文本时非常有用。在正则表达式中,可以使用 (?i) 开启不区分大小写模式,或使用 (?-i) 关闭它。

  • 启用不区分大小写模式: 通过在正则表达式的开头添加 (?i) 来启用不区分大小写模式。例如,正则表达式 (?i)apple 将匹配文本中的 "apple"、"Apple"、"aPPle" 等各种大小写形式。

  • 禁用不区分大小写模式: 通过在正则表达式的开头添加 (?-i) 来禁用不区分大小写模式。在不区分大小写模式被禁用后,正则表达式将严格区分字符的大小写。

不区分大小写模式对于需要匹配文本中的标识符、关键字、名称或其他文本,而不关心其大小写形式时非常有用。例如,如果你要匹配一个用户名,用户可能以不同的大小写形式输入,但你希望都能匹配。

以下是一个示例,演示如何使用不区分大小写模式匹配用户名:

package org.zyf.javabasic.regex;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @program: zyfboot-javabasic* @description: 使用不区分大小写模式匹配用户名* @author: zhangyanfeng* @create: 2023-11-05 15:25**/
public class CaseInsensitiveExample {public static void main(String[] args) {String text = "Welcome, uSer123!";// 启用不区分大小写模式,匹配用户名Pattern pattern = Pattern.compile("(?i)user123");Matcher matcher = pattern.matcher(text);if (matcher.find()) {System.out.println("Matched: " + matcher.group());} else {System.out.println("No match");}}
}

在上面的示例中,不区分大小写模式允许正则表达式匹配 "user123",尽管文本中的 "uSer123" 使用不同的大小写。

不区分大小写模式是处理大小写不敏感匹配需求的有用工具,它可以帮助你更容易地编写灵活的正则表达式。

2.点号通配模式

匹配模式中的点号通配模式(Dot-All Mode,Single-Line Mode)允许正则表达式中的点号 . 匹配任何字符,包括换行符。默认情况下,点号 . 只匹配除换行符外的字符。点号通配模式用于需要跨越多行匹配文本的情况。

  • 启用点号通配模式: 通过在正则表达式的开头添加 (?s) 来启用点号通配模式。点号通配模式使正则表达式中的点号 . 能够匹配包括换行符在内的任何字符。

  • 禁用点号通配模式: 通过在正则表达式的开头添加 (?-s) 来禁用点号通配模式。在禁用点号通配模式后,点号 . 将只匹配除换行符外的字符,与默认行为相同。

点号通配模式通常用于处理包含多行文本的情况,例如处理包含换行符的段落、HTML 标签、或需要匹配多行注释的文本。

以下是一个示例,演示如何使用点号通配模式匹配跨越多行的 HTML 标签内容:

package org.zyf.javabasic.regex;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @program: zyfboot-javabasic* @description: 使用点号通配模式匹配跨越多行的 HTML 标签内容* @author: zhangyanfeng* @create: 2023-11-05 15:30**/
public class DotAllModeExample {public static void main(String[] args) {String html = "<div>\nThis is some text inside a div.\n</div>";// 启用点号通配模式,匹配 div 标签内容Pattern pattern = Pattern.compile("<div>(.*?)</div>", Pattern.DOTALL);Matcher matcher = pattern.matcher(html);if (matcher.find()) {String divContent = matcher.group(1);System.out.println("Div content: " + divContent);} else {System.out.println("No match");}}
}

在上面的示例中,点号通配模式允许正则表达式匹配包括换行符在内的多行 HTML <div> 标签内容。

实际上,Java 中可以通过两种方式来启用点号通配模式:

使用 (?s):直接在正则表达式中的开头添加 (?s),如下所示:

Pattern pattern = Pattern.compile("(?s)<div>(.*?)</div>");

使用 Pattern.DOTALL 标志:在编译正则表达式时,通过传递 Pattern.DOTALL 标志来启用点号通配模式,如下所示:

Pattern pattern = Pattern.compile("<div>(.*?)</div>", Pattern.DOTALL);

两种方法都可以实现点号通配模式。在前一个示例中,我使用了第二种方法,使用 Pattern.DOTALL 标志,以演示点号通配模式的应用。如果你更喜欢在正则表达式中直接添加 (?s),你可以按照第一种方法来使用它。

点号通配模式在处理需要跨越多行的文本匹配时非常有用,它使点号 . 具有更广泛的匹配能力。

3.多行模式

多行模式是一种正则表达式匹配模式,它主要影响 ^$ 这两个锚点的匹配行为。默认情况下,^ 匹配字符串的开头,而 $ 匹配字符串的结尾。启用多行模式后,这些锚点将匹配文本的每一行的开头和结尾。

  • 启用多行模式: 通过在正则表达式的开头添加 (?m) 来启用多行模式。多行模式使正则表达式中的 ^$ 锚点匹配每行文本的开头和结尾。

  • 禁用多行模式: 如果不需要多行匹配模式,可以在正则表达式的开头添加 (?-m) 来禁用它。在禁用多行模式后,^$ 仍然只匹配整个字符串的开头和结尾。

多行模式通常用于处理包含多行文本的情况,例如处理文本文件中的多行注释、日志文件或需要逐行处理的文本数据。

以下是一个示例,演示如何使用多行模式匹配包含多行注释的文本:

package org.zyf.javabasic.regex;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @program: zyfboot-javabasic* @description: 使用多行模式匹配包含多行注释的文本* @author: zhangyanfeng* @create: 2023-11-05 15:37**/
public class MultilineModeExample {public static void main(String[] args) {String text = "This is a sample text.\n" +"/* This is a multiline\n" +"   comment. */\n" +"More text.";// 启用多行模式,匹配多行注释内容Pattern pattern = Pattern.compile("/\\*.*?\\*/", Pattern.MULTILINE | Pattern.DOTALL);Matcher matcher = pattern.matcher(text);while (matcher.find()) {String comment = matcher.group();System.out.println("Multiline Comment: " + comment);}}
}

在上面的示例中,多行模式使正则表达式匹配多行注释内容,而不仅仅匹配整个文本的开头和结尾。多行模式是一种有用的匹配模式,它允许你更精确地处理多行文本。

4.注释模式

匹配模式中的注释模式(Comments Mode)允许你在正则表达式中添加注释,以提高正则表达式的可读性和维护性。注释模式允许你在正则表达式中添加注释以解释模式的目的和行为,而这些注释不会影响匹配的结果。

  • 启用注释模式: 在正则表达式中使用 (?x)(?# ... ) 来启用注释模式。(?x) 告诉正则表达式引擎要忽略空白字符(除了在字符类内部),并且允许添加注释。你可以使用 (?# ... ) 添加注释,其中 ... 是你希望添加的注释内容。

  • 注释内容:(?# ... ) 中的注释内容可以是任何描述性文本,用于解释正则表达式的目的、匹配模式或任何你认为有助于理解和维护正则表达式的信息。注释内容不会影响匹配结果,它们仅用于文档和可读性。

注释模式通常用于复杂的正则表达式,以帮助其他人或你自己更容易地理解和维护正则表达式。注释模式使正则表达式更易于阅读,特别是在包含多个子表达式和匹配模式的情况下。

以下是一个示例,演示如何使用注释模式添加注释来解释正则表达式的含义:

package org.zyf.javabasic.regex;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @program: zyfboot-javabasic* @description: 使用注释模式添加注释来解释正则表达式的含义* @author: zhangyanfeng* @create: 2023-11-05 15:41**/
public class CommentsModeExample {public static void main(String[] args) {String text = "Hello, John! My name is ZYF";// 启用注释模式,匹配问候语Pattern pattern = Pattern.compile("(?x)        # 启用注释模式\n" +"Hello,      # 匹配 Hello\n" +"\\s         # 匹配一个空白字符\n" +"John        # 匹配 John");Matcher matcher = pattern.matcher(text);if (matcher.find()) {System.out.println("Matched: " + matcher.group());} else {System.out.println("No match");}}
}

在上面的示例中,注释模式通过 (?x) 启用,允许在正则表达式中添加注释,以更清晰地解释模式的目的。注释模式是一种有用的工具,它提高了正则表达式的可读性和维护性,特别是在编写复杂的正则表达式时。

(五)断言(Assertion)

正则表达式中的断言(Assertion)是一种特殊的匹配条件,它允许你指定匹配目标的位置而不是实际字符。最常见的断言有三种:单词的边界断言、行的开始或结束断言,以及环视断言。断言是非常有用的工具,它允许你精确控制匹配的位置和条件,以满足特定的匹配需求。不同类型的断言可以用于不同的情况,使正则表达式更强大和灵活。

1.单词的边界

单词的边界断言是正则表达式中的一种断言,用于匹配单词的开始或结束位置,而不是整个单词。它通常使用特殊字符 \b 表示单词的边界,或使用 \B 表示非单词的边界。

  • \b: \b 是单词的边界断言,它匹配单词的开始或结束位置。单词被定义为由字母、数字和下划线组成的字符序列。这意味着 \b 匹配单词的起始和结束,以及单词之间的位置。例如,正则表达式 \bword\b 匹配整个单词 "word",但不匹配 "sword" 或 "words" 中的 "word"。

  • \B: \B 是非单词的边界断言,它匹配不是单词的起始或结束位置。换句话说,它匹配单词内部的位置,或者非单词字符的位置。例如,正则表达式 \Bword\B 匹配 "sword" 或 "words" 中的 "word",但不匹配整个单词 "word"。

单词的边界断言通常用于需要精确匹配整个单词的情况,而不是部分匹配。它在文本处理中常用于查找特定单词或标识符。以下是一些示例用法:

  • 匹配一个特定单词(例如,\bapple\b 匹配整个单词 "apple")。
  • 查找文本中的标识符或关键字(例如,\bif\b 匹配单词 "if" 作为关键字)。
  • 分隔文本为单词或标识符(使用 \b\w+\b 可以提取文本中的所有单词或标识符)。

单词的边界断言是正则表达式中非常有用和常见的工具,用于精确匹配文本中的单词或标识符。

在Java中使用单词边界断言来验证和匹配文本:

package org.zyf.javabasic.regex;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @program: zyfboot-javabasic* @description: 使用单词边界断言来验证和匹配文本* @author: zhangyanfeng* @create: 2023-11-05 16:16**/
public class WordBoundaryExample {public static void main(String[] args) {// 文本示例String text = "The quick brown fox jumps over the lazy dog.";// 使用\b断言匹配整个单词 "fox"Pattern pattern1 = Pattern.compile("\\bfox\\b");Matcher matcher1 = pattern1.matcher(text);while (matcher1.find()) {System.out.println("Match: " + matcher1.group());}// 使用\B断言匹配非单词边界的 "fox"Pattern pattern2 = Pattern.compile("\\Bfox\\B");Matcher matcher2 = pattern2.matcher(text);while (matcher2.find()) {System.out.println("Non-Match: " + matcher2.group());}}
}

2.行的开始或结束

断言是正则表达式中的一种特殊元字符,用于指定匹配的位置而不匹配实际字符。行的开始和行的结束断言用于匹配文本的开头和结尾。以下是关于行的开始和行的结束断言的详细介绍:

行的开始断言 ^

  • ^ 是行的开始断言,用于匹配文本的开头。
  • 例如,正则表达式 ^apple 匹配以 "apple" 开头的文本。
  • 示例:^apple 匹配 "apple pie" 中的 "apple",但不匹配 "pineapple"。

行的结束断言 $

  • $ 是行的结束断言,用于匹配文本的结尾。
  • 例如,正则表达式 apple$ 匹配以 "apple" 结尾的文本。
  • 示例:apple$ 匹配 "caramel apple" 中的 "apple",但不匹配 "apple pie"。

这些行的开始和行的结束断言非常有用,因为它们允许精确地控制匹配的位置。它们通常用于查找或验证文本的起始或终止部分,例如查找文件中以特定字符串开头或结尾的行。

下面是一个示例,演示如何在Java中使用行的开始和行的结束断言:

package org.zyf.javabasic.regex;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @program: zyfboot-javabasic* @description: 使用行的开始和行的结束断言* @author: zhangyanfeng* @create: 2023-11-05 16:28**/
public class LineBoundaryExample {public static void main(String[] args) {// 文本示例String text = "apple\nbanana\ncherry\norange";// 使用^断言匹配行的开始Pattern pattern1 = Pattern.compile("^apple", Pattern.MULTILINE);Matcher matcher1 = pattern1.matcher(text);while (matcher1.find()) {System.out.println("Match at line start: " + matcher1.group());}// 使用$断言匹配行的结束Pattern pattern2 = Pattern.compile("cherry$", Pattern.MULTILINE);Matcher matcher2 = pattern2.matcher(text);while (matcher2.find()) {System.out.println("Match at line end: " + matcher2.group());}}
}

在这个示例中,我们使用 ^ 断言来匹配以 "apple" 开头的行,并使用 $ 断言来匹配以 "cherry" 结尾的行。运行代码,你将看到以下输出:

Match at line start: apple
Match at line end: cherry

这表明成功匹配了行的开头和行的结尾。可以根据需要修改正则表达式模式以匹配不同的文本部分。

3.环视(Lookaround)

环视(Lookaround)是正则表达式中的一种高级特性,用于在匹配时查看前面或后面是否满足某种条件,但实际上不匹配这些条件。环视分为正向环视和负向环视,用于查看某些条件是否存在或不存在。

正向环视(Positive Lookaround):正向环视用于查看前面或后面是否满足某种条件,并且只有在满足条件时才匹配成功。正向环视分为正向预查(Positive Lookahead)和正向回顾(Positive Lookbehind)两种情况。

  • 正向预查(Positive Lookahead):正向预查由 (?= ...) 表示,其中 ... 是要查看的条件。它要求紧跟在这个条件后面的内容匹配成功。

  • 正向回顾(Positive Lookbehind):正向回顾由 (?<= ...) 表示,其中 ... 是要查看的条件。它要求紧跟在这个条件前面的内容匹配成功。

负向环视(Negative Lookaround):负向环视用于查看前面或后面是否不满足某种条件,并且只有在不满足条件时才匹配成功。负向环视分为负向预查(Negative Lookahead)和负向回顾(Negative Lookbehind)两种情况。

  • 负向预查(Negative Lookahead):负向预查由 (?! ...) 表示,其中 ... 是要查看的条件。它要求紧跟在这个条件后面的内容不匹配成功。

  • 负向回顾(Negative Lookbehind):负向回顾由 (?<! ...) 表示,其中 ... 是要查看的条件。它要求紧跟在这个条件前面的内容不匹配成功。

环视的主要作用是在匹配时查看前面或后面的文本内容,以便更精确地定位需要的模式。这对于复杂的匹配和替换操作非常有用。

以下是一个 Java 代码示例,演示如何在正则表达式中使用环视(Lookaround)来验证文本中的条件:

package org.zyf.javabasic.regex;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @program: zyfboot-javabasic* @description: 使用环视(Lookaround)来验证文本中的条件* @author: zhangyanfeng* @create: 2023-11-05 16:38**/
public class LookaroundExample {public static void main(String[] args) {// 文本示例String text = "The quick brown fox jumps over the lazy dog.";// 正向预查示例:匹配包含 "fox" 后面是 "jumps" 的情况Pattern pattern1 = Pattern.compile("fox(?= jumps)");Matcher matcher1 = pattern1.matcher(text);while (matcher1.find()) {System.out.println("Positive Lookahead: " + matcher1.group());}// 负向预查示例:匹配包含 "fox" 后面不是 "lazy" 的情况Pattern pattern2 = Pattern.compile("fox(?! lazy)");Matcher matcher2 = pattern2.matcher(text);while (matcher2.find()) {System.out.println("Negative Lookahead: " + matcher2.group());}// 正向回顾示例:匹配包含 "fox" 前面是 "quick" 的情况text = "The quick fox jumps over the lazy dog.";Pattern pattern3 = Pattern.compile("(?<=quick )fox");Matcher matcher3 = pattern3.matcher(text);while (matcher3.find()) {System.out.println("Positive Lookbehind: " + matcher3.group());}// 负向回顾示例:匹配包含 "fox" 前面不是 "brown" 的情况Pattern pattern4 = Pattern.compile("(?<!brown )fox");Matcher matcher4 = pattern4.matcher(text);while (matcher4.find()) {System.out.println("Negative Lookbehind: " + matcher4.group());}}
}

这个示例展示了正向预查、负向预查、正向回顾和负向回顾的使用。

(六)正则转义

正则表达式中的转义用于指示正则表达式引擎将某些字符解释为特殊字符而不是普通字符。正则表达式中的特殊字符通常用于表示匹配规则和模式。以下是一些常见的正则表达式中需要转义的特殊字符:

  1. \: 反斜杠(\)用于转义紧跟其后的字符,使其变成普通字符。例如,\. 表示匹配点号而不是匹配任何字符。

  2. ^: 插入符(^)通常用于表示匹配行的开头,但在字符类(方括号内)中,它表示反义。要匹配插入符字符本身,需要使用 \^

  3. $: 美元符号($)通常用于表示匹配行的结尾。要匹配美元符号字符本身,需要使用 \$

  4. .: 点号(.)通常用于表示匹配除换行符之外的任何字符。要匹配点号字符本身,需要使用 \.

  5. *, +, ?, {, }: 这些字符通常用于表示量词,指定前面的字符可以重复多少次。要匹配这些字符本身,需要使用 \*, \+, \?, \{, \}

  6. |: 竖线(|)通常用于表示或操作,匹配两个选项中的一个。要匹配竖线字符本身,需要使用 \|

  7. (): 圆括号通常用于分组表达式。要匹配圆括号字符本身,需要使用 \(\)

  8. []: 方括号通常用于定义字符类,匹配其中的任何字符。要匹配方括号字符本身,需要使用 \[\]

  9. \: 反斜杠本身需要进行转义,因此要匹配反斜杠字符本身,需要使用 \\

要使用这些特殊字符作为普通字符进行匹配,只需在它们前面添加反斜杠进行转义。这样正则表达式引擎会将它们视为普通字符而不是特殊字符。

以下是一个Java类,用于验证正则表达式中的转义字符:

package org.zyf.javabasic.regex;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @program: zyfboot-javabasic* @description: 验证正则表达式中的转义字符* @author: zhangyanfeng* @create: 2023-11-05 16:48**/
public class RegexEscapeExample {public static void main(String[] args) {// 要匹配的文本String text = "This is a dot (.) and a question mark (?).";// 使用正则表达式匹配点号和问号,需要转义这两个字符String regex = "\\.";Pattern pattern = Pattern.compile(regex);Matcher matcher = pattern.matcher(text);System.out.println("Matching dots:");while (matcher.find()) {System.out.println("Match: " + matcher.group());}// 使用正则表达式匹配问号regex = "\\?";pattern = Pattern.compile(regex);matcher = pattern.matcher(text);System.out.println("\nMatching question marks:");while (matcher.find()) {System.out.println("Match: " + matcher.group());}}
}

在这个示例中,我们创建了一个Java类 RegexEscapeExample,并使用正则表达式来匹配文本中的点号和问号。由于点号和问号在正则表达式中是特殊字符,所以需要使用反斜杠进行转义,即 \\.\\?

三、总结

正则表达式作为一种强大的文本处理工具,在 Java 开发中扮演着至关重要的角色。从基本的模式匹配到复杂的文本处理,正则表达式提供了一种灵活而高效的方法来处理字符串数据。本文通过介绍正则表达式的基本概念、语法规则及其在 Java 中的应用示例,帮助读者建立了对这一工具的全面理解。

在实际开发中,合理使用正则表达式能够极大地提高文本处理的效率和准确性。然而,正则表达式的强大也伴随着复杂性,初学者可能会感到难以驾驭。因此,建议开发者在实际应用中逐步积累经验,结合具体的需求来设计和优化正则表达式。

最后,随着文本数据处理需求的不断增加,掌握正则表达式不仅可以提升个人的编程能力,也将为团队的项目开发提供更多的可能性。希望本文能够激励更多开发者深入探索正则表达式的世界,从而在日常编码中更好地利用这一工具。

相关文章:

掌握正则表达式:从基础到实用示例

目录 一、简单谈谈正则 二、基础知识学习 &#xff08;一&#xff09;正则元字符 1.特殊单字符 2.空白符 3.量词 4.范围备和选项 综合练习 &#xff08;二&#xff09;贪婪、非贪婪与独占模式 1.贪婪模式 2.非贪婪模式&#xff08;懒惰模式&#xff09; 3.独占模式…...

Python 中列表(List)、元组(Tuple)、集合(Set)和字典(Dict)四大数据结构的完整对比

以下是 Python 中列表&#xff08;List&#xff09;、元组&#xff08;Tuple&#xff09;、集合&#xff08;Set&#xff09;和字典&#xff08;Dict&#xff09;四大数据结构的完整对比分析&#xff0c;结合了核心特性、操作方式和应用场景的深度总结&#xff1a; 一、核心特性…...

LK光流和特征点的关系

uv方程 光流有两个假设&#xff1a; 1.亮度恒定&#xff0c;即图像相同位置的灰度短时不变。两帧中对应像素灰度/亮度相同 2.时间持续性&#xff08;微小移动&#xff09;&#xff0c;这意味着时间的变化不会引起像素位置的剧烈变化&#xff0c;这样像素的灰度值才能对位置求…...

Rocky Linux 9.5中完美迁移mysql5.6.17到mysql5.7.11

首先Rocky Linux 9.5中&#xff0c;默认官方建议使用的是mysql8.0&#xff0c;项目要兼容以往数据&#xff0c;经过测试跟mysql5.7.11能做兼容。 一&#xff1a;工具准备以及安装步骤 1、官网下载地址&#xff1a;https://downloads.mysql.com/archives/community/ 下载版本…...

练习题:113

目录 Python题目 题目 题目分析 需求理解 关键知识点 实现思路分析 代码实现 代码解释 定义列表&#xff1a; for 循环遍历列表&#xff1a; 输出元素&#xff1a; 运行思路 结束语 Python题目 题目 使用for循环遍历一个列表并输出每个元素。 题目分析 需求理…...

文件上传存储安全OSS 对象分站解析安全解码还原目录执行

# 文件 - 解析方案 - 执行权限 & 解码还原 1 、执行权限 文件上传后存储目录不给执行权限 2 、解码还原 数据做存储&#xff0c;解析固定(固定协议&#xff09;&#xff08;文件后缀名无关&#xff09; 文件上传后利用编码传输解码还原 # 文件 - 存储方案 - 分站存储…...

NUUO摄像头debugging_center_utils命令执行漏洞

免责声明&#xff1a;本号提供的网络安全信息仅供参考&#xff0c;不构成专业建议。作者不对任何由于使用本文信息而导致的直接或间接损害承担责任。如涉及侵权&#xff0c;请及时与我联系&#xff0c;我将尽快处理并删除相关内容。 漏洞描述 NUUO NVR是中国台湾省NUUO公司旗…...

华为OD机试2025A卷 - 正则表达式替换(Java Python JS C++ C )

最新华为OD机试 真题目录:点击查看目录 华为OD面试真题精选:点击立即查看 题目描述 为了便于业务互交,约定一个对输入的字符串中的下划线做统一替换。 具体要求如下: 输入字符串,将其中包含的每一个下划线“_”,使用特殊字符串(^|$|[,+])替换,并输出替换后的结果…...

2025-3-30算法打卡

一&#xff0c;小明的彩灯 1.题目描述&#xff1a; 题目描述 小明拥有 NN 个彩灯&#xff0c;第 ii 个彩灯的初始亮度为 aiai​。 小明将进行 QQ 次操作&#xff0c;每次操作可选择一段区间&#xff0c;并使区间内彩灯的亮度 xx&#xff08;xx 可能为负数&#xff09;。 求…...

【今日半导体行业分析】2025年3月30日

今日探针卡行业分析&#xff1a;把握机遇&#xff0c;应对挑战 一、引言 在半导体产业的精密制造流程中&#xff0c;探针卡作为晶圆测试环节的核心设备&#xff0c;犹如一颗精密的 “心脏”&#xff0c;承担着芯片封装前电学性能测试与筛选的重任。其性能的优劣直接关系到芯片…...

深度学习Note.5(机器学习2)

多项式回归 1.与线性回归联系&#xff1a; 与线性回归大体相似&#xff0c;代码部分复用性高&#xff0c;不同点&#xff1a;公式中为x的次方&#xff0c;可能要规范化处理。 2.paddle的API paddle.sin(x, nameNone) 功能&#xff1a;计算输入的正弦值。 输入&#xff1a;输入…...

三、分类模块,通用组件顶部导航栏Navbar

1.封装通用组件顶部导航栏Navbar 不同效果 Component export struct MkNavbar {Prop title: string Prop leftIcon: ResourceStr $r("app.media.ic_public_left")ProprightIcon: ResourceStr $r("app.media.ic_public_more")PropshowLeftIcon: boolean…...

PipeWire 音频设计与实现分析三——日志子系统

日志子系统 PipeWire 的日志子系统的设计分为多个层次。PipeWire 用 struct spa_log 对象描述日志组件&#xff0c;用 struct spa_log_methods 对象描述日志组件打印各层级日志的多个方法。PipeWire 为日志子系统添加了 topic 机制&#xff0c;不同文件中的日志按功能以不同的…...

playwright解决重复登录问题,通过pytest夹具自动读取storage_state用户状态信息

playwright解决重复登录问题&#xff0c;通过pytest夹具自动读取storage_state用户状态信息 conftest.py文件下封装两个夹具夹具一&#xff1a;将storage_state登录状态导出为json文件夹具二&#xff1a;重写夹具browser_context_args&#xff0c;添加storage_state登录状态登录…...

Codeforces Round 1014 (Div. 2)(A-D)

题目链接&#xff1a;Dashboard - Codeforces Round 1014 (Div. 2) - Codeforces A. Kamilka and the Sheep 思路 最大值-最小值 代码 void solve(){int n;cin>>n;vi a(n10);int mx0;int miinf;for(int i1;i<n;i){cin>>a[i];mimin(mi,a[i]);mxmax(mx,a[i])…...

vulhub靶场—— Tomcat8

目录 一、漏洞描述 二、靶场搭建 三、漏洞复现 1、弱密码 2、文件上传 一、漏洞描述 环境描述&#xff1a; Tomcat 支持后台部署 war 文件&#xff0c;可以直接将 webshell 部署到 web 目录下。tomcat 默认的管理页面 manager 使用 basic 认证用户名和密码登录&#xff0…...

c# ftp上传下载 帮助类

工作中FTP的上传和下载还是很常用的。如下载打标数据,上传打标结果等。 这个类常用方法都有了:上传,下载,判断文件夹是否存在,创建文件夹,获取当前目录下文件列表(不包括文件夹) ,获取当前目录下文件列表(不包括文件夹) ,获取FTP文件列表(包括文件夹), 获取当前目…...

Redis 哈希表结构详解

Redis 哈希表结构详解 相关链接 redis中 hashtable的 sizemask理解 一、核心结构体定义与作用 Redis 的哈希表实现基于 链表法解决冲突&#xff0c;并采用 渐进式 rehash 策略。其核心结构体包括 dictEntry、dictht 和 dict&#xff0c;三者协作实现高效的键值对存储。 二、结…...

接口等幂处理

介绍 ✅ 什么是等幂&#xff08;Idempotency&#xff09;&#xff1f; 等幂 无论这个操作被执行多少次&#xff0c;结果都是一样的&#xff0c;不会因为多次执行而产生副作用。 通俗一点说&#xff1a;“点一次和点一百次&#xff0c;效果是一样的。” ✅ 在接口中&#xff0…...

华为配置篇-BGP实验

BGP 一、简述二、常用命令总结三、实验 一、简述 二、常用命令总结 display bgp peer #查看 BGP 对等体 display bgp routing-table #查看 BGP 路由表#在R1上通过 network 命令发布路由 [R1]bgp 64513 [R1-bgp] network 10.1.1.1 24#在R2上将路由的下一跳地址修改为自身 [R2]…...

【Tauri2】008——简单说说配置文件

前言 配置文件&#xff0c;即tauri.conf.json Configuration Files | Taurihttps://tauri.app/zh-cn/develop/configuration-files/这个文件的作用 该文件由 Tauri 运行时和 Tauri CLI 使用。你可以定义构建设置&#xff08;例如在 tauri build 或 tauri dev 启动前运行的命令…...

Java学习笔记1——编程基础

一、整数类型变量 注意&#xff1a;每个字符型常量占两个字节 二、自动类型转换和强制类型转换 ​​​三、算术运算符 四、赋值运算符 五、比较运算符 六、逻辑运算符 七、运算符的优先级 运算符的优先级可以通过以下口诀来记忆&#xff1a; 括号优先&#xff0c;单目次之&am…...

CMD/DOS和批处理入门知识汇总

0、前言&#xff1a; 在工作中&#xff0c;有时候需要涉及到window系统更底层的一些东西&#xff0c;所以需要学习一些cmd指令和dos命令&#xff0c;来完成高效批处理任务&#xff0c;或者自动化办公。还有想要对系统中文件管理有更细致的认识&#xff0c;便于请理磁盘文件。后…...

Visual Studio 2019 Qt QML 项目环境搭建常见问题处理方法

在 Visual Studio 2019 运行 Qt/QML 项目比直接使用QtCreator环境麻烦&#xff0c;主要是有qmake 的一些配置项不能在 Visual Studio中设置。下面整理一些常见问题的处理方法&#xff0c;供参考&#xff1a; 搭建VS Qt 环境&#xff0c;在Visual Studios 2019下面安装 Qt Vis…...

Python-Django入手

18.1 建立项目 18.1.1 制定规范 - 定义项目目标&#xff1a;明确应用的核心功能 - 创建项目文档&#xff1a;用README.md记录技术栈和开发流程 - 规划目录结构&#xff1a;建议遵循Django官方推荐的项目布局 18.1.2 建立虚拟环境 在命令行执行&#xff1a; python -m ven…...

SakuraCat(2)Endpoint

Endpoint 功能概述 监听指定端口&#xff08;默认是 8080&#xff09;的客户端连接。接受客户端连接后&#xff0c;为每个连接创建一个新的线程进行处理。使用 Processor 类来处理客户端的请求和响应。 package com.SakuraCat.connector.protocolHandler;import com.SakuraC…...

19914 最小生成树2

19914 最小生成树2 ⭐️难度&#xff1a;中等 &#x1f31f;考点&#xff1a;最小生成树 &#x1f4d6; &#x1f4da; import java.util.*;public class Main {static class Edge{int u,v,w;Edge(int u,int v,int w){this.u u;this.v v;this.w w;}}static ArrayList<…...

SSE服务器主动推送至浏览器客户端,让你不再需要websocket

Server-Sent Events&#xff08;SSE&#xff09;是一种服务器向客户端推送实时更新的技术&#xff0c;基于HTTP协议。客户端通过EventSource API来接收事件流&#xff0c;而服务器则保持一个长连接&#xff0c;持续发送数据。这与传统的请求-响应模式不同&#xff0c;允许服务器…...

C++中的搜索算法实现

C中的搜索算法实现 在编程中&#xff0c;搜索算法是解决各种问题的基础工具之一。C作为一种功能强大的编程语言&#xff0c;提供了多种实现搜索算法的方式。本文将详细介绍两种常见的搜索算法&#xff1a;线性搜索和二分搜索&#xff0c;并通过代码示例展示它们的实现。 一、…...

OSI 七层模型和四层模型(TCP/IP 模型)

文章目录 前言一、OSI 七层模型二、TCP/IP 四层模型三、运行协议及设备1. OSI 七层模型2. TCP/IP 四层模型3. 运行协议4. 各类设备的作用 总结 前言 OSI 七层模型和四层模型&#xff08;TCP/IP 模型&#xff09;是两种常见的网络协议分层架构&#xff0c;它们的主要区别如下&a…...

将代理连接到 Elasticsearch 使用模型上下文协议

作者&#xff1a;来自 Elastic Jedr Blaszyk 及 Joe McElroy 让我们使用 Model Context Protocol 服务器 与 你的 数据 在 Elasticsearch 中聊天。 如果与你的数据交互像与同事聊天一样轻松&#xff0c;会怎样&#xff1f;想象一下&#xff0c;你只需简单地问&#xff1a;“显…...

前端调试实践与案例场景

前端调试实践与案例场景 前端开发中&#xff0c;调试是一项必不可少的技能。以下是一些常见的前端调试实践和相应的案例场景&#xff1a; 1. 浏览器开发者工具 案例场景&#xff1a;布局问题 用户报告在移动设备上页面布局错乱。使用 Chrome DevTools 的设备模拟功能和 Ele…...

安卓的布局方式

一、RelativeLayout 相对布局 特点&#xff1a;每个组件相对其他的某一个组件进行定位。 (一)主要属性 1、设置和父组件的对齐&#xff1a; alignParentTop &#xff1a; 设置为true&#xff0c;代表和父布局顶部对齐。 其他对齐只需要改变后面的Top为 Left、Right 或者Bottom&…...

计算机网络面经(一)

以下为个人总结&#xff0c;图源大部分会来自网络和JavaGuide 网络分层模型 OSI七层模型 各层的常见协议 应用层 用户接口 HTTP, FTP, SMTP, DNS表示层 数据格式转换 SSL/TLS, JSON, JPEG会话层 会话管理 NetBIOS, RPC, SSH传输层 端到端通信 TCP, UDP, QUIC网络层 路由寻址…...

k8s日志管理

k8s日志管理 k8s查看日志查看集群中不是完全运行状态的pod查看deployment日志查看service日志进入pod的容器内查看日志 管理k8s组件日志kubectl logs查看日志原理 管理k8s应用日志收集k8s日志思路收集标准输出收集容器中日志文件 k8s查看节点状态失败k8s部署prometheus监控 k8s…...

Netty源码—10.Netty工具之时间轮二

大纲 1.什么是时间轮 2.HashedWheelTimer是什么 3.HashedWheelTimer的使用 4.HashedWheelTimer的运行流程 5.HashedWheelTimer的核心字段 6.HashedWheelTimer的构造方法 7.HashedWheelTimer添加任务和执行任务 8.HashedWheelTimer的完整源码 9.HashedWheelTimer的总结…...

Baklib激活企业知识管理新动能

Baklib核心技术架构解析 Baklib的底层架构以模块化设计为核心&#xff0c;融合知识中台的核心理念&#xff0c;通过分布式存储引擎与智能语义分析系统构建三层技术体系。数据层采用多源异构数据接入协议&#xff0c;支持文档、音视频、代码片段等非结构化数据的实时解析与分类…...

CSP-J/S冲奖第21天:插入排序

一、插入排序概念 1.1 生活中的类比 • 扑克牌排序&#xff1a;就像整理手中的扑克牌&#xff0c;每次将一张牌插入到已排好序的牌中合适位置 • 动态演示&#xff1a; 初始序列&#xff1a;[5, 2, 4, 6, 1, 3] 排序过程&#xff1a; → [2, 5, 4, 6, 1, 3] → [2, 4, 5, 6, …...

Jest系列二之基础实践

Jest基础实践 官方文档地址&#xff1a;https://jest.nodejs.cn/docs 生命周期 在 Jest 中&#xff0c;生命周期方法大致分为两类&#xff1a;下面所罗列的生命周期方法&#xff0c;也是全局方法&#xff0c;不需要引入&#xff0c;直接就可以使用。 重复性的生命周期方法&…...

Scikit-learn全攻略:从入门到工业级应用

Scikit-learn全攻略:从入门到工业级应用 引言:Scikit-learn在机器学习生态系统中的核心地位 Scikit-learn作为Python最受欢迎的机器学习库,已成为数据科学家的标准工具集。根据2023年Kaggle调查报告,超过83%的数据专业人士在日常工作中使用Scikit-learn。本文将系统性地介…...

基于Python的图书馆信息管理系统研发

标题:基于Python的图书馆信息管理系统研发 内容:1.摘要 在数字化信息快速发展的背景下&#xff0c;传统图书馆管理方式效率低下&#xff0c;难以满足日益增长的信息管理需求。本研究旨在研发一款基于Python的图书馆信息管理系统&#xff0c;以提高图书馆信息管理的效率和准确性…...

Pytorch学习笔记(十七)Image and Video - Adversarial Example Generation

这篇博客瞄准的是 pytorch 官方教程中 Image and Video 章节的 Adversarial Example Generation 部分。 官网链接&#xff1a;https://pytorch.org/tutorials/beginner/fgsm_tutorial.html 完整网盘链接: https://pan.baidu.com/s/1L9PVZ-KRDGVER-AJnXOvlQ?pwdaa2m 提取码: …...

基于Arm GNU Toolchain编译生成的.elf转hex/bin文件格式方法

基于Arm GNU Toolchain编译生成的.elf转hex/bin文件格式方法 已经弃用的版本&#xff08;Version 10.3-2021.10&#xff09;&#xff1a;gcc-arm-none-eabi&#xff1a;https://developer.arm.com/downloads/-/gnu-rmArm GNU Toolchain当前版本&#xff1a;https://developer.a…...

Ubuntu系统Docker安装失败

问题&#xff1a; 1. 删除错误的 Docker 源 sudo rm -rf /etc/apt/sources.list.d/docker.list sudo rm -rf /etc/apt/keyrings/docker.gpg 2. 重新添加 Docker 官方 GPG 密钥 ​ sudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | …...

鸿蒙学习手册(HarmonyOSNext_API16)_数据持久化②:键值型数据库

概述 键值型数据库就像一个大抽屉柜&#xff0c;每个抽屉都有一个唯一的标签&#xff08;键&#xff09;&#xff0c;里面可以放任何东西&#xff08;值&#xff09;。当你需要存或取东西时&#xff0c;直接看标签拿对应的抽屉就行&#xff0c;不用管其他抽屉里有什么。这种简…...

多线程 - 线程安全 2 -- > 死锁问题

目录 小结复习&#xff1a; 线程安全&#xff1a; 如何解决线程安全问题&#xff1f; synchronized “死锁” 死锁的三种经典场景&#xff1a; 1. 一个线程&#xff0c;一把锁。 2.两个线程&#xff0c;两把锁。 3. N 个线程 M 把锁 完&#xff01; 小结复习&#xff1a…...

JavaScript函数详解

目录 一、函数的基础概念 1. 函数的定义方式 2. 函数的参数处理 3.匿名函数与立即执行函数 4.同名函数与函数提升 二、函数的作用域与闭包 1. 作用域&#xff08;Scope&#xff09; 2. 闭包&#xff08;Closure&#xff09; 三、高阶函数与函数式编程 1. 高阶函数 2…...

Python-八股总结

目录 1 python 垃圾处理机制2 yield3 python 多继承&#xff0c;两个父类有同名方法怎么办&#xff1f;4 python 多线程/多进程/协程4.1 多线程与GIL全局解释器锁4.2 多进程4.3 协程 5 乐观锁/悲观锁6 基本数据结构**1. 列表&#xff08;List&#xff09;****2. 元组&#xff0…...

整合分块请求大模型返回的测试用例及小工具显示bug修复

在之前的分块发送需求数据给大模型进行测试用例生成时&#xff0c;由于数据结构的改变&#xff0c;需要对分块的回复进行整合&#xff0c;正确的整合是保障系统稳定性和功能正确性的核心。随着测试需求的复杂化&#xff0c;这对测试工程师提出了更高的整合和管理要求。本文将为…...

记一道CTF题—PHP双MD5加密+”SALT“弱碰撞绕过

通过分析源代码并找到绕过限制的方法&#xff0c;从而获取到flag&#xff01; 部分源码&#xff1a; <?php $name_POST[username]; $passencode(_POST[password]); $admin_user "admin"; $admin_pw get_hash("0e260265122865008095838959784793");…...