10.4 Java正则表达式的十大问题
本文总结了有关Java 正则表达式主要问题。由于他们经常被问到,你可能发现他们很有用。
1.如何从字符串中提取数字?
使用正则表达式的一个常见问题是将所有数字提取到整数数组中。
在Java中,\ d表示一个数字范围(0-9)。 尽可能使用预定义的类将使您的代码更易于阅读并消除畸形字符类引入的错误。 有关更多详细信息,请参阅预定义的字符类。 请注意\ d中的第一个反斜杠\。 如果您在字符串文本中使用转义构造,则必须在反斜杠之前加上另一个反斜杠,以便编译该字符串。 这就是为什么我们需要使用\ d。
List<Integer> numbers = new LinkedList<Integer>();
Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher(str);
while (m.find()) {
numbers.add(Integer.parseInt(m.group()));
}
2.如何用换行符分割Java字符串?
至少有三种不同的方法来输入新的行字符,具体取决于您正在使用的操作系统。
\r represents CR (Carriage Return), which is used in Unix
\n means LF (Line Feed), used in Mac OS
\r\n means CR + LF, used in Windows
因此,用新行分割字符串最直接的方法是:
String lines[] = String.split("\\r?\\n");
但是如果你不想要空行,你可以使用,这也是我最喜欢的方式:
String.split("[\\r\\n]+")
真正独立于系统的更健壮的方式如下。 但请记住,如果两个换行符并排放置,您仍将获得空行。
String.split(System.getProperty("line.separator"));
3. Pattern.compile()的重要性
指定为字符串的正则表达式必须首先编译到Pattern类的实例中。 Pattern.compile()方法是创建对象实例的唯一方法。 因此典型的调用序列:
Pattern p = Pattern.compile("a*b");
Matcher matcher = p.matcher("aaaaab");
assert matcher.matches() == true;
从本质上讲,Pattern.compile()用于将正则表达式转换为有限状态机(参见编译器:原理,技术和工具(第二版))。 但参与匹配的所有状态都驻留在匹配器中。 通过这种方式,模式p可以被重用。 许多匹配者可以共享相同的模式。
Matcher anotherMatcher = p.matcher("aab");
assert anotherMatcher.matches() == true;
Pattern.matches()方法被定义为只使用一次正则表达式的便利。 该方法仍然使用compile()隐式地获取Pattern的实例,并匹配一个字符串。 因此,
boolean b = Pattern.matches("a*b", "aaaaab");
相当于上面的第一个代码,但对于重复匹配,效率不高,因为它不允许重新使用编译的模式。
4.如何转义正则表达式中字符
一般来说,正则表达式使用“\”来转义结构,但是在反斜杠之前加上另一个反斜线以便Java字符串编译是很痛苦的。
用户还可以通过另一种方式将字符串文字传递给模式,例如“$ 5”。 这样$没有什么特殊含义了。
Pattern.quote("$5");
5.为什么String.split()需要管道分隔符才能被转义?
String.split()将字符串分割为给定正则表达式的匹配。 Java表达式支持影响模式匹配方式的特殊字符,称为元字符。| 是一个元字符,用于匹配几个可能的正则表达式中的单个正则表达式。 例如,A | B表示A或B。有关更多详细信息,请参阅垂直条或管道符号的交替。 因此,要使用| 作为文字,您需要通过在\ \之前添加\来避开它,例如\ |。
6.我们如何将anbn与Java正则表达式匹配?
这是所有非空字符串的语言,由一些数字a组成,后面跟着相同数量的b,如ab,aabb和aaabbb。 这种语言可以显示为上下文无关语法S→aSb | ab,因此是一种非常规的语言。
但是,Java正则表达式实现可以识别的不仅仅是普通语言。 也就是说,他们不是通过形式语言理论定义的“规则”。 使用lookahead和自我参考匹配可以实现它。 在这里,我将首先给出最终的正则表达式,然后解释一点。 为了全面的解释,我会引用你阅读我们如何匹配一个^ n b ^ n与Java正则表达式。
但是,Java正则表达式实现可以识别的不仅仅是普通语言。 也就是说,他们不是通过形式语言理论定义的“规则”。 使用lookahead和自我参考匹配可以实现它。 在这里,我将首先给出最终的正则表达式,然后解释一点。 为了全面的解释,我会引用你阅读我们如何匹配一个^ n b ^ n与Java正则表达式。
Pattern p = Pattern.compile("(?x)(?:a(?= a*(\\1?+b)))+\\1");
// true
System.out.println(p.matcher("aaabbb").matches());
// false
System.out.println(p.matcher("aaaabbb").matches());
// false
System.out.println(p.matcher("aaabbbb").matches());
// false
System.out.println(p.matcher("caaabbb").matches());
与其解释这个复杂的正则表达式的语法,我宁愿稍微说一下它是如何工作的。
1 、在第一次迭代中,它会在第一个循环处停止,然后再前进(在跳过一些之后,通过使用*)是否存在b。 这是通过使用(?:a(?= a *(\ 1?+ b)))来实现的。 如果匹配,\ 1,自我引用匹配将匹配非常内部的括号元素,这是第一次迭代中的单个b。
2、在第二次迭代中,表达式将在第二个a处停止,然后它向前看(再次跳过)以查看是否有b。 但这一次,\ 1 + b实际上相当于bb,因此必须匹配两个b。 如果是这样,\ 1将在第二次迭代后更改为bb。
3、在第n次迭代中,表达式在第n个点处停止,看看前面是否有n个bs。
通过这种方式,如果b之后的a的数量相同,则表达式可以计数as和匹配的数量。
7.如何用字符串中的单个空格替换2个或更多空格并仅删除前导空格?
String.replaceAll()用给定的替换替换与给定正则表达式匹配的每个子字符串。 “2个或更多空格”可以用正则表达式[] +表示。 因此,下面的代码将起作用。 请注意,该解决方案最终不会删除所有前导空白和尾随空白。 如果你想删除它们,你可以在管道中使用String.trim()。
String line = " aa bbbbb ccc d ";
// " aa bbbbb ccc d "
System.out.println(line.replaceAll("[\\s]+", " "));
8.如何通过正则表达式判断素数
public static void main(String[] args) {
// false
System.out.println(prime(1));
// true
System.out.println(prime(2));
// true
System.out.println(prime(3));
// true
System.out.println(prime(5));
// false
System.out.println(prime(8));
// true
System.out.println(prime(13));
// false
System.out.println(prime(14));
// false
System.out.println(prime(15));
}
public static boolean prime(int n) {
return !new String(new char[n]).matches(".?|(..+?)\\1+");
}
该函数首先生成n个字符并尝试查看该字符串是否匹配。?(.. +?)\ 1+。 如果它是素数,表达式将返回false并且! 会扭转结果。
第一部分 .? 只是试图确保1不是素数。 神奇的部分是使用反向引用的第二部分。 (.. +?)\ 1+首先尝试匹配n个字符长度,然后用\ 1+重复几次。
根据定义,素数是大于1的自然数,除1和自身以外没有正数除数。 这意味着如果a = n * m那么a不是素数。 n * m可以进一步解释为“重复n次”,这正是正则表达式所做的:通过使用(.. +?)匹配n个字符长度,然后使用\ 1+重复m次。 因此,如果模式匹配,数字不是素数,否则是。 提醒一下! 会扭转结果。
9.如何分割逗号分隔的字符串,但忽略引号中的逗号?
你已经达到了正则表达式崩溃的地步。 编写简单的分离器会更好,更简洁,并且可以根据需要处理特殊情况。
或者,您可以使用switch语句或if-else模仿有限状态机的操作。 附件是一段代码。
public static void main(String[] args) {
String line = "aaa,bbb,\"c,c\",dd;dd,\"e,e";
List<String> toks = splitComma(line);
for (String t : toks) {
System.out.println("> " + t);
}
}
private static List<String> splitComma(String str) {
int start = 0;
List<String> toks = new ArrayList<String>();
boolean withinQuote = false;
for (int end = 0; end < str.length(); end++) {
char c = str.charAt(end);
switch(c) {
case ',':
if (!withinQuote) {
toks.add(str.substring(start, end));
start = end + 1;
}
break;
case '\"':
withinQuote = !withinQuote;
break;
}
}
if (start < str.length()) {
toks.add(str.substring(start));
}
return toks;
}
10.如何在Java正则表达式中使用反向引用
反向引用是Java正则表达式中另一个有用的功能。