JavaRush /Java 博客 /Random-ZH /Java 中的正则表达式 (RegEx)

Java 中的正则表达式 (RegEx)

已在 Random-ZH 群组中发布
正则表达式是程序员(即使是经验丰富的程序员)经常推迟到以后再讨论的话题。然而,大多数 Java 开发人员迟早要处理文本处理。最常见的是 - 在文本中进行搜索操作和编辑。如果没有正则表达式,与文本处理相关的高效且紧凑的程序代码简直是不可想象的。所以别再拖延了,我们现在就来处理“常客”吧。这并不是一项艰巨的任务。

RegEx 正则表达式是什么?

事实上,正则表达式(Java 中的 RegEx)是一种在文本中搜索字符串的模式。在Java中,这种模式的初始表示总是一个字符串,即String类的对象。但是,并不是任何字符串都可以编译为正则表达式,只有那些遵循正则表达式编写规则(语言规范中定义的语法)的字符串才可以编译为正则表达式。要编写正则表达式,需要使用字母和数字字符以及元字符 - 在正则表达式语法中具有特殊含义的字符。例如:
String regex = "java"; // string template "java";
String regex = "\\d{3}"; // string template of three numeric characters;

在 Java 中创建正则表达式

要在 Java 中创建 RegEx,您需要遵循两个简单的步骤:
  1. 使用正则表达式语法将其写为字符串;
  2. 将此字符串编译为正则表达式;
在任何 Java 程序中使用正则表达式都是从创建类对象开始的Pattern。为此,您需要调用类中可用的两个静态方法之一compile。第一个方法采用一个参数 - 正则表达式的字符串文字,第二个 - 加上另一个参数,用于打开将模板与文本进行比较的模式:
public static Pattern compile (String literal)
public static Pattern compile (String literal, int flags)
可能的参数值列表flags是在类中定义的Pattern,并且可以作为静态类变量提供给我们。例如:
Pattern pattern = Pattern.compile("java", Pattern.CASE_INSENSITIVE);//searching for matches with the pattern will be done case-insensitively.
本质上,该类Pattern是一个正则表达式构造函数。在底层,该方法compile调用类的私有构造函数Pattern来创建编译视图。实现这种创建模板实例的方法的目的是将其创建为不可变对象。创建时,会执行正则表达式的语法检查。如果该行中有错误,则会生成异常PatternSyntaxException

正则表达式语法

正则表达式语法基于符号的使用<([{\^-=$!|]})?*+.>,符号可以与字母字符组合。根据他们的角色,他们可以分为几组:
1. 用于匹配行边界或文本的元字符
元字符 目的
^ 行首
$ 行结束
\b 字边界
\B 没有字数限制
\A 输入开始
\G 上一场比赛结束
\Z 输入结束
\z 输入结束
2. 用于搜索字符类的元字符
元字符 目的
\d 数字符号
\D 非数字字符
\s 空格字符
\S 非空白字符
\w 字母数字字符或下划线
\W 除字母、数字或下划线之外的任何字符
任何字符
3. 用于搜索文本编辑符号的元字符
元字符 目的
\t 制表符
\n 换行符
\r 回车符
\F 转到新页面
\u0085 下一行字符
\u 2028 行分隔符
\u 2029 段落分隔符
4. 用于对字符进行分组的元字符
元字符 目的
[乙丙] 上述任何一项(a、b 或 c)
[^abc] 列出的以外的任何其他内容(非 a、b、c)
[a-zA-Z] 范围合并(拉丁字符 a 到 z 不区分大小写)
[广告[mp]] 字符串联(a 到 d 和 m 到 p)
[az&&[def]] 符号的交集(符号 d、e、f)
[az&&[^bc]] 减去字符(字符 a、dz)
5. 表示字符数量的元符号——量词。量词始终位于一个字符或一组字符之后。
元字符 目的
一个或缺失
* 零次或多次
+ 一次或多次
{n} n次
{n,} n次或以上
{n,m} 不少于n次且不多于m次

贪婪量词模式

量词的一个特殊功能是能够以不同的模式使用它们:贪婪、超贪婪和惰性。超贪婪模式+通过在量词后添加符号“ ”开启,惰性模式通过添加符号“ ?”开启。例如:
"A.+a" // greedy mode
"A.++a" // over-greedy mode
"A.+?a" // lazy mode
使用此模板作为示例,让我们尝试了解量词在不同模式下的工作方式。默认情况下,量词以贪婪模式运行。这意味着它会在字符串中查找最长的可能匹配。运行此代码的结果是:
public static void main(String[] args) {
    String text = "Egor Alla Alexander";
    Pattern pattern = Pattern.compile("A.+a");
    Matcher matcher = pattern.matcher(text);
    while (matcher.find()) {
        System.out.println(text.substring(matcher.start(), matcher.end()));
    }
}
我们将得到以下输出: Alla Alexa 给定模式“ ”的搜索算法А.+а按以下顺序执行:
  1. 在给定的模式中,第一个字符是俄语字母字符АMatcher从位置零开始,将其与文本的每个字符进行匹配。在文本中的位置 0 处有一个符号Е,因此Matcher它会按顺序遍历文本中的字符,直到遇到与模式的匹配项。在我们的示例中,这是位置 5 处的符号。

    Java 中的正则表达式 - 2
  2. 找到与模式的第一个字符的匹配后,Matcher它会检查与模式的第二个字符的匹配。在我们的例子中,这是符号“ .”,它代表任何字符。

    Java 中的正则表达式 - 3

    第六位是字母符号л。当然,它匹配“任何字符”模式。

  3. Matcher继续检查模式中的下一个字符。在我们的模板中,它是使用“ .+”量词指定的。由于模式中“任意字符”的重复次数为一次或多次,因此Matcher只要满足“任意字符”条件,它就会依次从字符串中取出下一个字符并检查其是否符合模式,在我们的示例中 - 直到行尾(从文本的第 7 号位置到第 18 号位置)。

    Java 中的正则表达式 - 4

    事实上,Matcher它把整条线都抓到了最后——这就是它的“贪婪”体现的地方。

  4. Matcher到达文本末尾并完成对模式的“ ”部分的检查后А.+,Matcher 开始检查模式的其余部分 - 字母字符а。由于正向的文本已经结束,因此检查发生在反向,从最后一个字符开始:

    Java 中的正则表达式 - 5
  5. Matcher“记住”模式“ .+”到达文本末尾时的重复次数,因此它将重复次数减少一并检查文本的模式,直到找到匹配项: Java 中的正则表达式 - 6

超贪婪量词模式

在超贪婪模式下,匹配器的工作方式与贪婪模式机制类似。不同之处在于,当您将文本抓取到行尾时,不会向后搜索。也就是说,超贪婪模式的前三个阶段将与贪婪模式类似。捕获整个字符串后,匹配器添加模式的其余部分并将其与捕获的字符串进行比较。在我们的示例中,当使用模式“ А.++а”执行 main 方法时,将找不到匹配项。 Java 中的正则表达式 - 7

惰性量词模式

  1. 在此模式下,在初始阶段,与贪婪模式一样,将寻求与模式的第一个字符的匹配:

    Java 中的正则表达式 - 8
  2. 接下来,它会查找与模式中的下一个字符(任何字符)的匹配项:

    Java 中的正则表达式 - 9
  3. 与贪婪模式不同,惰性模式会搜索文本中的最短匹配,因此在找到与模式的第二个字符(由点指定并与文本中第 6 号位置的字符匹配)的匹配后,Matcher将检查文本是否与模式的其余部分匹配 - “ а”字符。

    Java 中的正则表达式 - 10
  4. 由于未找到与文本中的模式匹配的内容(文本中的第 7 位置有符号“ ” л),Matcher因此在模式中添加另一个“任意字符”,因为它被指定为一次或多次,再次将图案与第 5 号到第 8 号位置的文本进行比较:

    Java 中的正则表达式 - 11
  5. 在我们的例子中,找到了匹配项,但尚未到达文本末尾。因此,从位置 9 开始,检查首先使用类似的算法搜索模式的第一个字符,然后重复直到文本结尾。

    Java 中的正则表达式 - 12
通过该方法,main当使用“ А.+?а”模板时,我们将得到以下结果: Alla Alexa 从我们的示例中可以看出,当对同一模板使用不同的量词模式时,我们得到了不同的结果。因此,在搜索时需要考虑到这一特点,根据想要的结果选择所需的模式。

正则表达式中的转义字符

由于 Java 中的正则表达式(或更准确地说其初始表示形式)是使用字符串文字指定的,因此有必要考虑与字符串文字相关的 Java 规范的规则。特别是,\Java 源代码中字符串文字中的反斜杠字符“”被解释为转义字符,警告编译器后面的字符是特殊字符,必须以特殊方式解释。例如:
String s = "The root directory is \nWindows";//wrap Windows to a new line
String s = "The root directory is \u00A7Windows";//insert paragraph character before Windows
因此,在描述正则表达式并使用“ \”字符(例如,对于元字符)的字符串文字中,必须将其加倍,以便 Java 字节码编译器不会对其进行不同的解释。例如:
String regex = "\\s"; // template for searching for space characters
String regex = "\"Windows\""; // pattern to search for the string "Windows"
如果我们计划将特殊字符用作“常规”字符,则还应该使用双反斜杠字符来转义特殊字符。例如:
String regex = "How\\?"; // template for searching the string "How?"

Pattern 类的方法

该类Pattern还有其他处理正则表达式的方法: String pattern()– 返回创建对象的正则表达式的原始字符串表示形式Pattern
Pattern pattern = Pattern.compile("abc");
System.out.println(Pattern.pattern())//"abc"
static boolean matches(String regex, CharSequence input)– 允许您根据参数中传递的文本检查在 regex 参数中传递的正则表达式input。返回: true – 如果文本与模式匹配; ——否则;例子:
System.out.println(Pattern.matches("A.+a","Alla"));//true
System.out.println(Pattern.matches("A.+a","Egor Alla Alexander"));//false
int flags()– 返回flags创建时设置的模板参数值,如果未设置该参数则返回0。例子:
Pattern pattern = Pattern.compile("abc");
System.out.println(pattern.flags());// 0
Pattern pattern = Pattern.compile("abc",Pattern.CASE_INSENSITIVE);
System.out.println(pattern.flags());// 2
String[] split(CharSequence text, int limit)– 将作为参数传递的文本拆分为元素数组String。该参数limit确定在文本中搜索的最大匹配数:
  • 当–执行匹配limit>0搜索;limit-1
  • at limit<0– 搜索文本中的所有匹配项
  • when limit=0– 搜索文本中的所有匹配项,同时丢弃数组末尾的空行;
例子:
public static void main(String[] args) {
    String text = "Egor Alla Anna";
    Pattern pattern = Pattern.compile("\\s");
    String[] strings = pattern.split(text,2);
    for (String s : strings) {
        System.out.println(s);
    }
    System.out.println("---------");
    String[] strings1 = pattern.split(text);
    for (String s : strings1) {
        System.out.println(s);
    }
}
控制台输出: Egor Alla Anna -------- Egor Alla Anna下面我们将考虑 另一种用于创建对象的类方法Matcher

匹配器类方法

Matcher是一个类,从中创建对象来搜索模式。Matcher– 这是一个“搜索引擎”,一个正则表达式的“引擎”。为了进行搜索,他需要获得两件事:搜索模式和搜索“地址”。为了创建对象,Matcher类中提供了以下方法Patternрublic Matcher matcher(CharSequence input) 作为参数,该方法采用将在其中执行搜索的字符序列。这些是实现接口的类的对象CharSequenceString您不仅可以传递, ,还可以传递StringBuffer, StringBuilder, Segmentand作为参数CharBuffer。搜索模板是Pattern调用该方法的类对象matcher。创建匹配器的示例:
Pattern p = Pattern.compile("a*b");// compiled the regular expression into a view
Matcher m = p.matcher("aaaaab");//created a search engine in the text “aaaaab” using the pattern "a*b"
现在,在“搜索引擎”的帮助下,我们可以搜索匹配项,找出匹配项在文本中的位置,并使用类方法替换文本。该方法boolean find()使用该模式在文本中搜索下一个匹配项。使用此方法和循环运算符,您可以根据事件模型分析整个文本(当事件发生时执行必要的操作 - 在文本中查找匹配项)。例如,使用此类的方法,int start()int end()可以确定文本中匹配的位置,并使用方法String replaceFirst(String replacement)String replaceAll(String replacement)文本中的匹配替换为另一个替换文本。例子:
public static void main(String[] args) {
    String text = "Egor Alla Anna";
    Pattern pattern = Pattern.compile("A.+?a");

    Matcher matcher = pattern.matcher(text);
    while (matcher.find()) {
        int start=matcher.start();
        int end=matcher.end();
        System.out.println("Match found" + text.substring(start,end) + " с "+ start + " By " + (end-1) + "position");
    }
    System.out.println(matcher.replaceFirst("Ira"));
    System.out.println(matcher.replaceAll("Olga"));
    System.out.println(text);
}
程序输出: 从 5 到 8 个位置找到了匹配的 Alla A 从 10 到 13 个位置找到了安娜匹配 Egor Ira Anna Egor Olga Olga Egor Alla Anna 从示例中可以清楚地看出,这些方法replaceFirst创建了replaceAll一个新对象String- 一个字符串,其中是源文本,其中与模板的匹配项被替换为作为参数传递给方法的文本。此外,该方法replaceFirst仅替换第一个匹配项,以及replaceAll测试中的所有匹配项。原文保持不变。其他类方法的使用Matcher以及正则表达式的示例可以在本系列文章中找到。处理文本时最常见的正则表达式操作来自类Pattern,并且Matcher内置于String. 这些是诸如splitmatchesreplaceFirst、之类的方法replaceAll。但事实上,“在幕后”他们使用PatternMatcher。因此,如果您需要在程序中替换文本或比较字符串而不编写不必要的代码,请使用String. 如果您需要高级功能,请考虑类PatternMatcher.

结论

正则表达式在 Java 程序中使用与规则定义的模式匹配的字符串进行描述。当代码运行时,Java 将此字符串重新编译为类对象Pattern,并使用该类对象Matcher在文本中查找匹配项。正如我在一开始所说的,正则表达式经常被放在一边以供以后使用,被认为是一个困难的话题。然而,如果您了解语法、元字符、转义的基础知识,并研究正则表达式的示例,您会发现它们比乍一看要简单得多。
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION