Java正则表达式中的十大问题

本文转载自 极客标签,原文链接:Click me,译文链接:Click me

1.如何从一个字符串中提取数字?

使用正则表达式的一个常见问题是提取所有的数字到整数的数组。在Java中,\ d代表的一系列数字(0-9)。任何时候如果可能的话,使用预定义类将会使你的代码容易读懂,并且可以消除由畸形的字符类引入的错误详情请参阅预定义字符类 的更多细节。请注意,第一个反斜杠\的\ D。如果你是一个字符串中使用转义构造,你必须先反斜杠用另一个反斜杠的编译字符串。这就是为什么我们需要使用\\ D。

 
 
  1. List<Integer> numbers = new LinkedList<Integer>();
  2. Pattern p = Pattern.compile("\\d+");
  3. Matcher m = p.matcher(str);
  4. while (m.find()) {
  5. numbers.add(Integer.parseInt(m.group()));
  6. }

 2.如何通过换行符分割Java字符串? 

至少有三种换行符进入新的一行,这取决于您正在使用的操作系统。

在Windows中使用\ r表示CR(回车),在Unix系统中使用\ n表示LF(换行),在Mac OS中使用\ r\ n表示CR + LF,因此,最简单直接的方法是通过换行符来分割字符串

 
 
  1. String lines[] = String.split("\\r?\\n");

 但是,如果你不想空行,你可以使用,这也是我最喜欢的方式:

 
 
  1. String.split("[\\r\\n]+")

 一个更强大真正独立于系统的方式,如下所示。但要记住,你仍然会得到空行如果有两个换行符并排放置。

 
 
  1. String.split(System.getProperty("line.separator"));

 3.Pattern.compile()的重要性

正则表达式,指定为字符串,必须首先被编译成Pattern类的一个实例。 Pattern.compile()方法是创建对象实例的唯一途径。一个典型的调用顺序是这样

 
 
  1. Pattern p = Pattern.compile("a*b");
  2. Matcher matcher = p.matcher("aaaaab");
  3. assert matcher.matches() == true;

 从本质上讲,Pattern.compile()是用来将一个正则表达式转移到有限状态机(见编译原理,技术与工具(第二版))。但是,所有参与的状态驻留在匹配器(Matcher)。通过这种方式,Pattern p可以重复使用。和许多匹配器(Matcher)可以共享相同的模式。

 
 
  1. Matcher anotherMatcher = p.matcher("aab");
  2. assert anotherMatcher.matches() == true;

 当一个正则表达式只使用一次时,Pattern.matches()方法很方便。这种方法仍然是使用compile()来得到一个Pattern的实例,和一个字符串相匹配。因此,

 
 
  1. boolean b = Pattern.matches("a*b", "aaaaab");

 等同于上面的第一次示范代码,虽然对重复匹配它是低效率的,因为它不允许编译Pattern被重复使用。

4.如何摆脱文本的正则表达式? 

一般情况下,正则表达式使用“\”来转义构造,但是这样,Java字符串的转义需要在反斜杠前加上另一个反斜杠,有点痛苦。还有另一种方式为用户传递字符串文字到Pattern,像“$10”。而不是写\\ $5或[$]5,我们可以键入

 
 
  1. Pattern.quote("$5");

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

String.split() 拆分围绕在给定的正则表达式匹配的字符串。 Java表达式支持影响Pattern被匹配的特殊字符,这称为元字符(metcharacter)。 |是用来匹配多个正则表达式中单个正则表达式的元字符。例如,A | B表示A或B。更多详情请参阅交替用竖线或管道符号。因此,要使用|作为字符,你需要在它前面加上\,如\\进行转义|。

  6.我们怎样用Java正则表达式才能匹配anbn(a的n次方b的n次方)? 

这是字符串a连接相等数量的字符串b,如ab,aabb,并aaabbb,由所有非空字符串组成的的语言。这种语言可以被表示为无上下文语法如S→aSb| ab,因此是非规则语言。 

然而,Java正则表达式实现可以识别的不仅仅是正规语言。也就是说,根据形式语言理论定义他们不是“正规”的。采用超前和自我参照的匹配将实现它。在这里,我将给出最终的正则表达式,然后再解释它一点点。对于一个全面的解释,我想请您阅读我们如何匹配^ nb的n次方与Java正则表达式

 
 
  1. Pattern p = Pattern.compile("(?x)(?:a(?= a*(\\1?+b)))+\\1");// true
  2. System.out.println(p.matcher("aaabbb").matches());// false
  3. System.out.println(p.matcher("aaaabbb").matches());// false
  4. System.out.println(p.matcher("aaabbbb").matches());// false
  5. System.out.println(p.matcher("caaabbb").matches());

 我宁愿说一点点它是如何操作的,而不是解释这个复杂的正则表达式的语法。 

1.第一次迭代,它停在第一个a,然后向前看(用a*跳过几步后)是否有a和b。这是通过使用(?:a(?= a*(\\1?+b)))实现的,如果匹配,\ 1,自参考匹配,将匹配内括号中的元素,这是第一次迭代中的单个b。

2.第二次迭代,表达式将停在第二个a,然后向前看(再次跳跃)是否有会是b。但是这一次,\\1+ b实际上相当于bb,所以两个b必须匹配。如果是的话,\1将在第二次迭代后改为bb。 

3.第n次迭代,表达式停在第n个a,看前边是否有n个b。 通过这种方式,该表达式可以计算出a的个数,并匹配后边是否跟着相同数目的b。

7.如何使用单个空格代替有2个或多个空格的字符串和只删除前导空格?

String.replaceAll()用给定的替换字符串替换字符串中与给定的正则表达式匹配的每个子字符串。 “2个或多个空格”,可以通过正则表达式[]+来表示。因此,下面的代码会正常执行。需要注意的是,该解决方案最终不会删除所有前导和尾部空格。如果你想将它们删除,您可以使用在管道(Pipeline)中的String.trim()。

 
 
  1. String line = " aa bbbbb ccc d ";// " aa bbbbb ccc d "
  2. System.out.println(line.replaceAll("[\\s]+", " "));

8.如何用正则表达式判断一个数是素数(质数)?

 
 
  1. public static void main(String[] args) {
  2. // false
  3. System.out.println(prime(1));
  4. // true
  5. System.out.println(prime(2));
  6. // true
  7. System.out.println(prime(3));
  8. // true
  9. System.out.println(prime(5));
  10. // false
  11. System.out.println(prime(8));
  12. // true
  13. System.out.println(prime(13));
  14. // false
  15. System.out.println(prime(14));
  16. // false
  17. System.out.println(prime(15));}
  18. public static boolean prime(int n) {
  19. return !new String(new char[n]).matches(".?|(..+?)\\1+");}

该函数首先生成n个字符,并试图看是否与字符串匹配 .?|(..+?)\\1 。如果是素数,则表达式将返回false,而非!将反转结果。 

第一部分 .?只是试图确保1不是素数。神奇的部分是反向引用使用的第二部分, (..+?)\\1+先尝试匹配n个字符的长度,然后通过\\1+重复了好几次。

根据定义,一个素数是一个自然数大于1时,有1以外没有正因子和自身。这意味着,如果a = n * m个则不是素数。 n * m可以是进一步解释“重复纳米时代”,而这正是正则表达式的作用:(?+)通过使用匹配n个字符的长度,然后使用\\1+重复m次。因此,如果模式匹配,则这个数不是素数,否则是。提醒非!将反转结果。

9.如何拆分逗号分隔的字符串,但忽略在引号中的逗号? 

你已经达到将正则表达式分解的地步。这能更好,更整洁地写一个简单的拆分器,并能如你所愿的处理特殊情况。 或者,您也可以模仿的有限状态机的操作,通过使用一个switch语句或if-else语句。附件是一个代码片段。

 
 
  1. public static void main(String[] args) {
  2. String line = "aaa,bbb,\"c,c\",dd;dd,\"e,e";
  3. List<String> toks = splitComma(line);
  4. for (String t : toks) {
  5. System.out.println("> " + t);
  6. }}
  7. private static List<String> splitComma(String str) {
  8. int start = 0;
  9. List<String> toks = new ArrayList<String>();
  10. boolean withinQuote = false;
  11. for (int end = 0; end < str.length(); end++) {
  12. char c = str.charAt(end);
  13. switch(c) {
  14. case ',':
  15. if (!withinQuote) {
  16. toks.add(str.substring(start, end));
  17. start = end + 1;
  18. }
  19. break;
  20. case '\"':
  21. withinQuote = !withinQuote;
  22. break;
  23. }
  24. }
  25. if (start < str.length()) {
  26. toks.add(str.substring(start));
  27. }
  28. return toks;}

 

  10.如何在Java正则表达式中使用反向引用?

反向引用是Java正则表达式的另一个有用的功能。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值