简单入门正则表达式 - 第十一章 Java与.Net中的正则表达式应用

时间:2022-11-11 15:48:37

一、java.util.regex 与 System.Text.RegularExpressions 介绍

对于 Java 和 .Net 这样的高级语言来说,正则表达式的支持是必不可少的,现在我们就分别针对 Java 和 .Net 的正则表达式进行介绍,然后对它们的操作功能进行详细说明。

为了支持正则表达式功能,Java 提供的工具包 java.util.regex,它主要由三个类(Pattern、Matcher 和 PatternSyntaxException)组成。正如 Pattern 的名字所表示的那样,它所代表的就是正则表达式的样式,除此之外,它也为开发人员提供了必要的正则表达式操作途径,匹配、替换和分割;而 Matcher 代表的是正则表达式编译并执行某个操作之后的结果,并为结果操作提供了丰富的功能,每次执行匹配所涉及的所有状态都驻留在匹配器(Matcher)中,所以,在多个匹配器之间,它们可以共享同一个模式;如果正则表达式引擎中编译的时候,发现输入的样式无效,那么异常 PatternSyntaxException 就会被抛出。

在使用正则表达式时,首先要对样式进行编译,然后才可以利用 Pattern 所提供的操作方法,Pattern 类并没有直接提供公共的构造函数,但我们可以通过它提供的静态方法 compile 来创建 Pattern 对象,在方法 compile 中,除了可以指定正则表达式样式外,我们还可以设置一些正则表达式选项,比如让正则表达式大小写不敏感和多行匹配模式等。与 Pattern 类类似,Matcher 类也不提供公共的构造函数,我们可以从 Pattern 对象的 matcher 方法中获取结果 Matcher 对象,其实 Matcher 本身是一个对正则表达式进行解释分析并进行匹配操作的引擎,这就是为什么多个 Matcher 对象可以共享一个 Pattern 对象的原因。

在 .Net 中,System.Text.RegularExpressions 命名空间中同样提供了与正则表达式操作密切相关的类,其中有Regex、Match、MatchCollection、Group、GroupCollection、Capture、CaptureCollection以及委托MatchEvaluator。除了 MatchEvaluator 类之外,其它类的作用与 Java 的 java.util.regex 包提供的类的功能类似,Regex 与 Pattern 对应,Match 与 Matcher 对应,而 Group、GroupCollection、Capture、CaptureCollection 是对 Match 中的内容的细化,它们提供了比 Java Matcher更多的可操作功能。而 MatchEvaluator 则相当于一个事件,该事件会在方法 System.Text.RegularExpressions.Regex.Replace 执行的时候被触发。

二、类 String 中的正则表达式方法介绍

为方便使用正则表达式,Java 在 String 类中提供了 matches、replaceAll、replaceFirst 和 split 四种快捷方法,它们的作用分别是匹配、替换全部匹配内容、替换第一个匹配的内容、分割;在 .Net 的 String 类中也有类似的方法,但它们并不支持正则表达式,不过我们可以利用 System.Text.RegularExpressions 命名空间下的类实现其对应的替换方案。

三、match 的使用

我们先通过两个例子来学习下匹配的相关操作,第一个用于学习简单的匹配操作,第二个用于学习复杂的匹配操作。对于处理简单的字符串匹配任务来说,String 类提供了一个 matches 方法,它能接受一个正则表达式样式的字符串,然后将自身的内容与之比较,如果与给定的样式相匹配就返回 true,否则就返回 false,此外,java.util.regex.Pattern 类的静态方法 matches(String, CharSequence) 方法也提供了完全相同的功能;如果要进行庞大而复杂的处理,就要借助工具包 java.util.regex 或是命名空间 System.Text.RegularExpressions 所提供的高级功能了。

在接下来的例子中,我们分别使用 Java 和 .Net 两个版本的代码介绍的匹配的用法。先来看下 Java 的 String 类的 matches 方法的实现源码,其实它就是利用工具包 java.util.regex 中的 Pattern 类的静态方法实现的,所以,即便在 .Net 中没有这样的方法,我们还是可以用命名空间 System.Text.RegularExpressions 的 Regex 类的 match 方法来实现相同的功能。

  1. public boolean matches(String regex) {

  2. return Pattern.matches(regex, this);

  3. }

matches 方法接受一个正则表达式样式 regex 参数,然后利用该样式与自身内容,即 this 作为比较的对象,按照这种思路,我们可以实现一个 .Net 版的 matches 方法。首先为 String 类型做一个包装类 StringWrapper,这样就可以为 String 添加 matches 方法了,然后实现 matches 方法,具体实现内容如下。

  1. /// <summary>

  2. /// String包装类,提供正则表达式相关方法

  3. /// </summary>

  4. class StringWrapper

  5. {

  6. private String value;

  7.  

  8. public StringWrapper(String value)

  9. {

  10. this.value = value;

  11. }

  12.  

  13. public String Value

  14. {

  15. get

  16. {

  17. return this.value;

  18. }

  19. set

  20. {

  21. this.value = value;

  22. }

  23. }

  24.  

  25. /// <summary>

  26. ///

  27. /// </summary>

  28. /// <param name="pattern"></param>

  29. /// <returns></returns>

  30. public bool matches(String pattern)

  31. {

  32. return System.Text.RegularExpressions.Regex.IsMatch(this.value, pattern);

  33. }

  34. }

现在,我们用 matches 方法来做一个简单的 Javascript 标识符判断功能。简单的理解,Javascript 标识符的组成,可以利用半角字母“A”至“Z”,半角数字“0”至“9”,以及符号“$”和“_”反复重复,其中字母并不区分大小写,并且标识符的首字母不能是数字。在构造正则表达式样式之前,我们先考虑下大小写问题,对于标识符来说,字母的大小写形式都是允许,这里有两个方案,一个是直接利用一个[A-Za-z$_][0-9A-Za-z$_]*样式,另外,我们还可以考虑使用正则表达式引擎的忽略大小写选项来达到不区分大小写匹配的功能,代码如下:

  1. /**

  2. * Java版本测试类

  3. */

  4. public class Main {

  5. /**

  6. * @param args

  7. */

  8. public static void main(String[] args) {

  9. // 匹配样式

  10. String pattern = "^[0-9A-Za-z$_]+$";

  11. // 测试字符串

  12. String[] testIdentifiers = new String[] {"$123",

  13. "hello world", "_int_value", "$_POST", "ok"};

  14. for (int i = 0; i < testIdentifiers.length; i++) {

  15. System.out.println("String /"" + testIdentifiers[i] + "/" is" +

  16. // matches 为 false 时,返回字符串 not

  17. (testIdentifiers[i].matches(pattern) ? "" : " not") +

  18. " a valid identifier.");

  19. }

  20. }

  21. }

  1. /// <summary>

  2. /// C#版本测试类

  3. /// </summary>

  4. class Tester

  5. {

  6. public static void Main(string[] args)

  7. {

  8. // 匹配样式

  9. String pattern = "^(?i)[A-Za-z$_][0-9A-Za-z$_]*$";

  10. // 测试字符串

  11. StringWrapper[] testIdentifiers = new StringWrapper[] {new StringWrapper("$123"),

  12. new StringWrapper("hello world"), new StringWrapper("_int_value"),

  13. new StringWrapper("$_POST"), new StringWrapper("ok")};

  14. for (int i = 0; i < testIdentifiers.Length; i++) {

  15. Console.WriteLine("String /"" + testIdentifiers[i] + "/" is" +

  16. // matches 为 false 时,返回字符串 not

  17. (testIdentifiers[i].matches(pattern) ? "" : " not") +

  18. " a valid identifier.");

  19. }

  20. Console.ReadLine();

  21. }

  22. }

设置正则表达式引擎的选项时,有两种备选方案:一种是利用选项参数显示地设置参数值,另一种则是采用内联方式进行指定,即把选项参数嵌套到正则表达式中。上面代码采用的就是内联方式,样式前方的(?i)就是告诉正则表达式引擎进行匹配时,不区分字母大小写,除了大小写之外还有多行模式(?m)、单行模式(?s)等选项,具体用法可以参考相关的说明文档。Java 和 .Net 的内联选项分别为 (?idmsux-idmsux)(?imnsx-imnsx),同时,它们还有一个非捕获群的版本(?idmsux-idmsux:)(?imnsx-imnsx:)。在“?”之后我们可以直接指明正则表达式选项,选项前面的符号“-”表示可以禁止某些选项,如果在符号“-”前后指明相同的选项,后面的选项设置会覆盖前面相同的选项设置,表示该选项被禁用,在默认的情况下,所有的正则表达式选项都会被关闭。

非捕获群版本的正则表达式选项和正则表达式选项的位置,都是影响选项作用范围的因素。无论在正则表达式样式的哪个位置使用非捕获群版本的选项时,只有选项中的样式以及嵌套在选项中的捕获群或非捕获群才会受到设置的选项影响,选项之外的内容仍会按照未设置选项时的规则进行样式匹配;而在使用一般的正则表达式选项设置样式时,在选项位置之后且与选项属于同一捕获群中的样式以及嵌套在这个样式中的捕获群或非捕获群才会受到设置的选项影响,样式之外的内容仍按照未设置选项时的规则进行样式匹配。下面的代码演示了几种内联选项的使用方法:

  1. /**

  2. * Java版本测试类

  3. */

  4. public class Main {

  5. /**

  6. * @param args

  7. */

  8. public static void main(String[] args) {

  9. // 在最前设置选项,选项之后内容受到影响,结果:true

  10. System.out.println("xABCABC".matches("(?i)Xabcabc"));

  11. // 在最前设置选项,选项之后,同一群组中内容受到影响,结果:false

  12. System.out.println("xABCABC".matches("((?i)Xabc)abc"));

  13. // 在最前设置选项,选项之后,同一群组中内容受到影响,结果:true

  14. System.out.println("xABCABC".matches("((?i)Xabc)ABC"));

  15. // 在最前设置选项,与选项在同一群组以及嵌套于同一群组的内容受到影响,结果:true

  16. System.out.println("xABCABC".matches("((?i)X(ab)c)ABC"));

  17. // 在中间设置选项,选项之后内容受到影响,结果:true

  18. System.out.println("xABCABC".matches("xAB(?i)caBc"));

  19. // 在中间设置选项,选项之后内容受到影响,结果:false

  20. System.out.println("xABCABC".matches("xABCaB(?i)c"));

  21. // 在中间设置选项,选项内部内容受到影响,结果:true

  22. System.out.println("xABCABC".matches("x(?i:abc)ABC"));

  23. // 在中间设置选项,选项内部内容受到影响,结果:false

  24. System.out.println("xABCABC".matches("x(?i:abc)abc"));

  25. // 在中间设置选项,选项内部内容受到影响,结果:false

  26. System.out.println("xABCABC".matches("x(?i:a(Bc)A)bc"));

  27. // 在中间设置选项,选项内部内容受到影响,结果:true

  28. System.out.println("xABCABC".matches("x(?i:a((b)C)a)BC"));

  29. // 在中间设置选项,选项内部内容以及嵌套捕获群受到影响,结果:true

  30. System.out.println("xABCABC".matches("xAB(?i:C(a)b)C"));

  31. // 在最后设置选项,选项内部内容受到影响,结果:false

  32. System.out.println("xABCABC".matches("xabcabc(?i)"));

  33. // 在最前设置选项,选项之后的样式以及样式中捕获群或非捕获群受到影响,结果:true

  34. System.out.println("xABCABC".matches("((?i)Xa(?:b)(c))ABC"));

  35. }

  36. }

  1. /// <summary>

  2. /// C#版本测试类

  3. /// </summary>

  4. class Tester

  5. {

  6. public static void Main(string[] args)

  7. {

  8. // 在最前设置选项,选项之后内容受到影响,结果:true

  9. Console.WriteLine(new StringWrapper("xABCABC").matches("(?i)Xabcabc"));

  10. // 在最前设置选项,选项之后,同一群组中内容受到影响,结果:false

  11. Console.WriteLine(new StringWrapper("xABCABC").matches("((?i)Xabc)abc"));

  12. // 在最前设置选项,选项之后,同一群组中内容受到影响,结果:true

  13. Console.WriteLine(new StringWrapper("xABCABC").matches("((?i)Xabc)ABC"));

  14. // 在最前设置选项,与选项在同一群组以及嵌套于同一群组的内容受到影响,结果:true

  15. Console.WriteLine(new StringWrapper("xABCABC").matches("((?i)X(ab)c)ABC"));

  16. // 在中间设置选项,选项之后内容受到影响,结果:true

  17. Console.WriteLine(new StringWrapper("xABCABC").matches("xAB(?i)caBc"));

  18. // 在中间设置选项,选项之后内容受到影响,结果:false

  19. Console.WriteLine(new StringWrapper("xABCABC").matches("xABCaB(?i)c"));

  20. // 在中间设置选项,选项内部内容受到影响,结果:true

  21. Console.WriteLine(new StringWrapper("xABCABC").matches("x(?i:abc)ABC"));

  22. // 在中间设置选项,选项内部内容受到影响,结果:false

  23. Console.WriteLine(new StringWrapper("xABCABC").matches("x(?i:abc)abc"));

  24. // 在中间设置选项,选项内部内容受到影响,结果:false

  25. Console.WriteLine(new StringWrapper("xABCABC").matches("x(?i:a(Bc)A)bc"));

  26. // 在中间设置选项,选项内部内容受到影响,结果:true

  27. Console.WriteLine(new StringWrapper("xABCABC").matches("x(?i:a((b)C)a)BC"));

  28. // 在中间设置选项,选项内部内容以及嵌套捕获群受到影响,结果:true

  29. Console.WriteLine(new StringWrapper("xABCABC").matches("xAB(?i:C(a)b)C"));

  30. // 在最后设置选项,选项内部内容受到影响,结果:false

  31. Console.WriteLine(new StringWrapper("xABCABC").matches("xabcabc(?i)"));

  32. // 在最前设置选项,选项之后的样式以及样式中捕获群或非捕获群受到影响,结果:true

  33. Console.WriteLine(new StringWrapper("xABCABC").matches("((?i)Xa(?:b)(c))ABC"));

  34. Console.ReadLine();

  35. }

  36. }

现在我们来看下第二个例子。首先构造一个 Book 类,其中含有四个属性,出版商、书名、版本和出版时间,然后从含有这些信息的字符串数组中将相关的内容提取到 Book 类的各个属性中。比如字符串“Addison Wesley - Introduction To SQL - Mastering The Relational Database Language, 4th Edition, Sep 2006”,第一个“-”之前的内容为出版商信息,第一个“-”之后与“Edition”之前的一个“,”之间的内容为书名,书名至“Edition”间的内容为版本信息,最后剩下的就是出版日期了。首先需要构造一个含有四个捕获群的正则表达式,每个群组都代表着 Book 的一个属性,其中代表“版本”的第三个捕获群是可选的,按照这种思路,我们可以构造样式(?i)(.+?-)(.+,)(.+?Edition)?(.+),并且在样式最前方加上一个内联的选项(?i)来忽略对“Edition”的大小写匹配,然后利用相关的 API 把每个捕获群的内容提取出来分别设置到对应的属性中去,下面的代码示例演示的就是如何利用正则表达式样式和相关编程语言的 API 来取得所有的属性。

  1. import java.util.regex.Matcher;

  2. import java.util.regex.Pattern;

  3.  

  4. /**

  5. * Java版本测试类

  6. */

  7. public class Main {

  8.  

  9. /**

  10. * @param args

  11. */

  12. public static void main(String[] args) {

  13. // 测试字符串

  14. String[] bookNames = new String[] {

  15. "Apress - Java Regular Expressions Taming the java.util.regex Engine, 2004",

  16. "O'Reilly - Learning Perl, 3rd Edition, Jul 2001",

  17. "O'Reilly - SWT - A Developer's Notebook, Oct 2004",

  18. "Wiley - Excel 2007 Power Programming with VBA, Apr 2007",

  19. "O'Reilly - JavaScript The Definitive Guide, 5th Edition, Aug 2006",

  20. "O'Reilly - CSS The Missing Manual, Aug 2006" };

  21. // 匹配样式

  22. String bookPattern = "(?i)(.+?-)(.+,)(.+?Edition)?(.+)";

  23. // 编译Pattern

  24. Pattern pattern = Pattern.compile(bookPattern);

  25. Matcher matcher = null;

  26. for (int i = 0; i < bookNames.length; i++) {

  27. String bookName = bookNames[i];

  28. // 进行匹配操作

  29. matcher = pattern.matcher(bookName);

  30. while (matcher.find()) {

  31. // 输出捕获群

  32. println(matcher.group(1));

  33. println(matcher.group(2));

  34. println(matcher.group(3));

  35. println(matcher.group(4));

  36. println("==============");

  37. }

  38. }

  39. }

  40.  

  41. /**

  42. * 格式化打印

  43. *

  44. * @param value

  45. */

  46. public static void println(String value) {

  47. if (value == null) {

  48. System.out.println();

  49. } else {

  50. System.out.println(value);

  51. }

  52. }

  53. }

  1. /// <summary>

  2. /// C#版本测试类

  3. /// </summary>

  4. class Tester

  5. {

  6. /// <summary>

  7. ///

  8. /// </summary>

  9. /// <param name="args"></param>

  10. public static void Main(string[] args)

  11. {

  12. // 测试字符串

  13. String[] bookNames = new String[] {

  14. "Apress - Java Regular Expressions Taming the java.util.regex Engine, 2004",

  15. "O'Reilly - Learning Perl, 3rd Edition, Jul 2001",

  16. "O'Reilly - SWT - A Developer's Notebook, Oct 2004",

  17. "Wiley - Excel 2007 Power Programming with VBA, Apr 2007",

  18. "O'Reilly - JavaScript The Definitive Guide, 5th Edition, Aug 2006",

  19. "O'Reilly - CSS The Missing Manual, Aug 2006" };

  20. // 匹配样式

  21. String bookPattern = "(?i)(.+?-)(.+,)(.+?Edition)?(.+)";

  22. // 编译Pattern

  23. Regex regex = new Regex(bookPattern);

  24. Match match = null;

  25. for (int i = 0; i < bookNames.Length; i++)

  26. {

  27. String bookName = bookNames[i];

  28. // 进行匹配操作

  29. match = regex.Match(bookName);

  30. while (match.Success)

  31. {

  32. // 输出捕获群

  33. println(match.Groups[1].Value);

  34. println(match.Groups[2].Value);

  35. println(match.Groups[3].Value);

  36. println(match.Groups[4].Value);

  37. println("==============");

  38. match = match.NextMatch();

  39. }

  40. }

  41. Console.ReadLine();

  42. }

  43.  

  44. /// <summary>

  45. /// 格式化打印

  46. /// </summary>

  47. /// <param name="value"></param>

  48. public static void println(String value)

  49. {

  50. if (value == null)

  51. {

  52. Console.WriteLine();

  53. }

  54. else

  55. {

  56. Console.WriteLine(value);

  57. }

  58. }

  59. }

Java 和 .Net 的 API 操作方法很相似,都是先获取一个 match 对象,然后通过 match 对象得到样式中所代表的各个捕获群。这里要注意的是,通过索引值来取出捕获群的内容时,下标要从 1 开始,因为 0 代表的是整个表达式,而非第一个捕获群。下面继续介绍 match 的另一种操作方法,就是从一个指定的字符串中,利用一个正则表达式样式反复进行匹配操作,然后将匹配的结果进行处理,示例代码如下:

  1. import java.util.regex.Matcher;

  2. import java.util.regex.Pattern;

  3.  

  4. /**

  5. * Java版本测试类

  6. */

  7. public class Main {

  8.  

  9. /**

  10. * @param args

  11. */

  12. public static void main(String[] args) {

  13. // 测试字符串

  14. String bookNames = "Apress - Java Regular Expressions Taming the java.util.regex Engine, 2004/n" +

  15. "O'Reilly - Learning Perl, 3rd Edition, Jul 2001/n" +

  16. "O'Reilly - SWT - A Developer's Notebook, Oct 2004/n" +

  17. "Wiley - Excel 2007 Power Programming with VBA, Apr 2007/n" +

  18. "O'Reilly - JavaScript The Definitive Guide, 5th Edition, Aug 2006/n" +

  19. "O'Reilly - CSS The Missing Manual, Aug 2006";

  20. // 匹配样式

  21. String bookPattern = "(?i)(.+?-)(.+,)(.+?Edition)?(.+)";

  22. // 编译Pattern

  23. Pattern pattern = Pattern.compile(bookPattern);

  24. Matcher matcher = null;

  25. // 进行匹配操作

  26. matcher = pattern.matcher(bookNames);

  27. while (matcher.find()) {

  28. // 输出捕获群

  29. println(matcher.group(1));

  30. println(matcher.group(2));

  31. println(matcher.group(3));

  32. println(matcher.group(4));

  33. println("==============");

  34. }

  35. }

  36.  

  37. /**

  38. * 格式化打印

  39. *

  40. * @param value

  41. */

  42. public static void println(String value) {

  43. if (value == null) {

  44. System.out.println();

  45. } else {

  46. System.out.println(value);

  47. }

  48. }

  49. }

  1. /// <summary>

  2. /// C#版本测试类

  3. /// </summary>

  4. class Tester

  5. {

  6. /// <summary>

  7. ///

  8. /// </summary>

  9. /// <param name="args"></param>

  10. public static void Main(string[] args)

  11. {

  12. // 测试字符串

  13. String bookNames = "Apress - Java Regular Expressions Taming the java.util.regex Engine, 2004/n" +

  14. "O'Reilly - Learning Perl, 3rd Edition, Jul 2001/n" +

  15. "O'Reilly - SWT - A Developer's Notebook, Oct 2004/n" +

  16. "Wiley - Excel 2007 Power Programming with VBA, Apr 2007/n" +

  17. "O'Reilly - JavaScript The Definitive Guide, 5th Edition, Aug 2006/n" +

  18. "O'Reilly - CSS The Missing Manual, Aug 2006";

  19. // 匹配样式

  20. String bookPattern = "(?i)(.+?-)(.+,)(.+?Edition)?(.+)";

  21. // 编译Pattern

  22. Regex regex = new Regex(bookPattern);

  23. Match match = null;

  24. // 进行匹配操作

  25. match = regex.Match(bookNames);

  26. while (match.Success)

  27. {

  28. // 输出捕获群

  29. println(match.Groups[1].Value);

  30. println(match.Groups[2].Value);

  31. println(match.Groups[3].Value);

  32. println(match.Groups[4].Value);

  33. println("==============");

  34. match = match.NextMatch();

  35. }

  36. Console.ReadLine();

  37. }

  38.  

  39. /// <summary>

  40. /// 格式化打印

  41. /// </summary>

  42. /// <param name="value"></param>

  43. public static void println(String value)

  44. {

  45. if (value == null)

  46. {

  47. Console.WriteLine();

  48. }

  49. else

  50. {

  51. Console.WriteLine(value);

  52. }

  53. }

  54. }

从上面几个 match 的例子中,我们可以总结出三点内容:第一,字符串所提供的 match 或正则表达式 API 引擎中所提供的静态方法可以进行简单的字符串匹配判断;第二,可以利用 match 类所提供的方法,按索引从捕获群中取得相关的内容;第三,根据指定的正则表达式样式,从整个字符串中反复进行匹配,依次取得与样式匹配的内容,该内容可以用 match 对象所代表,所以我们可以利用第二条来继续进行更为细致的操作。

在 Java 中,只用一个 Matcher 类就可以达到对匹配的内容进行操作的目的,而 .Net 则是采用了逐层细化的方式,将匹配的内容用 Match、Group 和 Capture 来表示,然后可以通过这三个类来对结果进行操作,这样就可以获取更大的灵活性了。比如我们用正则表达式样式(abc)*来匹配字符串“abcabcabc”,首先得到一个 match 对象,然后通过该对象得到 group,再用 group 取得 capture。由于表达式中只有一组圆括号,所以 group 的值就是最后一次成功匹配的内容,即最后一组 abc;capture 的作用就是记录每次匹配的结果,所以匹配的结果中有三个 capture 对象,每个 capture 记录一组 abc。

四、replace 的使用

在 Java 的 String 类中,有两个利用正则表达式进行字符串替换的方法:replaceFirst 和 replaceAll。调用方式分别是 str.replaceAll(regex, replacement) 和 str.replaceFirst(regex, replacement),参数 regex 和 replacement 都是字符串类型,前面一个是正则表达式,后面是要进行替换的内容,它们的执行结果都是替换操作后的字符串。虽然 .Net 并没有为 String 类型提供这两种方法,但我们可以仿照 Java 的 String 类的 replaceFirst 和 replaceAll 的实现,然后利用 .Net 命名空间 System.Text.RegularExpressions 中 Regex 类的 replace 方法(参考 Regex.Replace (String, String, Int32) 和 Regex.Replace (String, String))来为 StringWrapper 增加这两个方法。

现在,我们来看一个例子,把下面表格中的左边 Java 代码多行注释,改写成右边的单行注释的形式。


  1. // Sample Code

  2. package info.lession;

  3.  

  4. public class MainRunner {

  5.  

  6. /**

  7. * @param args

  8. */

  9. public static void main(String[] args) {

  10. // 添加任务处理器

  11. TaskProcessorManager.addProcessor(null);

  12.  

  13. // 执行任务

  14. TaskProcessorManager.processAll();

  15. }

  16. }


  1. // Sample Code

  2. package info.lession;

  3.  

  4. public class MainRunner {

  5.  

  6. /** @param args */

  7. public static void main(String[] args) {

  8. // 添加任务处理器

  9. TaskProcessorManager.addProcessor(null);

  10.  

  11. // 执行任务

  12. TaskProcessorManager.processAll();

  13. }

  14. }

首先要构造正则表达式(?s)/[*].*?[*]/来匹配我们要修改的注释,然后使用replaceAll方法进行替换。


  1. public static void convertCommentFromMultilineToSingle() throws Exception {

  2. BufferedReader reader = new BufferedReader(new FileReader(

  3. new File("C://SampleCode.txt")));

  4. String lineString = null;

  5. StringBuilder stringBuilder = new StringBuilder();

  6. FileWriter fileWriter = new FileWriter("C://x.txt");

  7. while ((lineString = reader.readLine()) != null) {

  8. fileWriter.write(lineString + System.getProperty("line.separator"));

  9. // stringBuilder.append(new String(lineString.getBytes("ISO-8859-1"), "UTF-8"))

  10. stringBuilder.append(lineString)

  11. .append(System.getProperty("line.separator"));

  12. }

  13. // 为多行注释做标记

  14. lineString = stringBuilder.toString().replaceAll("(?s)/[*](.*?)[*]/",

  15. "#标记开始#$1#标记结束#");

  16. // 多行注释转单行

  17. lineString.replaceAll("(?s)#标记开始#((//s*)?([^//s]+)(//s*)?)*?#标记结束#", "123");

  18. fileWriter.close();

  19. System.out.println(lineString);

  20. }

五、split 的使用

split 方法的作用是把原始字符串按照一定样式规则进行分割,然后再把分割后的结果字符串放到一个数组中,这样我们就可以遍历其中每一个分割的结果内容了。String 类为我们提供了两个 split 方法,一个就是我们通常理解的安分割样式提取字符,另一个更为灵活一些,可以让我们指定应用分割样式的次数以便得到不同的分割结果。通过 java.util.regex.Pattern 创建的 Pattern 对象所提供的 split 方法的操作结果与 String 类的 split 方法的操作结果是相同。在结果数组中,被提取出来的字符串顺序与它们在被分割字符串中的先后位置保持一致。

在 Sun 的 Javadoc 中有这样一个例子,要求对字符串“boo:and:foo”分别按字符“:”和“o”进行分割。下面表格是不同的参数样式对应的 split 方法执行后的结果:

Regex Limit Result
: 2 { "boo", "and:foo" }
: 5 { "boo", "and", "foo" }
: -2 { "boo", "and", "foo" }
o 5 { "b", "", ":and:f", "", "" }
o -2 { "b", "", ":and:f", "", "" }
o 0 { "b", "", ":and:f" }

参数 limit 能限制正则表达式被应用的次数,从而也就能影响到最终结果字符串的长度。假设 limit 参数值为 正整数 n,操作的结果就应该是 n + 1,这种情况只适用于分割符的数量大于等于 n。在 Java 中,如果指定了参数值为正整数 n,那么实际上执行分割操作的次数就为 n - 1,务必要记住这一点,表格中第一个结果就属于这种情况。如果 n 为非正整数,字符串就会被尽可能多的被分割,表格中的第三个、第五个和第六个结果就属于这种情况,但当 n 为零的时候,split 会自动地把结果字符串后面的空项抛弃。

现在我们用下面的表格来分多步分析上面表格中第四个结果。这里有两个要注意的地方就是第二步和第四步,“o:and:foo”被分割后的结果是一个空字符串和“:and:foo”,因为位于第一个“o”的前端没有任何字符,所以就被指派了一个空字符串;同理,第四步的“o”的两端没有任何字符,分割后的结果,也就是两个空字符串了。

步骤 结果
{ "b", "o:and:foo" }
{ "b", "", ":and:foo" }
{ "b", "", ":and:f", "o" }
{ "b", "", ":and:f", "", "" }

对于 .Net 的也有与之类似的功能,请参考"System_Text_RegularExpressions_Regex_Split".split(/_/g).join(".")。 // :p Run with a kind of ECMAScript