Java 正则表达式的 10 大问题

总结了关于 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 String?

根据您正在使用的操作系统,至少有三种不同的方法可以输入换行符。

\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类的实例。图案。compile()方法是创建对象实例的唯一方法。因此,一个典型的调用序列是

Pattern p = Pattern.compile(“a*b”);
Matcher matcher = p.matcher(“aaaaab”);
assert matcher.matches() == true;

本质上,模式compile()用于将正则表达式转换为有限状态机(参见编译器:原理、技术和工具(第 2 版))。但是执行匹配所涉及的所有状态都存在于匹配器中。通过这种方式,Pattern p可以被重用。许多匹配器可以共享相同的模式。

Matcher anotherMatcher = p.matcher(“aab”);
assert anotherMatcher.matches() == true;

Pattern.matches() 当一个正则表达式只使用一次时, matches()方法被定义为一种方便。该方法仍然使用compile()隐式获取Pattern的实例,并匹配一个字符串。所以,

boolean b = Pattern.matches(“a*b”, “aaaaab”);

等效于上面的第一个代码,尽管对于重复匹配,它的效率较低,因为它不允许重用已编译的模式。

4. 正则表达式如何转义文本?

通常,正则表达式使用“\”来转义结构,但是在反斜杠之前加上另一个反斜杠以便编译 Java 字符串是很痛苦的。用户还有另一种方法可以将字符串文字传递给Pattern,例如“$5”。除了写\\$5or [$]5,我们可以输入

Pattern.quote(“$5”);

5、为什么String.split()需要管道分隔符进行转义?

字符串split()围绕给定正则表达式的匹配拆分字符串。Java 表达式支持影响模式匹配方式的特殊字符,称为元字符|是一个元字符,用于匹配几个可能的正则表达式中的单个正则表达式。例如,A|B表示要么A要么B。有关详细信息,请参阅与垂直条或管道符号交替。因此,要|用作文字,您需要通过\在其前面添加来转义它,例如\\|.

6. 我们如何将 a n b n与 Java 正则表达式匹配?

这是所有非空字符串的语言,由一些a‘ 后跟相等数量的b‘ 组成,例如ab,aabbaaabbb。这种语言可以被证明是上下文无关文法 S → aSb | ab,因此是一种非常规语言。

但是,Java 正则表达式实现可以识别的不仅仅是常规语言。也就是说,它们不是形式语言理论定义的“常规”。使用前瞻自引用匹配将实现它。这里我先给出最终的正则表达式,再稍微解释一下。要获得全面的解释,我建议您阅读How can we match a^nb^n with Java regex

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. 在第一次迭代中,它在第一次停止a然后向前看(在a使用 跳过一些 s之后a*)是否存在 a b。这是通过使用(?:a(?= a*(\\1?+b))). 如果匹配,\1自引用匹配,将匹配最里面的括号元素,这是b第一次迭代中的一个。
  2. 在第二次迭代中,表达式将在第二次停止a,然后向前看(再次跳过as)以查看是否会有b。但这一次,\\1+b实际上等价于,因此必须匹配bb两个s。b如果是这样,\1bb在第二次迭代后更改。
  3. 在第n次迭代中,表达式在第n次停止,a并查看前面是否有n b秒。

通过这种方式,表达式可以计算s 的数量,如果后面的 s数量相同a,则匹配。ba

7. 如何用单个空格替换字符串中的2个或多个空格,只删除前导空格?

字符串replaceAll()用给定的替换替换与给定正则表达式匹配的每个子字符串。“2个或多个空格”可以用正则表达式表示[ ]+。因此,以下代码将起作用。请注意,该解决方案最终不会删除所有前导和尾随空格。如果您想删除它们,可以使用String管道中的修剪()

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*ma不是素数。n*m可以进一步解释为“重复m次”,这正是正则表达式所做的:使用 匹配n 个长度的字符(..+?),然后使用 重复m\\1+。因此,如果模式匹配,则该数字不是素数,否则为素数。提醒!将反转结果。

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 正则表达式中另一个有用的特性。