精华内容
下载资源
问答
  • Java Pattern和Matcher字符匹配详解

    万次阅读 多人点赞 2017-09-02 21:40:33
    Pattern类定义  public final class Pattern extends Object implementsSerializable正则表达式的编译表示形式。用于编译正则表达式后创建一个匹配模式。  指定为字符串的正则表达式必须首先被编译为此类的实例...

    Pattern类定义

             public final class Pattern extends Object implementsSerializable正则表达式的编译表示形式。用于编译正则表达式后创建一个匹配模式。

            指定为字符串的正则表达式必须首先被编译为此类的实例。然后,可将得到的模式用于创建Matcher对象,依照正则表达式,该对象可以与任意字符序列匹配。执行匹配所涉及的所有状态都驻留在匹配器中,所以多个匹配器可以共享同一模式。

            因此,典型的调用顺序是:

            Pattern p =Pattern.compile("a*b");

            Matcher m =p.matcher("aaaaab");

            boolean b = m.matches();

            在仅使用一次正则表达式时,可以方便地通过此类定义matches方法。此方法编译表达式并在单个调用中将输入序列与其匹配。语句:

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

            等效于上面的三个语句,尽管对于重复的匹配而言它效率不高,因为它不允许重用已编译的模式。

            此类的实例是不可变的,可供多个并发线程安全使用。Matcher类的实例用于此目的则不安全。


    Pattern类方法详解

            1Pattern complie(String regex):编译正则表达式,并创建Pattern类。

            由于Pattern的构造函数是私有的,不可以直接创建,所以通过静态的简单工厂方法compile(String regex)方法来创建,将给定的正则表达式编译并赋予给Pattern类。

            2String pattern():返回正则表达式的字符串形式。

            其实就是返回Pattern.complile(Stringregex)的regex参数。示例如下:

            String regex ="\\?|\\*";

            Pattern pattern= Pattern.compile(regex);

            StringpatternStr = pattern.pattern();//返回\?\*

            3Pattern compile(String regex, int flags)

            方法功能和compile(Stringregex)相同,不过增加了flag参数,flag参数用来控制正则表达式的匹配行为,可取值范围如下:

            Pattern.CANON_EQ启用规范等价。当且仅当两个字符的“正规分解(canonicaldecomposition)”都完全相同的情况下,才认定匹配。默认情况下,不考虑“规范相等性(canonical equivalence)”。

            Pattern.CASE_INSENSITIVE启用不区分大小写的匹配。默认情况下,大小写不敏感的匹配只适用于US-ASCII字符集。这个标志能让表达式忽略大小写进行匹配,要想对Unicode字符进行大小不敏感的匹配,只要将UNICODE_CASE与这个标志合起来就行了。

            Pattern.COMMENTS模式中允许空白和注释。在这种模式下,匹配时会忽略(正则表达式里的)空格字符(不是指表达式里的“\s”,而是指表达式里的空格,tab,回车之类)。注释从#开始,一直到这行结束。可以通过嵌入式的标志来启用Unix行模式。

            Pattern.DOTALL启用dotall模式。在这种模式下,表达式‘.’可以匹配任意字符,包括表示一行的结束符。默认情况下,表达式‘.’不匹配行的结束符。

            Pattern.LITERAL:启用模式的字面值解析。

            Pattern.MULTILINE启用多行模式。在这种模式下,\^’和‘$’分别匹配一行的开始和结束。此外,^’仍然匹配字符串的开始,‘$’也匹配字符串的结束。默认情况下,这两个表达式仅仅匹配字符串的开始和结束。

            Pattern.UNICODE_CASE启用Unicode感知的大小写折叠。在这个模式下,如果你还启用了CASE_INSENSITIVE标志,那么它会对Unicode字符进行大小写不敏感的匹配。默认情况下,大小写不敏感的匹配只适用于US-ASCII字符集。

            Pattern.UNIX_LINES  启用Unix行模式。在这个模式下,只有‘\n’才被认作一行的中止,并且与‘.’、‘^’、以及‘$’进行匹配。

            4int flags():返回当前Pattern的匹配flag参数。

            5String[] split(CharSequence input)

            Pattern有一个split(CharSequenceinput)方法,用于分隔字符串,并返回一个String[]。此外String[] split(CharSequence input, int limit)功能和String[]split(CharSequence input)相同,增加参数limit目的在于要指定分割的段数。

            6static boolean matches(String regex, CharSequenceinput)

            是一个静态方法,用于快速匹配字符串,该方法适合用于只匹配一次,且匹配全部字符串。方法编译给定的正则表达式并且对输入的字串以该正则表达式为模式开展匹配,该方法只进行一次匹配工作,并不需要生成一个Matcher实例。

            7Matcher matcher(CharSequence input)

            Pattern.matcher(CharSequenceinput)返回一个Matcher对象。Matcher类的构造方法也是私有的,不能随意创建,只能通过Pattern.matcher(CharSequence input)方法得到该类的实例。Pattern类只能做一些简单的匹配操作,要想得到更强更便捷的正则匹配操作,那就需要将Pattern与Matcher一起合作。Matcher类提供了对正则表达式的分组支持,以及对正则表达式的多次匹配支持。

            Java代码示例:

            Pattern p = Pattern.compile("\\d+");

            Matcher m = p.matcher("22bb23");

            // 返回p也就是返回该Matcher对象是由哪个Pattern对象的创建的

            m.pattern();


    Pattern类使用示例:

    package com.zxt.regex;
     
    import java.util.regex.Pattern;
     
    public classPatternTest {
     
        public static void main(String[] args) {
     
            // 使用Pattern.compile方法编译一个正则表达式,创建一个匹配模式
            Patternpattern = Pattern.compile("\\?|\\*");
     
           
            // pattern()函数返回正则表达式的字符串形式返回\?\*
            StringpatternStr = pattern.pattern();
            System.out.println(patternStr);
            // flags()返回当前Pattern的匹配flag参数,这里并没有定义
            int flag = pattern.flags();
            System.out.println(flag);
     
           
            // split方法对字符串进行分割
            // 123 123 456 456
            String[]splitStrs = pattern.split("123?123*456*456");
            for (int i = 0; i < splitStrs.length; i++) {
                System.out.print(splitStrs[i] + "  ");
            }
            System.out.println();
           
            // 123 123*456*456
            String[]splitStrs2 = pattern.split("123?123*456*456",2);
            for (int i = 0; i < splitStrs2.length; i++) {
                System.out.print(splitStrs2[i] + "  ");
            }
            System.out.println();
     
            Patternp = Pattern.compile("\\d+");
            String[]str = p.split("我的QQ是:456456我的电话是:0532214我的邮箱是:aaa@aaa.com");
            for (int i = 0; i < str.length; i++) {
                System.out.printf("str[%d] = %s\n",i, str[i]);
            }
            System.out.println();
     
           
            // Pattern.matches用给定的模式对字符串进行一次匹配,(需要全匹配时才返回true)
            System.out.println("Pattern.matches(\"\\\\d+\",\"2223\") is " + Pattern.matches("\\d+", "2223"));
            // 返回false,需要匹配到所有字符串才能返回true,这里aa不能匹配到
            System.out.println("Pattern.matches(\"\\\\d+\", \"2223aa\")is " + Pattern.matches("\\d+", "2223aa"));
            // 返回false,需要匹配到所有字符串才能返回true,这里bb不能匹配到
            System.out.println("Pattern.matches(\"\\\\d+\",\"22bb23\") is " + Pattern.matches("\\d+", "22bb23"));
     
        }
     
    }


    Matcher类定义

            public final class Matcher extends Object implementsMatchResult通过调用模式(Pattern)的matcher方法从模式创建匹配器。创建匹配器后,可以使用它执行三种不同的匹配操作:

            1matches方法尝试将整个输入序列与该模式匹配。

            2lookingAt尝试将输入序列从头开始与该模式匹配。

            3find方法扫描输入序列以查找与该模式匹配的下一个子序列。

            每个方法都返回一个表示成功或失败的布尔值。通过查询匹配器的状态可以获取关于成功匹配的更多信息。

            匹配器在其输入的子集(称为区域)中查找匹配项。默认情况下,此区域包含全部的匹配器输入。可通过region方法修改区域,通过regionStart和regionEnd方法查询区域。区域边界与某些模式构造交互的方式是可以更改的。

            此类还定义使用新字符串替换匹配子序列的方法,需要时,可以从匹配结果计算出新字符串的内容。可以先后使用appendReplacement和appendTail方法将结果收集到现有的字符串缓冲区,或者使用更加便捷的replaceAll方法创建一个可以在其中替换输入序列中每个匹配子序列的字符串。

            匹配器的显式状态包括最近成功匹配的开始和结束索引。它还包括模式中每个捕获组捕获的输入子序列的开始和结束索引以及该子序列的总数。出于方便的考虑,还提供了以字符串的形式返回这些已捕获子序列的方法。

            匹配器的显式状态最初是未定义的;在成功匹配导致IllegalStateException抛出之前尝试查询其中的任何部分。每个匹配操作都将重新计算匹配器的显式状态。匹配器的隐式状态包括输入字符序列和添加位置,添加位置最初是零,然后由appendReplacement方法更新。

            可以通过调用匹配器的reset()方法来显式重置匹配器,如果需要新输入序列,则调用其reset(CharSequence)方法。重置匹配器将放弃其显式状态信息并将添加位置设置为零。

            此类的实例用于多个并发线程是不安全的。


    Matcher类方法详解

            1、Matcher类提供了三个匹配操作方法,三个方法均返回boolean类型,当匹配到时返回true,没匹配到则返回false。

            boolean matches()最常用方法:尝试对整个目标字符展开匹配检测,也就是只有整个目标字符串完全匹配时才返回真值。

            boolean lookingAt()对前面的字符串进行匹配,只有匹配到的字符串在最前面才会返回true

            boolean find():对字符串进行匹配,匹配到的字符串可以在任何位置。

            2、返回匹配器的显示状态:intstart():返回当前匹配到的字符串在原目标字符串中的位置;int end():返回当前匹配的字符串的最后一个字符在原目标字符串中的索引位置;String group():返回匹配到的子字符串。

            3、int start(),int end(),int group()均有一个重载方法,它们分别是int start(int i),int end(int i),int group(int i)专用于分组操作,Mathcer类还有一个groupCount()用于返回有多少组。

            4、Matcher类同时提供了四个将匹配子串替换成指定字符串的方法:

            1)String replaceAll(Stringreplacement):将目标字符串里与既有模式相匹配的子串全部替换为指定的字符串。

            2)String replaceFirst(Stringreplacement):将目标字符串里第一个与既有模式相匹配的子串替换为指定的字符串。

            3)、还有两个方法Matcher appendReplacement(StringBuffersb, String replacement) StringBufferappendTail(StringBuffer sb)也很重要,appendReplacement允许直接将匹配的字符串保存在另一个StringBuffer中并且是渐进式匹配,并不是只匹配一次或匹配全部,而appendTail则是将未匹配到的余下的字符串添加到StringBuffer中。

            5、其他一些方法:例如Matcherreset():重设该Matcher对象。

            Matcher reset(CharSequence input):重设该Matcher对象并且指定一个新的目标字符串。

            Matcher region(int start, int end):设置此匹配器的区域限制。

     

    Matcher类使用示例:

    package com.zxt.regex;
     
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
     
    public classMatcherTest {
     
        public static void main(String[] args) {
            Patternp = Pattern.compile("\\d+");
     
            // matches()对整个字符串进行匹配
            // 返回false,因为bb不能被\d+匹配,导致整个字符串匹配未成功。
            Matcherm = p.matcher("22bb23");
            System.out.println(m.matches());
            m = p.matcher("2223");
            // 返回true,因为\d+匹配到了整个字符串
            System.out.println(m.matches());
     
            // lookingAt()对字符串前缀进行匹配
            m = p.matcher("22bb23");
            // 返回true,因为\d+匹配到了前面的22
            System.out.println(m.lookingAt());
            m = p.matcher("aa2223");
            // 返回false,因为\d+不能匹配前面的aa
            System.out.println(m.lookingAt());
     
            // find()对字符串进行匹配,匹配到的字符串可以在任何位置。
            m = p.matcher("22bb23");
            System.out.println(m.find()); // true
            m = p.matcher("aa2223");
            System.out.println(m.find()); // true
            m = p.matcher("aabb");
            System.out.println(m.find()); // false
            // 当匹配器匹配失败时,使用返回匹配器状态的方法将出错,例如:m.start();
     
            m = p.matcher("aa2223bb");
            System.out.println(m.find()); // true
     
            System.out.println(m.start()); // 2
            System.out.println(m.end()); // 6
            System.out.println(m.group()); // 2223
     
            p = Pattern.compile("([a-z]+)(\\d+)");
            m = p.matcher("aaa2223bb");
            // 匹配aaa2223
            m.find();
            // 返回2,因为有2组
            System.out.println(m.groupCount());
            // 返回0, 返回第一组匹配到的子字符串在字符串中的索引号
            System.out.println(m.start(1));
            // 返回3
            System.out.println(m.start(2));
            // 返回3 返回第一组匹配到的子字符串的最后一个字符在字符串中的索引位置.
            System.out.println(m.end(1));
            // 返回2223,返回第二组匹配到的子字符串
            System.out.println(m.group(2));
        }
    }


    应用实例

    1、一个简单的邮箱验证小程序

    package com.zxt.regex;
     
    import java.util.Scanner;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
     
    /*
     * 一个简单的邮件地址匹配程序
     */
    public classEmailMatch {
     
        public static void main(String[] args) throws Exception {
     
            Scannersc = new Scanner(System.in);
            while (sc.hasNext()) {
                Stringinput = sc.nextLine();
               
                // 检测输入的EMAIL地址是否以非法符号"."或"@"作为起始字符
                Patternp = Pattern.compile("^@");
                Matcherm = p.matcher(input);
                if (m.lookingAt()) {
                    System.out.println("EMAIL地址不能以'@'作为起始字符");
                }
     
                // 检测是否以"www."为起始
                p = Pattern.compile("^www.");
                m = p.matcher(input);
                if (m.lookingAt()) {
                    System.out.println("EMAIL地址不能以'www.'起始");
                }
     
                // 检测是否包含非法字符
                p = Pattern.compile("[^A-Za-z0-9.@_-~#]+");
                m = p.matcher(input);
                StringBuffersb = new StringBuffer();
                boolean result = m.find();
                boolean deletedIllegalChars= false;
     
                while (result) {
                    // 如果找到了非法字符那么就设下标记
                    deletedIllegalChars= true;
                    // 如果里面包含非法字符如冒号双引号等,那么就把他们消去,加到SB里面
                    m.appendReplacement(sb, "");
                    result = m.find();
                }
     
                // 此方法从添加位置开始从输入序列读取字符,并将其添加到给定字符串缓冲区。
                // 可以在一次或多次调用 appendReplacement 方法后调用它来复制剩余的输入序列。
                m.appendTail(sb);
                if (deletedIllegalChars){
                    System.out.println("输入的EMAIL地址里包含有冒号、逗号等非法字符,请修改");
                    System.out.println("您现在的输入为: " + input);
                    System.out.println("修改后合法的地址应类似: " + sb.toString());
                }
            }
     
            sc.close();
        }
    }


    2、判断身份证:要么是15位,要么是18位,最后一位可以为字母,并写程序提出其中的年月日。

            可以使用正则表达式来定义复杂的字符串格式:(\d{17}[0-9a-zA-Z]|\d{14}[0-9a-zA-Z])可以用来判断是否为合法的15位或18位身份证号码。因为15位和18位的身份证号码都是从7位到第12位为身份证为日期类型。这样我们可以设计出更精确的正则模式,提取身份证号中的日期信息。

    package com.zxt.regex;
     
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
     
    public classIdentityMatch {
     
        public static void main(String[] args) {
            // 测试是否为合法的身份证号码
            String[]id_cards = { "130681198712092019","13068119871209201x","13068119871209201","123456789012345",
                    "12345678901234x","1234567890123"};
     
            // 测试是否为合法身份证的正则表达式
            Patternpattern = Pattern.compile("(\\d{17}[0-9a-zA-Z]|\\d{14}[0-9a-zA-Z])");
     
            // 用于提取出生日字符串的正则表达式
            Patternpattern1 = Pattern.compile("\\d{6}(\\d{8}).*");
            // 用于将生日字符串分解为年月日的正则表达式
            Patternpattern2 = Pattern.compile("(\\d{4})(\\d{2})(\\d{2})");
     
            Matchermatcher = pattern.matcher("");
            for (int i = 0; i < id_cards.length; i++) {
                matcher.reset(id_cards[i]);
                System.out.println(id_cards[i] + " is id cards:" + matcher.matches());
     
                // 如果它是一个合法的身份证号,提取出出生的年月日
                if (matcher.matches()) {
                    Matchermatcher1 = pattern1.matcher(id_cards[i]);
                    matcher1.lookingAt();
                    Stringbirthday = matcher1.group(1);
     
                    Matchermatcher2 = pattern2.matcher(birthday);
                    if (matcher2.find()) {
                        System.out.println("它对应的出生年月日为:" + matcher2.group(1) + "年" + matcher2.group(2) + "月"
                                +matcher2.group(3) + "日");
                    }
     
                }
     
                System.out.println();
            }
        }
    }
    


    展开全文
  • Java正则表达式

    千次阅读 2019-07-06 19:03:50
    java.util.regex程序包只包含用于实现Java正则表达式处理技术的两个类,分别名为Pattern和Matcher。自然而然你会想到正则表达式由模式匹配(pattern matching)而成。java.lang还定义了一个新接口,它支持这些新的类...

    正则表达式基础

    参考书籍:精通正则表达式。

    Java正则表达式API

    java.util.regex程序包只包含用于实现Java正则表达式处理技术的两个类,分别名为PatternMatcher。自然而然你会想到正则表达式由模式匹配(pattern matching)而成。java.lang还定义了一个新接口,它支持这些新的类。在研究Patternt和Matcher之前,我们先快速浏览一下CharSequence这一新概念。另外,为方便起见String类为运行正则表达式匹配提供了一些新程序作为捷径

    CharSequence接口

    正则表达式是根据字符序列进行模式匹配的。虽然String对象封装了字符序列,但是它们并不是能够这样做的唯一对象。

    JDK 1.4定义了一个名为CharSequence的新接口,可描述特定不变的字符序列。该新接口是一个抽象(abstraction),它把字符序列从包含这些字符的具体实现(specific implementation)中分离出来。JDK 1.4对“年高德勋”的String和StringBuffer类进行了改进,用于实现CharSequence接口。新的CharBuffer类也实现了CharSequence。CharSequence接口也在字符集映射中投入了使用

    CharSequence定义的API十分简单。毕竟它没有花太多“笔墨”描述字符序列。

    package java.lang;
    public interface CharSequence
    {
        int length();
        char charAt (int index);
        public String toString();
        CharSequence subSequence (int start, int end);
    }

    CharSequence描述的每个字符序列通过length( )方法会返回某个长度值。通过调用charAt( )可以得到序列的各个字符,其中索引是期望的字符位置(desired character position)。字符位置从零到字符序列的长度之间,与我们熟悉的String.charAt( )基本一样。

    toString( )方法返回的String对象包括所描述的字符序列。这可能很有用,如打印字符序列。正如之前提过的,String现在实现了CharSequence。String和CharSequence同为不变的,因此如果CharSequence描述一个完整的String,那么CharSequence的toString( )方法返回的是潜在的String对象而不是副本。如果备份对象是StringBuffer或CharBuffer,系统将创建一个新的String保存字符序列的副本。

    最后通过调用subSequence( )方法会创建一个新的CharSequence描述子范围(subrange)。start和end的指定方式与String.substring( )的方式相同:start必须是序列的有效索引(valid index);end必须比start大,标志的是最末字符的索引加一。换句话说,start是起始索引(计算在内),end是结束索引(不计算在内)。

    CharSequence接口因为没有赋值方法(mutator method)看上去似乎是不变的,但是基本的实现对象可能不是不变的。CharSequence方法反映了基本对象的现状。如果状态改变,CharSequence方法返回的信息同样会发生变化。如何你依赖CharSequence保持稳定且不确认基础的实现,你可以调用toString( )方法,对字符序列拍个真实不变的快照。

    Pattern类

    Pattern类封装了正则表达式,它是你希望在目标字符序列中检索的模式。匹配正则表达式的代价可能非常高昂,因为可能排列数量巨大,尤其是模式反复应用的情况。大部分正则表达式处理器(包括Perl在内,在封装中)首先会编译表达式,然后利用编译好的表达式在输入中进行模式检测。

    在这一点上Java正则表达式程序包别无两样。Pattern类的实例是将一个编译好的正则表达式封装起来。让我们看看完整的Pattern API,看看它是如何使用的。记住,这并不是一个句法完整的类文件,它只中去掉了类主体的方法签名。

    package java.util.regex;
    public final class Pattern implements java.io.Serializable
    {
        public static final int UNIX_LINES
        public static final int CASE_INSENSITIVE
        public static final int COMMENTS
        public static final int MULTILINE
        public static final int DOTALL
        public static final int UNICODE_CASE
        public static final int CANON_EQ
        public static boolean matches (String regex, CharSequence input)
        public static Pattern compile (String regex)
        public static Pattern compile (String regex, int flags)
        public String pattern()
        public int flags()
        public String[] split (CharSequence input, int limit)
        public String[] split (CharSequence input)
        public Matcher matcher (CharSequence input)
    }

    上面所列的第一个方法matches( )是个公用程序。它可以进行完整的匹配操作,并根据正则表达式是否匹配整个的(entire)输入序列返回一个布尔值。这种方法很容易上手,因为你无须追踪任何对象;你要做的仅是调用一个简单的静态方法并测试结果。

    public boolean goodAnswer (String answer)
    {
    return (Pattern.matches ("[Yy]es|[Yy]|[Tt]rue", answer));
    }

    这种方法适用于默认设置尚可接受并且只需进行一次测试的情况。假如你要重复检查同一模式,假如你要找的模式是输入的子序列,又假如你要设置非默认选项,那么你应当创建一个新的Pattern对象并使用新对象的API方法。 

    需要注意的是Pattern类并没有public 构造函数。只有通过调用静态工厂方法才可以创建新的实例。compile( )的两个形式采用的都是正则表达式的String参数。返回的Pattern对象包含被转换成已编译内部形式的正则表达式。如果你提供的正则表达式形态异常,那么compile( )工厂方法会抛出java.util.regex.PatternSyntaxException(模式句法异常)。这是未经检查的异常,因此如果你对自己使用的正则表达式是否可行存在疑虑(例如它传递给你是一个变量),那么你可以把对compile( )的调用放到try/catch块中进行检测。

    compile( )的第二种形式接受标志有一个位掩码,这影响了正则表达式的默认编译。这些标志启用了可选的编译模式行为,例如如何处理边界或不区分大小写等。(除CANOB_EQ外)这些标志(flag)同样可由嵌入表达式内的子表达式启用。标志可以与布尔或(OR)表达式结合使用,如下所示:

    Pattern pattern = Pattern.compile ("[A-Z][a-zA-Z]*",
    Pattern.CASE_INSENSITIVE | Pattern.UNIX_LINES);

    Pattern类的实例是不变的,各个实例与对应的正则表达式绑定,无法修改。Pattern对象也是线程安全的,可被多个线程同时使用。

    Matcher类

    Matcher类为匹配字符序列的正则表达式模式提供了丰富的API。Matcher实例常常通过对Pattern对象调用matcher( )方法来创建的,它常常采用由该Pattern封装的正则表达式:

    Matcher类的实例是监控状态型对象,它们封装了与特定输入字符序列匹配的具体正则表达式。Matcher对象并不是线程安全的,因为它们在方法调用之间有保有内状态(hold internal state)。一个Matcher实例来自一个Pattern实例,Matcher对象的pattern( )返回的是向后引用(back reference),指向创建了Matcher的Pattern对象。Matcher对象可以重复使用,但是因其监控状态属性,为了开始新匹配操作它们必须处于已知状态。这可通过调用reset( )方法来实现,该方法在与匹配程序有关的CharSequence之前为模式匹配备好了对象。无参数的reset( )将使用上次为Matcher设置的CharSequence。如果你希望对新的字符序列进行匹配,那么你可以将一个新的CharSequence传递给reset( ),随后匹配将针对目标进行。例如,随着你读取各行的文件,你可以把它传递给reset( )。

    matches( ),如果整个(entire)字符序列匹配正则表达式的模式,则它返回true。反之如果模式匹配的只是子序列,方法将返回false。在文件中,这种方法用于选取恰好满足一定模式的行是非常有用的。这种行为(behavior)与作用于Pattern类的公用程序matches( )相同。

    lookingAt( )方法与matches( )相似,但是它不要求整个序列的模式匹配。如果正则表达式模式匹配字符序列的beginning(开头),则lookingAt( )返回true。lookingAt( )方法往往从序列的头部开始扫描。该方法的名字暗示了匹配程序正在“查看”目标是否以模式开头。如果返回为true,那么可以调用start( )、end( )和group( )方法匹配的子序列的范围(随后将给出更多关于这些程序的内容)。

    find( )方法运行的是与lookingAt( )相同类型的匹配操作,但是它会记住前一个匹配的位置并在之后重新开始扫描。从而允许了相继调用find( )对输入进行逐句比对,寻找嵌入的匹配。复位后第一次调用该方法,则扫描将从输入序列的首个字符开始。在随后调用中,它将从前一个匹配的子序列后面的第一个字符重新开始扫描。如各个调用来说,如果找到了模式将返回true;反之将返回false。通常你会使用find( )循环访问一些文本来查找其中所有匹配的模式。

    带位置参数的find( )会在给定的索引位置进行隐式复位并从该位置开始扫描。然后如果需要可以调用无参数的find( )扫描输入序列剩余的部分。

    一旦检查到匹配,你可以通过调用start( )和end( )确定匹配位于字符序列的什么位置。Start( )方法返回的是匹配序列首个字符的索引;end( )方法返回的值等于匹配序列最末字符的索引加一。这些返回值与CharSequence.subsequence( )的返回值一致,可直接用于提取匹配的子序列。

     

    CharSequence subseq; 
    if (matcher.find( )) 
    { 
    subseq = input.subSequence (matcher.start(), matcher.end( )); 
    }

    一些正则表达式可以匹配空字符串,这种情况下start( )和end( )将返回相同的值。只有当匹配之前已经过matches( )、lookingAt( )或检测find( )的检测,start( )和end( )返回的值才有意义。如果没有检测到匹配或最后的匹配尝试返回的是false,那么调用start( )或end( )将导致java.lang.IllegalStateException(Java语言非法状态异常)。为了了解带有group参数的start( )和end( ),我们首先需要知道表达式捕获组(expression capture group)。

    正则表达式可能包含称为捕获组(capture group)的子表达式,它们被小括号括了起来。在正则表达式的求值期间将保存匹配这些捕获组表达式的输入子序列。一旦完全匹配操作完成,这些保存的代码片断可通过确定相应的组号从Matcher对象上重新获取。捕获组可以嵌套使用,数量可以通过从左到右计算左括弧(开括号)得到。无论整个表达式是否有子组,它的捕获组总能记为组零(group zero)。例如,正则表达式A((B)(C(D)))可能有的捕获组编号如表5-3所示。

     这种分组句法存在异常事件。以(?开头的组是个纯的(pure)或说是无法捕获的组。它的值无法保存且它对无法计算捕获组编号。

    捕获组在正则表达式模式中的编号由groupCount( )方法返回。该值来自原始的Pattern对象,是不可变的。组号必须为正且小于groupCount( )返回的值。传递超出范围的组号将导致java.lang.IndexOutOfBoundsException(java语言索引出界异常)。

    可以将捕获组号传递给start( )和end( )来确定子序列是否匹配已知的捕获组子表达式。有可能出现这样一种情况,即整个表达式成功匹配但是有一个或多个的捕获组无法匹配。如果请求的捕获组当前没有设置则start( )和end( )方法的返回值将为-1。

    (正如之前看到的)你可以利用start( )和end( )返回的值从输入的CharSequence中提取出匹配的子序列,但是group( )方法为此提供了更简单的方式。调用带数字参数的group( )将返回一个字段,该字段是匹配特殊捕获组的子序列。如果你调用的group( )不含参数,则返回将是与整个正则表达式(组零)匹配的子序列。


    String match0 = input.subSequence (matcher.start(), matcher.end()).toString( );
    String match2 = input.subSequence (matcher.start (2), matcher.end (2)).toString( );
    上述代码与下列代码等效:
    String match0 = matcher.group( );
     String match2 = matcher.group(2);

    最后让我们看看Matcher对象解决修改字符序列的方法。正则表达式最常见的应用之一是查找并替换(search-and-replace)。这种应用使用replaceFirst( )和replaceAll( )可以轻轻松松就搞定。它们的行为方式是相同的,区别在于replaceFirst( )在找到第一个匹配后就会停止,而replaceAll( )将循环执行直到替换完所有的匹配。二者都带有String参数,String参数是用于替换输入字符序列中匹配模式的替换值(replacement value) 

    上文提过,捕获组在正则表达式内可以向后引用(back-reference)。它们也可以被你提供组replaceFirst( )或replaceAll( )的替换字符串引用。捕获组号通过添加美元符号$可嵌入替换字符串中。当替换字符串被替换成结果字符串时,每次出现的$g将被group( )返回的值代替。如果你想在替换字符串使用字面量(literal)美元符号,那么你必须在它前面加个反斜杠符号(\$)。如果想要传递反斜杠符号,你必须多加一个反斜杠(\\)。如果你想在捕获组引用后面跟上字面量的数值型数字,那么你可以用反斜杠将它们与组号分开,像这样:123$2\456。

    Matcher对象记住的状态信息位(the bits of state information)之一是追加位置(append position)。追加位置是用于记住输入字符序列的量,这些字符序列已经通过之前调用appendReplacement( )复制了出来。当调用appendReplacement( )时,将发生如下过程:
    1. 从输入中读取字符是从当前追加位置开始,读取的字符将被添加到已知的StringBuffer中。最后复制的字符就在匹配模式的首个字符之前。这个字符位于start( )返回的索引减一的位置。
    2. 如先前描述的,替换字符串被添加给StringBuffer并替换任何嵌入的捕获组引用。
    3. 追加位置更新成跟在匹配模式后面的字符的索引,这个索引是end( )返回的值。
    仅当前一个匹配操作成功(通常调用find( ))appendReplacement( )方法才能正常工作。如果前一个匹配返回的是false或在复位后立即调用该方法,你将得到一个“令人愉快的奖励”:java.lang.IllegalStateException(java语言非法状态异常)。

    String类的正则表达式方法

     
    package java.lang;
    public final class String
    implements java.io.Serializable, Comparable, CharSequence
    {
        // This is a partial API listing
        public boolean matches (String regex)
        public String [] split (String regex)
        public String [] split (String regex, int limit)
        public String replaceFirst (String regex, String replacement)
        public String replaceAll (String regex, String replacement)
    }

    正则表达式语句

    参考:Java Pattern类的用法详解(正则表达式)

     

    展开全文
  • 先贴出异常 服务器的CPU使用率一直保持在10%左右,...是java进程跑死的,只能找java进程下哪些线程高cpu使用率 问题线程已经找到 USER %CPU PRI SCNT WCHAN USER SYSTEM TID TIME [root@27a2a017-029b-48d8-...

    先贴出异常

    服务器的CPU使用率一直保持在10%左右,最近突然激增,没有下降的趋势,而且重启后依然攀升,至死方休O/\O,查看进程

    是java进程跑死的,只能找java进程下哪些线程高cpu使用率

    问题线程已经找到

    USER     %CPU PRI SCNT WCHAN  USER SYSTEM    TID     TIME
    [root@27a2a017-029b-48d8-bee2-255610ec9649 ~]# ps -mp 190180 -o THREAD,tid,time|uniq -c|sort -nr
          1 USER     %CPU PRI SCNT WCHAN  USER SYSTEM    TID     TIME
          1 admin    83.8  19    - -         -      - 231456 1-00:42:04
          1 admin    82.4  19    - -         -      - 390319 16:22:37
          1 admin    62.3   -    - -         -      -      - 11-02:21:35
          1 admin    44.3  19    - -         -      - 390320 08:48:52
          1 admin     3.4  19    - futex_    -      -  60245 00:00:34
          1 admin     2.8  19    - futex_    -      -  60243 00:00:28
          1 admin     2.7  19    - futex_    -      -  50481 00:01:25
          1 admin     2.6  19    - futex_    -      -  57651 00:00:42
          1 admin     2.5  19    - futex_    -      -  61324 00:00:19
          1 admin    25.0  19    - -         -      -  63595 3-17:04:59
          1 admin     2.3  19    - futex_    -      -  61321 00:00:18
          1 admin     2.3  19    - futex_    -      -  60244 00:00:24
          1 admin     2.3  19    - futex_    -      -  45505 00:01:38
          1 admin     2.2  19    - futex_    -      -  57650 00:00:35
          1 admin     1.8  19    - futex_    -      -  61327 00:00:14
          1 admin     1.8  19    - futex_    -      -  61320 00:00:14
          1 admin     1.5  19    - -         -      -  60189 00:00:15

    找线程的堆栈信息(TID记得转16进制:390319=>5f4af)

    [admin@27a2a017-029b-48d8-bee2-255610ec9649 ~]jstack 190180 |grep 5f4af -A 500
    "JSF-BZ-22000-99-T-1114" #45190 daemon prio=5 os_prio=0 tid=0x00007fd36400f800 nid=0x5f4af runnable [0x00007fd35ccd2000]
       java.lang.Thread.State: RUNNABLE
            at java.util.regex.Pattern$5.isSatisfiedBy(Pattern.java:5251)
            at java.util.regex.Pattern$5.isSatisfiedBy(Pattern.java:5251)
            at java.util.regex.Pattern$5.isSatisfiedBy(Pattern.java:5251)
            at java.util.regex.Pattern$5.isSatisfiedBy(Pattern.java:5251)
            at java.util.regex.Pattern$CharProperty.match(Pattern.java:3776)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4260)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4279)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4279)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4279)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4279)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4279)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4279)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4279)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4279)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4279)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4279)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4279)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4279)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4279)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4279)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4279)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4279)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4279)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4279)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4279)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4279)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.matchInit(Pattern.java:4804)
            at java.util.regex.Pattern$Prolog.match(Pattern.java:4741)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4279)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupCurly.match0(Pattern.java:4485)
            at java.util.regex.Pattern$GroupCurly.match(Pattern.java:4405)
            at java.util.regex.Pattern$Loop.match(Pattern.java:4794)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$CharProperty.match(Pattern.java:3777)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4279)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$CharProperty.match(Pattern.java:3777)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$CharProperty.match(Pattern.java:3777)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4279)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$CharProperty.match(Pattern.java:3777)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$CharProperty.match(Pattern.java:3777)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4279)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$CharProperty.match(Pattern.java:3777)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$CharProperty.match(Pattern.java:3777)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$CharProperty.match(Pattern.java:3777)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$CharProperty.match(Pattern.java:3777)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$CharProperty.match(Pattern.java:3777)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$CharProperty.match(Pattern.java:3777)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$CharProperty.match(Pattern.java:3777)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$CharProperty.match(Pattern.java:3777)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$CharProperty.match(Pattern.java:3777)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Loop.matchInit(Pattern.java:4801)
            at java.util.regex.Pattern$Prolog.match(Pattern.java:4741)
            at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
            at java.util.regex.Pattern$BranchConn.match(Pattern.java:4568)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4272)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3798)
            at java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3798)
            at java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3798)
            at java.util.regex.Pattern$Curly.match0(Pattern.java:4247)
            at java.util.regex.Pattern$Curly.match(Pattern.java:4234)
            at java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3798)
            at java.util.regex.Pattern$Branch.match(Pattern.java:4604)
            at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658)
            at java.util.regex.Pattern$Begin.match(Pattern.java:3525)
            at java.util.regex.Matcher.match(Matcher.java:1270)
            at java.util.regex.Matcher.matches(Matcher.java:604)
            at com.jd.union.open.gateway.common.util.RegexUtils.verifyUrl(RegexUtils.java:14)
            at com.jd.union.open.gateway.service.api.goods.GoodsServiceImpl.queryLinkGoods(GoodsServiceImpl.java:664)
            at com.jd.union.open.gateway.service.api.goods.GoodsServiceImpl$$FastClassBySpringCGLIB$$1f88f92c.invoke(<generated>)
            at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
            at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)
            at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
            at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85)
            at com.jd.union.open.gateway.common.aop.JOpenAspect.execJOpenAspect(JOpenAspect.java:188)
            at sun.reflect.GeneratedMethodAccessor100.invoke(Unknown Source)
            at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
            at java.lang.reflect.Method.invoke(Method.java:497)
            at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:629)
            at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:618)
            at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
            at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168)
            at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85)
            at com.jd.ump.annotation.JAnnotation.execJAnnotation(JAnnotation.java:105)
            at sun.reflect.GeneratedMethodAccessor99.invoke(Unknown Source)
            at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
            at java.lang.reflect.Method.invoke(Method.java:497)
            at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:629)
            at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:618)
            at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
            at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168)
            at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
            at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
            at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)
            at com.jd.union.open.gateway.service.api.goods.GoodsServiceImpl$$EnhancerBySpringCGLIB$$9bc334b3.queryLinkGoods(<generated>)
            at sun.reflect.GeneratedMethodAccessor346.invoke(Unknown Source)
            at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
            at java.lang.reflect.Method.invoke(Method.java:497)
            at com.jd.jsf.gd.filter.ProviderInvokeFilter.reflectInvoke(ProviderInvokeFilter.java:140)
            at com.jd.jsf.gd.filter.ProviderInvokeFilter.invoke(ProviderInvokeFilter.java:100)
            at com.jd.union.open.gateway.common.filter.GatewayFilter.invoke(GatewayFilter.java:20)
            at com.jd.jsf.gd.filter.ProviderConcurrentsFilter.invoke(ProviderConcurrentsFilter.java:62)
            at com.jd.jsf.gd.filter.ProviderTimeoutFilter.invoke(ProviderTimeoutFilter.java:39)
            at com.jd.jsf.gd.filter.ProviderMethodCheckFilter.invoke(ProviderMethodCheckFilter.java:78)
            at com.jd.jsf.gd.filter.ProviderInvokeLimitFilter.invoke(ProviderInvokeLimitFilter.java:54)
            at com.jd.jsf.gd.filter.ProviderHttpGWFilter.invoke(ProviderHttpGWFilter.java:47)
            at com.jd.jsf.gd.filter.ProviderGenericFilter.invoke(ProviderGenericFilter.java:99)
            at com.jd.jsf.gd.filter.ProviderContextFilter.invoke(ProviderContextFilter.java:73)
            at com.jd.jsf.gd.filter.ExceptionFilter.invoke(ExceptionFilter.java:49)
            at com.jd.jsf.gd.filter.SystemTimeCheckFilter.invoke(SystemTimeCheckFilter.java:79)
            at com.jd.jsf.gd.filter.FilterChain.invoke(FilterChain.java:275)
            at com.jd.jsf.gd.server.ProviderProxyInvoker.invoke(ProviderProxyInvoker.java:67)
            at com.jd.jsf.gd.server.JSFTask.doRun(JSFTask.java:123)
            at com.jd.jsf.gd.server.BaseTask.run(BaseTask.java:27)
            at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
            at java.util.concurrent.FutureTask.run(FutureTask.java:266)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
            at java.lang.Thread.run(Thread.java:745)

    问题已经很明确,是代码的问题(通过以上方式可以解决各种问题),正则表达式校验的重复执行导致高CPU。

    正则如下:

    private static final Pattern PATTERN_URL = Pattern.compile( "^([hH][tT]{2}[pP]:/*|[hH][tT]{2}[pP][sS]:/*|[fF][tT][pP]:/*)(([A-Za-z0-9-~]+).)+([A-Za-z0-9-~\\/])+(\\?{0,1}(([A-Za-z0-9-~]+\\={0,1})([A-Za-z0-9-~]*)\\&{0,1})*)$");

    导致问题的数据

    https://coupon.m.watereasy.com/coupons/show.action?key=aec58667c0c14d7bae24vbb73b83efcv&amp;roleId=278889568&amp;mall.watereasy.com/index-5935345.html\n\n100-50香蕉

     

    正则修改完毕(网上的url正则或多或少都有问题,还是根据自身业务按需设置,又是中文造成的问题)

    private static final Pattern PATTERN_URL= Pattern.compile( "^(([hH][tT]{2}[pP]|[hH][tT]{2}[pP][sS])://)(([A-Za-z0-9-~]+).)+([A-Za-z0-9-~\\/])+([\\w./?%&=\\u4e00-\\u9fa5]+)$");

     

    这个表达式99.99%执行时是没有问题的,但总有一个线程以某种方式“逃离”整个服务器,必须手动杀死它(否则最终导致堆栈溢出)

    问题的根源在于:此正则表达式具有经典:/ ^(A *)* $/形式,请注意,仅当模式与目标字符串不匹配时才会发生此失控行为。

    失控模式:^(A*|B*|C*|D*)*$有几个方式可以解决它:

    •  ^(A|B|C|D)*$  –  从组中的四个备选项中的每一个中删除星号(“零或更多”量词)。
    •  ^(A*+|B*+|C*+|D*+)*$ – 使每个替代星号量词占有(即将每个*改为*)。
    •  ^(?>A*|B*|C*|D*)*$ – 使包含替代原子的组成为原子。

    第二个应该比第一个执行得快一点,但这三个都将解决“正则表达式疯狂”的问题。 (最好不用正则表达式解析HTML)

    对于我来说,只能以简化的表达式修改此问题。

    此问题的更多解决方式:

    https://stackoverflow.com/questions/19990609/regex-gone-wild-java-util-regex-pattern-matcher-goes-into-high-cpu-loop

    《さくら》 、《さくら ~あなたに出会えてよかった~》 、《桜ひらり》

    展开全文
  • StackOverflowError in java.util.regex.Pattern 引擎与回溯 这里引用下一位老哥的原文,简单介绍下正则表达式的引擎和回溯机制。 正则引擎主要可以分为基本不同的两大类:一种是DFA(确定型有穷自动机),...

    问题与分析

    某天领导report了一个问题:线上的CPU自从上一个版本迭代后就一直处于居高不下的状况,领导看着这段时间的曲线图判断是有两条线程在不停的死循环。

    接到任务后去查看了AWS的CloudWatch,发现线上CPU确实一直居高不下,使用率基本是之前的两倍;另外发现线程使用率以比之前频繁很多。后来公司的大佬拿到dump后经过分析发现,是由正则表达式造成的CPU持续高使用率的问题。

    堆栈信息如下:

    at java.util.regex.Pattern$Loop.match(Pattern.java:4787)
    	at java.util.regex.Pattern$GroupTail.match(Pattern.java:4719)
    	at java.util.regex.Pattern$Curly.match0(Pattern.java:4281)
    	at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
    	at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
    	at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
    	at java.util.regex.Pattern$GroupCurly.match0(Pattern.java:4487)
    	at java.util.regex.Pattern$GroupCurly.match(Pattern.java:4407)
    	at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
    	at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
    	at java.util.regex.Pattern$GroupHead.match(Pattern.java:4660)
    	at java.util.regex.Pattern$Loop.match(Pattern.java:4787)
    	at java.util.regex.Pattern$GroupTail.match(Pattern.java:4719)
    	at java.util.regex.Pattern$Curly.match0(Pattern.java:4281)
    	at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
    	at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
    	at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
    	at java.util.regex.Pattern$GroupCurly.match0(Pattern.java:4487)
    	at java.util.regex.Pattern$GroupCurly.match(Pattern.java:4407)
    	at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
    	at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
    	at java.util.regex.Pattern$GroupHead.match(Pattern.java:4660)
    	at java.util.regex.Pattern$Loop.match(Pattern.java:4787)
    	at java.util.regex.Pattern$GroupTail.match(Pattern.java:4719)
    	at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
    	at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
    	at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
    	at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
    	at java.util.regex.Pattern$GroupCurly.match0(Pattern.java:4487)
    	at java.util.regex.Pattern$GroupCurly.match(Pattern.java:4407)
    	at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
    	at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
    	at java.util.regex.Pattern$GroupHead.match(Pattern.java:4660)
    	at java.util.regex.Pattern$Loop.matchInit(Pattern.java:4803)
    	at java.util.regex.Pattern$Prolog.match(Pattern.java:4743)
    	at java.util.regex.Pattern$GroupCurly.match0(Pattern.java:4487)
    	at java.util.regex.Pattern$GroupCurly.match(Pattern.java:4407)
    	at java.util.regex.Pattern$GroupTail.match(Pattern.java:4719)
    	at java.util.regex.Pattern$BranchConn.match(Pattern.java:4570)
    	at java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3800)
    	at java.util.regex.Pattern$Branch.match(Pattern.java:4606)
    	at java.util.regex.Pattern$GroupHead.match(Pattern.java:4660)
    	at java.util.regex.Pattern$Start.match(Pattern.java:3463)
    	at java.util.regex.Matcher.search(Matcher.java:1248)
    	at java.util.regex.Matcher.find(Matcher.java:637)
    	at com.core.cbx.mybatis.plugin.sql.TableAliasParseInjector.getTableAlias(TableAliasParseInjector.java:48)
    

    还是第一次知道正则表达式也会引发这种问题,网上查了下资料,原来有不少人也遇到同样的问题。而这个问题,是由正则表达式的灾难性回溯(Catastrophic Backtracking),或者说回溯陷阱造成的。

    另外,可以发现在jdk的JIRA里也有人提出了这个issue,不过目前依然还没有解决这个bug,下面是官方的issue链接:StackOverflowError in java.util.regex.Pattern

    引擎与回溯

    这里引用下一位老哥的原文,简单介绍下正则表达式的引擎和回溯机制。

    正则引擎主要可以分为基本不同的两大类:一种是DFA(确定型有穷自动机),另一种是NFA(不确定型有穷自动机)。简单来讲,NFA 对应的是正则表达式主导的匹配,而 DFA 对应的是文本主导的匹配。

    DFA从匹配文本入手,从左到右,每个字符不会匹配两次,它的时间复杂度是多项式的,所以通常情况下,它的速度更快,但支持的特性很少,不支持捕获组、各种引用等等;而NFA则是从正则表达式入手,不断读入字符,尝试是否匹配当前正则,不匹配则吐出字符重新尝试,通常它的速度比较慢,最优时间复杂度为多项式的,最差情况为指数级的。但NFA支持更多的特性,因而绝大多数编程场景下(包括java,js),我们面对的是NFA。

    Java的正则表达式引擎用的是NFA算法,在根据正则表达式来匹配文本时,拥有回溯机制。在遇到以下字符时就有可能发生回溯:

    1. ?
    2. +
    3. *
    4. {min, max}

    以上四种默认是贪婪模式去匹配文本,也就是说,会尽可能多地去匹配更多的字符。在这个匹配的过程中,必然会一次次地匹配文本,一直到匹配不上时,才会回溯一次,重新用正则表达式的下一个字符去匹配回溯之前匹配不上的文本。

    这里说的比较抽象,有兴趣的可以自行搜索下正则表达式的回溯以及贪婪模式、懒惰模式(也叫勉强模式)和独占模式(也叫侵占模式),下面附上一篇图文并茂的文章:正则表达式三种模式:贪婪模式、懒惰模式、独占模式

    总之,简单地说,由于正则表达式的回溯,如果我们的正则表达式写得不够好,并且被匹配的字符串文本又非常长,就有可能大量触发回溯,导致CPU飙升,甚至是堆栈溢出。这也就是所谓的灾难性回溯,或者说回溯陷阱。

    这里还是拿上面文章里的例子来举例:为了校验马来西亚的商店名字,写了如下一条正则表达式:

    ^([A-Za-z0-9._()&'\\- ]|[aAàÀảẢãÃáÁạẠăĂằẰẳẲẵẴắẮặẶâÂầẦẩẨẫẪấẤậẬbBcCdDđĐeEèÈẻẺẽẼéÉẹẸêÊềỀểỂễỄếẾệỆfFgGhHiIìÌỉỈĩĨíÍịỊjJkKlLmMnNoOòÒỏỎõÕóÓọỌôÔồỒổỔỗỖốỐộỘơƠờỜởỞỡỠớỚợỢpPqQrRsStTuUùÙủỦũŨúÚụỤưƯừỪửỬữỮứỨựỰvVwWxXyYỳỲỷỶỹỸýÝỵỴzZ])+$
    

    这就是一个很简单的^()+$结构,由于校验允许使用英文字母大小写、数字、越南文和一些特殊字符如“&”,“-”,“_”等,于是直接把这些字符都塞到[]里,然后为了方便观看把越南文特地抽出来塞到另一个[]里,最后把这两个[]|拼接起来。

    看上去非常简单的结构,但却会在线上时不时引发CPU过高的问题,可以用下面的测试类简单跑一下看看:

    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    public class Test {
    
        private static final String REGEX_TABLE_ALIAS = "^([A-Za-z0-9._()&'\\- ]|[aAàÀảẢãÃáÁạẠăĂằẰẳẲẵẴắẮặẶâÂầẦẩẨẫẪấẤậẬbBcCdDđĐeEèÈẻẺẽẼéÉẹẸêÊềỀểỂễỄếẾệỆfFgGhHiIìÌỉỈĩĨíÍịỊjJkKlLmMnNoOòÒỏỎõÕóÓọỌôÔồỒổỔỗỖốỐộỘơƠờỜởỞỡỠớỚợỢpPqQrRsStTuUùÙủỦũŨúÚụỤưƯừỪửỬữỮứỨựỰvVwWxXyYỳỲỷỶỹỸýÝỵỴzZ])+$";
    
        public static void main(final String[] args) {
            final String string = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!";
            final Pattern pattern = Pattern.compile(REGEX_TABLE_ALIAS);
            final Matcher matcher = pattern.matcher(string);
            final boolean result = matcher.find();
            System.out.println(result);
        }
    }
    

    你会发现,当在校验这个aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!字符串时,竟然无法立刻打印出校验结果,需要等待相当长的一段时间。如果把这个字符串改成这个,!aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,就可以秒出结果。

    这两个字符串是一样长,区别仅仅是!在首位和末位而已,但在校验时花费的时间却完全不同,原因是!是非法字符,但在末位时,会触发大量回溯,如果这个字符串文本有数百位,上千位,就很有可能会发生堆栈溢出。

    原文作者的解决方法是把原来的正则表达式改为独占模式,也就是在+后加上+,将^()+$结构变成^()++$结构。这种做法我认为其实不太好,独占模式也是会尽可能地匹配更多的字符,但是却不会发生回溯,如果正则表达式写得不好,就可能会校验漏。

    其实有个更好的改法,就是单纯把原来的表达式里的两个[]合并成一个[],如下:

    ^([A-Za-z0-9._()&'\\- aAàÀảẢãÃáÁạẠăĂằẰẳẲẵẴắẮặẶâÂầẦẩẨẫẪấẤậẬbBcCdDđĐeEèÈẻẺẽẼéÉẹẸêÊềỀểỂễỄếẾệỆfFgGhHiIìÌỉỈĩĨíÍịỊjJkKlLmMnNoOòÒỏỎõÕóÓọỌôÔồỒổỔỗỖốỐộỘơƠờỜởỞỡỠớỚợỢpPqQrRsStTuUùÙủỦũŨúÚụỤưƯừỪửỬữỮứỨựỰvVwWxXyYỳỲỷỶỹỸýÝỵỴzZ])+$
    

    这时候再去跑上面的测试类,你会发现在校验上面的两个字符串文本时,都可以秒出校验结果。原因是新的表达式减少了回溯的机会,相当于把Java里连续多个if语句给合并成一个了,这样就减少了分支,自然就降低了灾难性回溯的可能性。

    具体案例与解决方案

    上面是其他人遇到的案例,这里说下博主遇到的case以及最终的解决方案。在系统中我们用的是自己魔改过的mybatis,其中有个正则表达式是用来获取sql中的表别名的,如下:

    (FROM|JOIN|,)(\\s)+([A-Z0-9_]+(\\s)+[A-Z0-9_]+(,| )*)+(\\s)+(JOIN|WHERE|INNER|LEFT|OUTER|ON|ORDER)
    

    这个本来一直都没有问题,直到前段时间系统迭代后,有客户在页面上搜索了一段比较长的字符串。这个搜索的操作其实就是向db发出一条sql,用来模糊查询若干个字段是否含有用户搜索的这段字符串。然后在组装这条sql的时候,会使用到上述的正则表达式来获取表别名,具体组装逻辑这里就不说了。最后组装成的sql比较长,大概一万多个字符(已经简化过了)。之所以这么长,是因为我们会拿用户输入的字符串去挨个模糊查询数据表里的很多个字符类型的列,也就是说,会有大量的like '%xxxx%'的部分。

    当这条很长的sql被上述的正则表达式匹配时,就会发生灾难性回溯,导致系统长时间假死。这里就不贴出来具体的sql了,就简单分析下上述正则表达式存在什么问题。

    表达式分成了三块部分,第一部分是(FROM|JOIN|,)(\\s)+,第二部分是([A-Z0-9_]+(\\s)+[A-Z0-9_]+(,| )*)+,第三部分是(JOIN|WHERE|INNER|LEFT|OUTER|ON|ORDER)。这个很好理解,就是简单匹配下表别名,比如:from Table_A a, Table_B b where ...

    可以发现,表达式的第一部分和第二部分都有,,而第二部分的末尾使用了+限定必须至少匹配一次,导致当sql过长时并存在大量逗号空格时,会触发大量回溯。为了避免这种情况,应当尽量把第二部分末尾的+去掉,如果可能的话,可以转换成*

    最终的修改方案是分为了两部分:
    第一部分是简化sql,因为原本是直接拿组装后的sql去匹配,其实sql里大量的like '%xxxx%'部分毫无意义,因为目的只是拿到表别名而已。所以在匹配之前,把这些模糊匹配的部分直接去掉了。

    第二部分是修改正则表达式,测试时直接拿简化前的sql去匹配,如果不会发生灾难性回溯就算过关了。最终修改后的样子如下:

    (FROM|JOIN)(\\s)+([A-Z0-9_]+(\\s)+[A-Z0-9_]+((\\s)*(,|JOIN)(\\s)*[A-Z0-9_]+(\\s)+[A-Z0-9_]+)*)(\\s)+(JOIN|WHERE|INNER|LEFT|OUTER|ON|ORDER)
    

    这里推荐个在线检查正则表达式匹配字符串文本的网站,可以用来发现是否会触发灾难性回溯:Online regex tester and debugger: PHP, PCRE, Python, Golang and JavaScript
    关于这个网站的用法可以看看这篇文章的末尾部分:一个正则表达式引发的血案,让线上CPU100%异常!

    排查高CPU使用率的方法

    1. 使用top命令查找在大量占用CPU的进程的PID
    2. 使用ps -mp pid -o THREAD,tid,time定位到大量占用CPU的线程TID;也可以用这个命令直接排序下,更方便找到大量占用CPU的线程:ps -mp pid -o THREAD,tid,time|uniq -c|sort -nr
    3. 将上述找到的线程TID转换成十六进制:printf “%x\n” TID,比如原本的线程TID是28802,可以用上面的命令转成十六进制数7082
    4. 使用PID以及刚刚转成十六进制的TID来打印出该线程的堆栈信息:jstack PID|grep TID -A 100。也可以把完整的堆栈信息输入到一个log文件里,有两种方法:
      1. 方法一是用kill -3 PID > threadDump.log 2>&1,这种方法不适用于JDK1.6以上的版本
      2. 方法二是用jstack -l PID > threadDump.log 2>&1
    5. 接下来就是分析堆栈信息,定位到问题代码的位置了。

    下面简单介绍下上述命令的几个关键参数的含义:

    ps命令:
    -m 显示所有的执行者。
    -p 指定进程的PID,并列出该进程的状况。
    -o 用户自定义输出格式。
    
    unic命令:
    -c 检查文件是否已经按照顺序排序,排序过为真
    
    sort命令:
    -n 按照数值大小进行排序
    -r 以相反的顺序进行排序,即降序排序,从大排到小
    
    jstack命令:
    -l long listing. Prints additional information about locks,会打印出额外的锁信息,可以在发生死锁时用来观察锁持有情况
    -m to print both java and native frames (mixed mode),不仅会输出Java堆栈信息,还会输出C/C++堆栈信息(比如Native方法)
    
    kill命令:
    -signal 指定发送的信号类型,比如:
    -3是打印进程的线程信息,并不会终止进程;
    -9是强制杀死进程,一般用于立即杀死无响应或者卡死的进程;
    -15是柔和地终止进程,一般会在终止之前保存数据、关闭连接,需要经过一段时间后才会完全退出进程,效果等同于-TERM
    

    参考链接

    展开全文
  • 那时候,我还没有开始学习Java,不知道Java支持正则表达式,所以我的第一个方案是想办法将数据传输到后台,然后利用Shell脚本正则表达式得到匹配结果。如果后来真的那么做了,那就二了。后来我研究了以下别的类似的...
  • java应用中Pattern和Matcher的使用

    千次阅读 2017-08-22 14:33:01
    1.Pattern和Matcher简介 在很多种情况下,我们都必须对字符串进行匹配,以便判断字符串的格式是否符合要求,对字符串中的内容进行提取。比如,我要从一段话aabdfe中,判断这段话是否有包含ab这个词,那么如果用if-...
  • java web.xml配置文件中url-pattern路径匹配方式 url-pattern元素用于匹配对应的Servlet或Filter。使用事例如下: dispatcherServlet org.springframework.web.servlet.DispatcherServlet ...
  • 最近线上项目出现了java.util.regex.PatternSyntaxException,项目也没什么改动,除了特殊 字符表的字符集由于原来是utf8编码的字符集,不支持4个字节的字符,修改成了utf8mb4字节,其余 的也没什么改动.异常原因如下 ...
  • Caused by: java.lang.IllegalArgumentException: Invalid <url-pattern> in servlet mapping at org.apache.catalina.core.StandardContext.addServletMappingDecoded(StandardContext.java:3195) at org.apache....
  • MyBatis面试题(2020最新版)

    万次阅读 多人点赞 2019-09-24 16:40:33
    整理好的MyBatis面试题库,史上最全的MyBatis面试题,MyBatis面试宝典,特此分享给大家 MyBatis 介绍 MyBatis 是一款优秀的...MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plai...
  • JAVA Pattern

    千次阅读 2012-04-22 16:25:26
    有许多源代码开放的正则表达式库可供Java程序员使用,而且它们中的许多支持Perl 5兼容的正则表达式语法。我在这里选用的是Jakarta-ORO正则表达式库,它是最全面的正则表达式API之一,而且它与Perl 5正则表达式完全...
  • 一、Pattern的使用 这个使用很简单。 1、把正则表达式编译为Pattern对象: 比如: Pattern compile = Pattern.compile("http://([a-z]+\\.)+[a-z]+"); 就是用于匹配http的url的正则表达式(随手写的,可能有...
  • java.util.regex包下Pattern、Matcher类学习

    千次阅读 2018-04-07 15:02:12
    java.util.regex包下Pattern、Matcher类学习 此文所有的程序代码地址:RegularExperssion java.util.regex包主要包括以下三个类: Pattern 类 Matcher 类 PatternSyntaxException 类 PatternSyntaxException 是...
  • 在写程序的过程中,有时会需要匹配、查找、替换或者是判断字符串的出现情况,而且有时不能用简单的纯编码方式解决这些问题,这个时候就会想到要正则表达式,无论是Java, PHH, C#, Python, JavaScript, ActionScript...
  • 第二,它可以使用xml的方式来组织管理我们的sql,因为一般程序出错很多情况下是sql出错,别人接手代码后能快速找到出错地方,甚至可以优化原来写的sql。 SSM框架整合配置 好了,前面bb那么多,下面我们...
  • Java - 正则表达式 - Pattern - Matcher

    万次阅读 2013-08-21 14:35:01
    Pattern.compile("java", Pattern.CASE_INSENSITIVE); //上下代码是等价的 System.out.println("JAVA".matches("(?i)(java)")); System.out.println("java".matches("(?i)(java)")); } } /* 打印结果: ...
  • 结论:Pattern与Matcher一起合作.Matcher类提供了对正则表达式的分组支持,以及对正则表达式的多次匹配支持. 单独用Pattern只能使用...Java正则表达式通过java.util.regex包下的Pattern类与Matcher类实现(建议在阅
  • [Java] 字符串验证--java.util.regex.Pattern

    千次阅读 2013-04-07 11:02:29
    转自: ... 字符串验证--java.util.regex.Pattern  今天修改了jsf的验证器部分...看到了对于email的验证.....看起来还是有点头大呀.....于是乎翻javadoc....找到java.util
  • 此处用Matcher和Pattern类会非常简单,这两个类是利用正则表达式匹配查找的相关类,详细可以参见java Pattern和Matcher详解 public static void main(String[] args) { String str = "人生自古谁无死,留取丹心...
  • String regex="[0-9]{2}\\:[0-9]{2}\\:[0-9]{2}"; // 提取 xx:xx:xx 也就是...Pattern pattern=Pattern.compile(regex); String input=result.trim(); Matcher matcher=pattern.matcher(input); while(matcher.find()){
  • Java 枚举(enum) 详解7种常见的用法

    万次阅读 多人点赞 2016-08-11 11:14:45
    Java中它虽然算个“小”功能,却给我的开发带来了“大”方便。 大师兄我【大师兄】又加上自己的理解,来帮助各位理解一下。 用法一:常量 在JDK1.5之前,我们定义常量都是:public static final....。现在好了...
  • java正则表达式 match find匹配位置

    千次阅读 2019-01-09 12:00:06
    java正则表达式 match find匹配位置
  • 在开发过程中我们经常会用到String的indexOf方法,这个方法是用来获取某个特定字符在指定字符串中第一次出现的位置,通常用来判断是否包含或者用来进行字符串的截取,有些时候我们不仅需要获取这个特定字符第一次...
  • public static void main(String[] args){ String string = "213213/13123/14432432"; //这里是获取"... Matcher slashMatcher = Pattern.compile("/1").matcher(string); ...
  • /** * 查询字符串中首个数字出现的位置 * @param str 查询的字符串 * @return 若存在,返回位置索引,否则返回-1; */ ... Matcher matcher = Pattern.compile("[0-9]").matcher(str); if(...
  • jwt

    千次阅读 2019-08-17 22:19:01
    import java.util.regex.Pattern; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet....
  • 要解决这个问题,知道其发生的原因,就是了解 url-pattern 的规则。Servlet和filter是J2EE开发中常用的技术,使用方便,配置简单。servlet和filter中的url-pattern有一些文章在里面的,总结了一些东西,以免遇到...
  • java解析docx获取文字图片并识别图片准确位置需求简述功能模块版本1生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的...
  • public static int getCharacterPosition(String string){ //这里是获取"/"符号的位置 Matcher slashMatcher = Pattern.compile("/").matcher(string); int mIdx = 0; while(slashMatcher.find()) { mI
  • Java面试题及答案2019版(下)

    万次阅读 多人点赞 2019-01-30 10:04:58
    上一篇:2019年最新Java面试题及答案整理(上) 51、类ExampleA继承Exception,类ExampleB继承ExampleA。 有如下代码片断: try{ thrownewExampleB("b") }catch(ExampleAe){ System.out.println("ExampleA"); ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 96,763
精华内容 38,705
关键字:

java找到pattern的位置

java 订阅