zoukankan      html  css  js  c++  java
  • Java中的正则表达式包含embedded flags(转)

    JDK 1.4提供了内建的正则表达式支持,相应的,String类也提供了许多与正则表达式有关的方法,例如matches、replaceAll和split方法,为日常应用提供了许多便利。
    在工作中,我发现,掌握一些不常用的技巧,往往可以极大地提高效率,以下是我总结的一点经验:

    1.合理利用embedded flag。
    某些时候,我们会遇到这样的情况:
    要求匹配一个字符串,例如abcdefg,其中abc必须为小写,def大小写无所谓,g为小写。
    此类问题,通常的做法是这样生成一个Pattern:

    Pattern p = Pattern.compile("abcdefg", Pattern.CASE_INSENSITIVE);然而,此时这样做不能满足我们的要求,此时可借助embedded flag。

    查阅JDK文档,在Special construts(non-capturing)中可以看到:
    (?idmsux-idmsux)       Nothing, but turns match flags on - off
    (?idmsux-idmsux:X)        X, as a non-capturing group with the given flags on - off

    其中,各个字母的含义为
    embedded                      flags construction flags meanings
    i                Pattern.CASE_INSENSITIVE Enables case-insensitive matching.
    d               Pattern.UNIX_LINES Enables Unix lines mode.
    m              Pattern.MULTILINE Enables multi line mode.
    s               Pattern.DOTALL Enables "." to match line terminators.
    u               Pattern.UNICODE_CASE Enables Unicode-aware case folding.
    x               Pattern.COMMENTS Permits white space and comments in the pattern.
    ---             Pattern.CANON_EQ Enables canonical equivalence.


    如此一来,我们就可写出符合要求的Pattern字符串:

    Pattern p = Pattern.compile("abc(?i)def(?-i)g");

    在这个Pattern中,我们在def子串之前设置了CASE_INSENSITIVE为on,在之后将其设置为off,保证了abcg都为小写,而def可以为大小写。

    有人可能会认为,不需要那么复杂,直接写

    Pattern p = Pattern.compile("abc[dD][eE][fF]g");

    也可以解决问题。

    确实,在这种情况下,两种办法的效果是一样。然而,有时候我们并不能事先确定字符串的内容(例如,需要从HTML代码中提取一个结点,也就是一对tag之间的内容,而tag是运行时动态决定的),此时只能借助match flag。

    如果只修改了一次match flag,那么它的作用范围将从此处开始,一直到延伸到Pattern末尾,也就是说下面两个Pattern:

    Pattern p1 = Pattern.compile("(?is)abcdefg");

    Pattern p2 = Pattern.compile("abcdefg", PATTERN.CASE_INSENSITIVE | Pattern.DOTALL);

    它们的作用是一样的。

    match flag可以组合使用,例如(?is)表示大小写不敏感,同时.可以用来匹配换行符;同时,match flag也可以与外部的int flag同时使用,例如:

    Pattern pattern = Pattern.compile( "(?-i:[A-Z])[A-Z]*" , Pattern.CASE_INSENSITIVE);

    代表第一个字符为大写英文字母的单词,需要注意的是,此时第一个字符处于non-capturing group中,因此在group count时要格外注意。

    一般情况下,我们都是调用String类的matches方法验证字符串的合法性,遇上复杂的情况,合理使用match flag会让我们事半功倍。

    2.利用split
    在引入split之前,如果需要分割字符串,必须使用StringTokenizer类,使用非常麻烦,而且缺乏灵活性(因为不容许分割的字符串有多种选择),JDK1.4引入了split方法,容许利用正则表达式分割字符串,非常方便。

    以前我们也许需要这样写:

    StringTokenizer st = new StringTokenizer("this is a test");
    while (st.hasMoreTokens()){
        System.out.println(st.nextToken());
    }

    现在只需要这样:

    String[] words = "this is a test".split("\\s");
    for(string/ s : words){ 
    System.
    out.println(s);
    }

    3.关于replaceAll
    JDK1.4为String引入了replaceAll方法,可以方便地进行字符串替换

    String source = "abcdefg";

    String result = source.replaceAll("bcd", "BCD");

    System.out.println("result is: " + result);

    得到的结果是
    aBCDefg

    需要注意的是,replaceAll的两个参数,都是正则表达式,按照JDK的文档

    str.replaceAll(regex, repl)等价于

    Pattern.compile(regex).matcher(str).replaceAll(repl)
    某些情况下,我们需要在替换的同时用到back reference,也就是说,被替换的内容是不确定的,而替换的结果又是与这些被替换内容相关的,此时就会发现使用正则表达式的好处了。
    例如,某段文本中出现许多“班级-学号”(例如,172-04)的字符串,需要更改为“学号-班级”(04-172)的格式,我们可以在替换结果中利用$符号对被替换内容中的部分进行back reference:

    String source = "172-04";
    System.out.println(source.replaceAll(
    "(\\d+)-(\\d+)","$2-$1");

    结果为
    04-172 

  • 相关阅读:
    洛谷P3224 [HNOI2012]永无乡 线段树合并
    洛谷P3605 [USACO17JAN]Promotion Counting——线段树合并
    python之三元表达式、列表推导、生成器表达式、递归、匿名函数、内置函数
    python之迭代器、生成器与面向过程编程
    Python之函数对象、函数嵌套、名称空间与作用域、闭包函数、装饰器
    Python之函数基础
    Python之字符编码与文件操作
    Python基本数据类型
    python介绍
    编程基础
  • 原文地址:https://www.cnblogs.com/Fskjb/p/1654593.html
Copyright © 2011-2022 走看看