【正则表达式系列一】分组的本质原理用法python java示例详解
文章目录
原理
正则表达式中的分组是由括号 ( )
来表示的。它们的作用是将一部分模式组合在一起,并将其视为一个整体。
正则表达式引擎在处理分组时,会根据匹配规则和分组的位置来执行不同的操作。以下是正则表达式分组的工作原理:
1.匹配:
当应用正则表达式时,引擎会尝试从输入字符串中找到与整个模式匹配的子字符串。分组允许将多个字符组合在一起,并为整个组指定匹配规则。
2.捕获:
捕获分组可以记住匹配的内容,以便后续引用。当整个模式匹配成功时,引擎会将匹配的内容保存在相应的捕获分组中,以便在后续的操作中使用。
3.引用:
在正则表达式中,您可以使用反向引用来引用先前捕获的分组。通过使用 \数字
的形式,其中数字表示分组的索引,您可以在同一表达式中引用之前匹配的内容。
4.分组顺序:
分组的顺序非常重要。正则表达式引擎按照从左到右的顺序处理分组,先处理外层的分组,再处理内层的分组。这意味着内层分组可以嵌套在外层分组中,以实现更复杂的匹配规则。
5.非捕获分组:
非捕获分组是指使用 (?: )
语法来创建的分组,它不会记住匹配的内容,主要用于分组但不需要后续引用或捕获的情况。
通过这些原理和机制,正则表达式能够实现复杂的模式匹配、捕获和引用操作,提供了强大的文本处理能力。分组使得我们可以将模式划分为逻辑单元,并根据需求进行匹配和操作。
作用
- 分组可以应用量词:您可以将量词(例如
*
、+
、?
)应用于整个分组,以指定该分组可以出现的次数。 - 分组可以捕获匹配的内容:当匹配成功时,您可以使用捕获组来提取和使用分组匹配的内容。
- 分组可以进行反向引用:在正则表达式中,您可以使用
\数字
的形式来引用先前捕获的分组。这允许您在同一表达式中引用之前匹配的内容。
类别
1.捕获分组(Capturing Group):
这是最常见的分组类型。通过使用括号 ( )
来创建一个捕获分组,并且在匹配成功时,它会记住匹配的内容,以便后续引用。
示例:`(pattern)`
2.非捕获分组(Non-capturing Group):
非捕获分组与捕获分组类似,也使用括号 ( )
,但在匹配成功时不会记住匹配的内容。非捕获分组对于只需要分组但不需要后续引用或捕获的情况很有用。
示例:`(?:pattern)`
3.零宽断言分组(Zero-width Assertion Group):
零宽断言分组用于指定位置而不是字符。它们不匹配实际的字符,而是用于限制匹配出现在某些位置之前或之后的条件。
- 正向肯定查找(Positive Lookahead):
(?=pattern)
匹配出现在某个模式pattern
之前的位置。 - 正向否定查找(Negative Lookahead):
(?!pattern)
匹配不出现在某个模式pattern
之前的位置。 - 反向肯定查找(Positive Lookbehind):
(?<=pattern)
匹配出现在某个模式pattern
之后的位置。 - 反向否定查找(Negative Lookbehind):
(?<!pattern)
匹配不出现在某个模式pattern
之后的位置。
4.命名分组(Named Group):
命名分组是为了更方便地引用和捕获内容,可以给分组指定一个名称。通过使用语法 (?<name>pattern)
或 (?'name'pattern)
来创建命名分组。
示例:`(?<name>pattern)`
这些分组类型使得正则表达式更加灵活和强大,可以根据需要进行匹配、引用和捕获。根据具体的需求,您可以选择适合的分组类型来实现所需的功能。
示例
下面是在 Python 和 Java 中使用正则表达式分组的示例:
1.捕获分组
正则表达式 (\\d{2})-(\\d{2})-(\\d{4})
用于匹配日期格式。
解释该正则表达式的各个部分:
(\\d{2})
:捕获分组,匹配两个数字(0-9),表示日。-
:匹配连字符 “-” 字符。(\\d{2})
:捕获分组,再次匹配两个数字,表示月份。-
:匹配连字符 “-” 字符。(\\d{4})
:捕获分组,匹配四个数字,表示年份。
这个正则表达式可以用来匹配日期格式,例如 “12-31-2022”。通过将模式划分为三个捕获分组,它可以提取日、月和年的值。
在 Java 中,由于反斜杠 \
在字符串中具有特殊意义,需要使用双反斜杠 \\
来转义。因此,表达式中的 \\d
表示数字的匹配。
如果应用该正则表达式到文本中,它将找到与模式匹配的日期,并返回匹配的结果。
Python 示例:
import re
# 捕获分组示例
pattern = r'(\d{2})-(\d{2})-(\d{4})'
input_string = '31-12-2022'
match = re.match(pattern, input_string)
if match:
day = match.group(1)
month = match.group(2)
year = match.group(3)
print(f"Day: {day}, Month: {month}, Year: {year}")
#Day: 31, Month: 12, Year: 2022
Java 示例:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main {
public static void main(String[] args) {
String pattern = "(\\d{2})-(\\d{2})-(\\d{4})";
String inputString = "31-12-2022";
Pattern regex = Pattern.compile(pattern);
Matcher matcher = regex.matcher(inputString);
if (matcher.matches()) {
String day = matcher.group(1);
String month = matcher.group(2);
String year = matcher.group(3);
System.out.println("Day: " + day + ", Month: " + month + ", Year: " + year);
}
}
}
//Day: 31, Month: 12, Year: 2022
这两个示例都演示了如何使用正则表达式的捕获分组来提取日期字符串中的年、月和日。通过指定括号 ( )
将模式划分为不同的分组,然后使用 group()
方法(在 Python 中)或 group(int)
方法(在 Java 中)来获取每个分组的内容。
请注意,Python 使用 re.match()
函数执行正则表达式匹配,而 Java 使用 Pattern
和 Matcher
类来执行匹配。同时,Java 的捕获分组索引从 1 开始,而 Python 从 0 开始。
2.非捕获分组
下面是在 Python 和 Java 中使用非捕获分组的示例:
正则表达式 (?:https?|ftp)://[^\\s/$.?#].[^\\s]*
用于匹配 URL。
解释该正则表达式的各个部分:
(?:https?|ftp)
:非捕获分组,匹配 “http” 或 “https” 或 “ftp”。://
:匹配 “😕/” 字符串。[^\\s/$.?#]
:字符类,匹配除空白字符、斜杠、美元符号、问号和井号之外的任意字符。.
:匹配任意字符(除了换行符)。[^\\s]*
:字符类,匹配零个或多个非空白字符。
这个正则表达式可以用来匹配类似 “http://www.example.com” 的 URL,其中包含协议 (http、https 或 ftp),以及域名部分(除了空白字符、斜杠、美元符号、问号和井号之外的字符)。
请注意,在 Java 中使用正则表达式时,由于反斜杠 \
在字符串中具有特殊意义,需要使用双反斜杠 \\
来转义。因此,表达式中的 \\s
表示空白字符的匹配。
如果应用该正则表达式到文本中,它将找到与模式匹配的 URL,并返回匹配的结果。
Python 示例:
import re
# 非捕获分组示例
pattern = r'(?:https?|ftp)://[^\s/$.?#].[^\s]*'
input_string = 'Visit my website at http://www.example.com'
match = re.search(pattern, input_string)
if match:
url = match.group()
print("URL:", url)
#URL: http://www.example.com
Java 示例:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main {
public static void main(String[] args) {
String pattern = "(?:https?|ftp)://[^\\s/$.?#].[^\\s]*";
String inputString = "Visit my website at http://www.example.com";
Pattern regex = Pattern.compile(pattern);
Matcher matcher = regex.matcher(inputString);
if (matcher.find()) {
String url = matcher.group();
System.out.println("URL: " + url);
}
}
}
//URL: http://www.example.com
这两个示例演示了如何在正则表达式中使用非捕获分组来匹配 URL。在正则表达式中,使用 (?: )
来创建一个非捕获分组。这样,分组将被用于分组模式,但不会记住匹配的内容。
请注意,Python 使用 re.search()
函数执行正则表达式搜索,而 Java 使用 Pattern
和 Matcher
类来执行搜索。
3.零宽断言分组
3.1正向肯定查找
"(?<=\\bMr|Ms)\\. \\w+"
这个正则表达式的详解如下:
-
(?<=\\bMr|Ms)
: 这是一个正向回顾断言,用于指定前面必须出现"Mr"或"Ms"。它确保匹配的句子有"Mr"或"Ms"作为前缀。 -
\\.
: 这部分匹配一个句点和一个空格字符。注意,句点需要使用转义字符"\"进行转义。 -
\\w+
: 这部分匹配一个或多个单词字符。单词字符包括字母、数字和下划线。
综合起来,“(?<=\bMr|Ms)\. \w+” 表示匹配以句点和空格开头,后面紧跟一个或多个单词字符的句子。但是,这些句子必须以"Mr"或"Ms"开头。换句话说,它用于查找以"Mr"或"Ms"开头的句子中的单词。
以下是在 Python 和 Java 中使用正向肯定查找的零宽断言分组示例:
Python 示例:
import re
# 正向肯定查找示例
pattern = r'(?<=\bMr|Ms)\. \w+'
input_string = 'Hello, Mr. John. How are you, Ms. Smith?'
matches = re.findall(pattern, input_string)
for match in matches:
print("Match:", match)
# Match: . John
# Match: . Smith
Java 示例:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main {
public static void main(String[] args) {
String pattern = "(?<=\\bMr|Ms)\\. \\w+";
String inputString = "Hello, Mr. John. How are you, Ms. Smith?";
Pattern regex = Pattern.compile(pattern);
Matcher matcher = regex.matcher(inputString);
while (matcher.find()) {
String match = matcher.group();
System.out.println("Match: " + match);
}
}
}
这两个示例演示了如何在正则表达式中使用正向肯定查找的零宽断言分组。在正则表达式中,使用 (?<= )
来创建一个正向肯定查找,它匹配出现在某个模式之前的位置。
在上述示例中,正则表达式 (?<=\bMr|Ms)\. \w+
匹配以 “Mr” 或 “Ms” 开头的称呼后跟一个点和一个单词的情况。通过使用正向肯定查找,我们只匹配符合条件的部分(例如 “Mr. John” 和 “Ms. Smith”),而不包括前面的称呼部分。
请注意,在 Java 中使用正则表达式时,由于反斜杠 \
在字符串中具有特殊意义,需要使用双反斜杠 \\
来转义。因此,表达式中的 \\b
表示单词边界的匹配。
应用这些正则表达式到文本中后,它们将找到与模式匹配的部分,并返回匹配的结果。
3.2正向否定查找
"(?<!\\bMr|Ms)\\. \\w+"
这个正则表达式的详解如下:
-
“(?<!\bMr|Ms)”: 这是一个负向回顾断言,用来指定前面不能出现"Mr"或"Ms"。它确保匹配的句子不会以"Mr"或"Ms"作为前缀。
-
"\. “: 这部分匹配一个句点和一个空格字符。注意,句点需要使用转义字符”\"进行转义。
-
“\w+”: 这部分匹配一个或多个单词字符。单词字符包括字母、数字和下划线。
综合起来,“(?<!\bMr|Ms)\. \w+” 表示匹配以句点和空格开头,后面紧跟一个或多个单词字符的句子。但是,这些句子不能以"Mr"或"Ms"开头。换句话说,它用来查找不以"Mr"或"Ms"开头的句子中的单词。
以下是在 Python 和 Java 中使用正向否定查找的零宽断言分组示例:
Python 示例:
import re
# 正向否定查找示例
pattern = r'(?<!\bMr|Ms)\. \w+'
input_string = 'Hello, Mr. John. How are you, Ms. Smith?'
matches = re.findall(pattern, input_string)
for match in matches:
print("Match:", match)
#Match: . How
Java 示例:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main {
public static void main(String[] args) {
String pattern = "(?<!\\bMr|Ms)\\. \\w+";
String inputString = "Hello, Mr. John. How are you, Ms. Smith?";
Pattern regex = Pattern.compile(pattern);
Matcher matcher = regex.matcher(inputString);
while (matcher.find()) {
String match = matcher.group();
System.out.println("Match: " + match);
}
}
}
//Match: . How
这两个示例演示了如何在正则表达式中使用正向否定查找的零宽断言分组。在正则表达式中,使用 (?<! )
来创建一个正向否定查找,它匹配不出现在某个模式之前的位置。
在上述示例中,正则表达式 (?<!\bMr|Ms)\. \w+
匹配不以 “Mr” 或 “Ms” 开头的点和单词的情况。通过使用正向否定查找,我们只匹配符合条件的部分(例如 “Hello” 和 “How”),而不包括以 “Mr” 或 “Ms” 开头的部分。
请注意,在 Java 中使用正则表达式时,由于反斜杠 \
在字符串中具有特殊意义,需要使用双反斜杠 \\
来转义。因此,表达式中的 \\b
表示单词边界的匹配。
应用这些正则表达式到文本中后,它们将找到与模式匹配的部分,并返回匹配的结果。
3.3 反向肯定查找
"(?<=Hello, )\\w+"
这个正则表达式的详解如下:
-
“(?<=Hello, )”: 这是一个正向回顾断言,用来指定前面必须出现"Hello, "。它确保匹配的单词有"Hello, "作为前缀。
-
“\w+”: 这部分匹配一个或多个单词字符。单词字符包括字母、数字和下划线。
综合起来,“(?<=Hello, )\w+” 表示匹配一个或多个单词字符,并且这些单词字符以"Hello, "作为前缀。换句话说,它用来查找以"Hello, "开头的单词。
以下是在 Python 和 Java 中使用反向肯定查找的零宽断言分组示例:
Python 示例:
import re
# 反向肯定查找示例
pattern = "(?<=Hello, )\\w+"
input_string = 'Hello, Mr. John. How are you, Ms. Smith?'
matches = re.findall(pattern, input_string)
for match in matches:
print("Match:", match)
#Match: Mr
Java 示例:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main {
public static void main(String[] args) {
String pattern = "(?<=Hello, )\\w+";
String inputString = "Hello, Mr. John. How are you, Ms. Smith?";
Pattern regex = Pattern.compile(pattern);
Matcher matcher = regex.matcher(inputString);
while (matcher.find()) {
String match = matcher.group();
System.out.println("Match: " + match);
}
}
}
//Match: Mr
这两个示例演示了如何在正则表达式中使用反向肯定查找的零宽断言分组。在正则表达式中,使用 (?<= )
来创建一个反向肯定查找,它匹配出现在某个模式之后的位置。
在上述示例中,正则表达式 \w+(?<=Hello, )
匹配以 "Hello, " 开头的单词的情况。通过使用反向肯定查找,我们只匹配符合条件的部分(例如 “Mr.”),而不包括 "Hello, " 部分。
应用这些正则表达式到文本中后,它们将找到与模式匹配的部分,并返回匹配的结果。
3.4 反向否定查找
'\w+(?<!Hello, )'
这个正则表达式的详解如下:
-
“(?<!Hello, )”: 这是一个负向回顾断言,用来指定前面不能出现"Hello, "。它确保匹配的单词不会有"Hello, "作为前缀。
-
“\w+”: 这部分匹配一个或多个单词字符。单词字符包括字母、数字和下划线。
综合起来,“(?<!Hello, )\w+” 表示匹配一个或多个单词字符,并且这些单词字符不能以"Hello, "作为前缀。换句话说,它用来查找不以"Hello, "开头的单词。
以下是在 Python 和 Java 中使用反向否定查找的零宽断言分组示例:
Python 示例:
import re
# 反向否定查找示例
pattern = r'\w+(?<!Hello, )'
input_string = 'Hello, Mr. John. How are you, Ms. Smith?'
matches = re.findall(pattern, input_string)
for match in matches:
print("Match:", match)
#Match: Hello
#Match: Mr
#Match: John
#Match: How
#Match: are
#Match: you
#Match: Ms
#Match: Smith
Java 示例:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main {
public static void main(String[] args) {
String pattern = "(?<!Hello, )\\w+";
String inputString = "Hello, Mr. John. How are you, Ms. Smith?";
Pattern regex = Pattern.compile(pattern);
Matcher matcher = regex.matcher(inputString);
while (matcher.find()) {
String match = matcher.group();
System.out.println("Match: " + match);
}
}
}
//Match: Hello
//Match: r
//Match: John
//Match: How
//Match: are
//Match: you
//Match: Ms
//Match: Smith
这两个示例演示了如何在正则表达式中使用反向否定查找的零宽断言分组。在正则表达式中,使用 (?<! )
来创建一个反向否定查找,它匹配不出现在某个模式之后的位置。
在上述示例中,正则表达式 \w+(?<!Hello, )
匹配不以 "Hello, " 开头的单词的情况。通过使用反向否定查找,我们只匹配符合条件的部分(例如 “John.” 和 “Smith?”),而不包括以 "Hello, " 开头的部分。
应用这些正则表达式到文本中后,它们将找到与模式匹配的部分,并返回匹配的结果。
4.命名分组
"(?<username>[A-Za-z0-9]+)@(?<domain>[A-Za-z]+\\.[A-Za-z]+)"
这个正则表达式的详解如下:
-
(?<username>[A-Za-z0-9]+)
: 这是一个命名分组,用于匹配由字母和数字组成的用户名。[A-Za-z0-9]+
表示匹配一个或多个字母或数字。 -
@
: 匹配邮箱地址中的"@"符号。 -
(?<domain>[A-Za-z]+\.[A-Za-z]+)
: 这是另一个命名分组,用于匹配域名部分。[A-Za-z]+\.[A-Za-z]+
表示匹配一个或多个字母组成的子域名,后跟一个点号(转义为\.
),然后再跟一个或多个字母组成的顶级域名。
综合起来,“(?[A-Za-z0-9]+)@(?[A-Za-z]+.[A-Za-z]+)” 表示匹配一个邮箱地址,并将用户名存储在名为"username"的命名分组中,将域名存储在名为"domain"的命名分组中。换句话说,它用于提取邮箱地址中的用户名和域名。
以下是Python和Java中命名分组的示例:
在Python中,使用(?P<name>pattern)
语法来创建命名分组。下面是一个示例,匹配一个由字母组成的姓名,并将姓氏和名字存储在不同的命名分组中:
import re
pattern = r"(?P<last_name>[A-Za-z]+), (?P<first_name>[A-Za-z]+)"
text = "Doe, John"
match = re.match(pattern, text)
if match:
last_name = match.group("last_name")
first_name = match.group("first_name")
print(f"Last Name: {last_name}")
print(f"First Name: {first_name}")
#Last Name: Doe
#First Name: John
在Java中,使用(?<name>pattern)
语法来创建命名分组。下面是一个示例,匹配一个由字母组成的邮箱地址,并将用户名和域名存储在不同的命名分组中:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main {
public static void main(String[] args) {
String pattern = "(?<username>[A-Za-z0-9]+)@(?<domain>[A-Za-z]+\\.[A-Za-z]+)";
String text = "example@example.com";
Pattern regex = Pattern.compile(pattern);
Matcher matcher = regex.matcher(text);
if (matcher.matches()) {
String username = matcher.group("username");
String domain = matcher.group("domain");
System.out.println("Username: " + username);
System.out.println("Domain: " + domain);
}
}
}
//Username: example
//Domain: example.com
以上示例演示了如何在Python和Java中使用命名分组来提取特定的模式部分。