正则表达式详解
1. 基本字符匹配
- 普通字符:大多数字符在正则表达式中表示它们自身,例如
a
匹配字符a
。 - 特殊字符:如
.
、*
、+
等,它们在正则表达式中有特殊含义。
2. 元字符
.
:匹配除换行符外的任意字符。\d
:匹配任意数字,等同于[0-9]
。\D
:匹配任意非数字字符。\w
:匹配字母、数字或下划线,等同于[a-zA-Z0-9_]
。\W
:匹配非字母、数字或下划线的字符。\s
:匹配任意空白字符,包括空格、制表符、换行符等。\S
:匹配任意非空白字符。
3. 字符类
[abc]
:匹配a
、b
或c
中的任意一个字符。[^abc]
:匹配除了a
、b
和c
之外的任意字符。[a-z]
:匹配任意小写字母。[A-Z]
:匹配任意大写字母。[0-9]
:匹配任意数字。
4. 量词
*
:匹配前面的字符0次或多次。+
:匹配前面的字符1次或多次。?
:匹配前面的字符0次或1次。{n}
:匹配前面的字符恰好n
次。{n,}
:匹配前面的字符至少n
次。{n,m}
:匹配前面的字符至少n
次,至多m
次。
5. 边界匹配
^
:匹配行的开始。$
:匹配行的结束。\b
:匹配单词边界。\B
:匹配非单词边界。
6. 分组和捕获
(abc)
:将abc
作为一个整体进行匹配,并创建一个捕获组。(?:abc)
:非捕获组,只匹配不捕获。
7. 反向引用
\1
:引用第一个捕获组的内容。\2
:引用第二个捕获组的内容。
示例:
- 匹配邮箱地址:
\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b
- 匹配电话号码:
\d{3}-\d{4}-\d{4}
- 匹配URL:
https?://[^\s]+
- 匹配HTML标签:
<([a-zA-Z]+)[^>]*>.*?</\1>
正则表达式的应用:
- 文本搜索:在文本编辑器中查找特定的字符串。
- 文本替换:替换文本中的某些部分,如批量修改文件名。
- 数据验证:验证用户输入的数据格式是否正确,如邮箱、电话号码等。
- 数据提取:从文本中提取特定的信息,如从日志文件中提取错误信息。
建议:
- 实践是学习正则表达式的最佳方式。尝试在不同的场景下使用正则表达式,如在文本编辑器、编程语言中进行搜索和替换。
- 使用在线正则表达式测试工具,如regex101.com,可以帮助你理解和测试正则表达式。
- 阅读正则表达式的文档和教程,理解不同元字符和量词的含义和用法。
- 尝试解决实际问题,如编写一个正则表达式来验证用户输入的密码是否符合要求。
示例:
在桌面角色扮演游戏(TRPG,俗称“跑团”)中,玩家需要掷出若干个骰子,根据掷出的结果推进游戏进度。在线上同样可以跑团,方法是由玩家们向机器人发出指令,由机器人随机产生每个需要掷出的骰子的结果。
玩家向机器人发出的指令是一个仅涉及加法和减法的表达式,即对若干个数字进行一系列加法或减法计算。这些数字可以是直接给出的非负整数(数字不超过 1000),也可以是若干个骰子掷出的结果。
“掷骰子”这个动作对应的指令格式为 xdy,表示摇动 x 个 y 面的骰子(1≤x≤1000,2≤y≤1000)。当 x 为 1 时,1 可以省略。
例如指令 2d3+3-d4 的意思是:先掷出 2 个 3 面骰子(你不必考虑现实中是否存在这样的骰子),不妨假设结果为 1 和 3,则 2d3 的结果就是两个骰子的面值之和 4;然后计算 4 + 3,得到结果为 7;再掷出 1 个 4 面骰子,不妨假设结果为 2,则计算 7 - 2 得到最终结果 5。
本题就请你计算玩家输入的指令里,不同种类的骰子需要掷出几个,以及可能得到的结果在什么区间范围内。
输入格式:
输入在一行中给出一条符合题目描述的玩家输入机器人的指令。题目保证指令长度不超过 20000
输出格式:
首先输出不同种类的骰子分别需要掷出几个。每种骰子的信息占一行,依次输出骰子的面数和投掷的数量,按面数从小到大输出。
输入指令保证至少有一个骰子需要掷出。
最后一行输出两个数,表示根据输入指令可以得到的最小结果和最大结果。
同一行数字间以 1 个空格分隔,行首尾不得有多余空格。
题解:
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main {
public static void main(String[] args) {
String input = "d6+3d5+2-2d3+2d5"; // 示例输入,实际应用中应从控制台读取
Map<Integer, Integer> diceMap = new HashMap<>();
int minSum = 0;
int maxSum = 0;
// 正则表达式匹配所有骰子指令和数字
Pattern pattern = Pattern.compile("(\\d*)d(\\d+)|(\\+|-)(\\d+)");
Matcher matcher = pattern.matcher(input);
while (matcher.find()) {
if (matcher.group(2) != null) {
// 匹配到骰子指令,例如 "2d6"
int count = matcher.group(1).isEmpty() ? 1 : Integer.parseInt(matcher.group(1));
int sides = Integer.parseInt(matcher.group(2));
diceMap.put(sides, diceMap.getOrDefault(sides, 0) + count);
// 更新最小值和最大值
minSum += (count > 1) ? (sides * (count - 1)) : 0; // 减去多余的最小值
maxSum += (count > 1) ? (sides * count) : sides;
} else if (matcher.group(3) != null) {
// 匹配到数字,例如 "+3"
int number = Integer.parseInt(matcher.group(4));
minSum += (matcher.group(3).equals("+")) ? number : -number;
maxSum += number; // 最大值只考虑正数
}
}
// 输出每种骰子的数量
for (Map.Entry<Integer, Integer> entry : diceMap.entrySet()) {
System.out.println(entry.getKey() + " " + entry.getValue());
}
// 输出最小结果和最大结果
System.out.println(minSum + " " + maxSum);
}
}
注:
在Java中,正则表达式中的特殊字符需要进行转义,以便正确地表示它们。在正则表达式中,\d
表示匹配任意数字(0-9)。然而,在Java字符串中,反斜杠\
本身就是一个转义字符,因此如果你想在字符串中表示一个反斜杠,你需要使用两个反斜杠\\
。
当你在Java代码中写"\\d+"
时,第一个反斜杠\
是用来转义第二个反斜杠\
的,这样Java就会将\\
解释为一个单独的反斜杠字符。然后,这个单独的反斜杠\
再去转义后面的d
,告诉Java这是一个正则表达式中的特殊字符\d
,而不是普通的字符d
。
所以,当你在Java代码中看到"\\d+"
时,它实际上是表示正则表达式中的\d+
,其中\d
匹配任意数字,而+
表示前面的元素(在这里是\d
)应该出现一次或多次。
总结一下,Java字符串中的"\\d+"
实际上对应于正则表达式中的\d+
,但由于Java字符串中反斜杠的转义规则,你需要在字符串中写成"\\d+"
。