Pattern跟Matcher详解(字符串匹配和字节码)

Pattern和Matcher详解(字符串匹配和字节码)

一:起因

(1)Java里面进行字符串操作,第一个想到的是String类 和 StringBuilder类 内含replace() 、replaceAll() 、split()、matches()等方法 —— 其实String类里面的           public String[] split(String regex, int limit) 和 matches()方法,调用是Pattern.compile().matches()方法 ----- 源码为:

        return Pattern.compile(regex).split(this, limit);

(2)String类里面的public String replaceAll(String regex, String replacement) 方法 也是一样的 ----- 源码为 : 

      public String replaceAll(String regex, String replacement) {
        return Pattern.compile(regex).matcher(this).replaceAll(replacement);
    }

(3)String类里面的public boolean matches(String regex)方法 也是一样的 ----- 源码为 :

public boolean matches(String regex) {
        return Pattern.matches(regex, this);
    }

(4)相对于Java, C++语言就没有那么高大上了,连split() 和 trim()函数都没有,但是可以通过c++string类里面的其他函数实现,例如find_first_of / find_not_first_of() 等。

(5)java正则表达式通过java.util.regex包下的Pattern类与Matcher类实现,(建议在阅读本文时,打开java API文档,当介绍到哪个方法时,查看java API中的方法说明,效果会更佳).

二:详解

(1)Pattern类用于创建一个正则表达式,也可以说创建一个匹配模式,它的构造方法是私有的,不可以直接创建,但可以通过Pattern.complie(String regex)简单工厂方法创建一个正则表达式,轮到Matcher类登场了,Pattern.matcher(CharSequence input)返回一个Matcher对象. Matcher类的构造方法也是私有的,不能随意创建,只能通过Pattern.matcher(CharSequence input)方法得到该类的实例:

(2)find()对字符串进行匹配,匹配到的字符串可以在任何位置.  当使用matches(),lookingAt(),find()执行匹配操作后,就可以利用如下三个方法得到更详细的信息Mathcer.start()/ Matcher.end()/ Matcher.group() 
start()返回匹配到的子字符串在字符串中的索引位置. 
end()返回匹配到的子字符串的最后一个字符在字符串中的索引位置. 
group()返回匹配到的子字符串

Pattern p=Pattern.compile("\\d+"); 
Matcher m=p.matcher("aaa2223bb"); 
m.find();//匹配2223 
m.start();//返回3 
m.end();//返回7,返回的是2223后的索引号 
m.group();//返回2223 

Mathcer m2=p.matcher("2223bb"); 
m2.lookingAt();   //匹配2223 
m2.start();   //返回0,由于lookingAt()只能匹配前面的字符串,所以当使用lookingAt()匹配时,start()方法总是返回0 
m2.end();   //返回4 
m2.group();   //返回2223 

Matcher m3=p.matcher("2223"); //如果Matcher m3=p.matcher("2223bb"); 那么下面的方法出错,因为不匹配返回false
m3.matches();   //匹配整个字符串 
m3.start();   //返回0
m3.end();   //返回3,原因相信大家也清楚了,因为matches()需要匹配所有字符串 
m3.group();   //返回2223

说说正则表达式的分组在java中是怎么使用的. 
start(),end(),group()均有一个重载方法它们是start(int i),end(int i),group(int i)专用于分组操作,Mathcer类还有一个groupCount()用于返回有多少组.
三:案例回放

(1)简单练习

 public static void main(String[] args) {
	        String phones1 = "MKY 的手机号码:0939-100391" 
	        		+"XL 的手机号码:0939-666888aaaa"
	        		+"LJ 的手机号码:0952-600391" 
    				+"XQZ 的手机号码:0939-550391";;
	        String regex = ".*0939-\\d{6}";
	        Pattern pattern = Pattern.compile(regex);
	        Matcher matcher = pattern.matcher(phones1);

	        while(matcher.find()) {
	            System.out.println(matcher.group()+"&&&&&");
	            System.out.println("start: " + matcher.start());
	            System.out.println("end: " + matcher.end());
	        }// 仅仅返回一个结果的,返回最长的结果,而不是一截一截的
	        
	        String phones2 = "LJ 的手机号码:0952-600391\r\n" 
	        				+"XQZ 的手机号码:0939-550391";
	        //重用pattern
	        matcher = pattern.matcher(phones2);

	        while(matcher.find()) {
	            System.out.println(matcher.group());
	        }
	        // 另外一个pattern
	        String text = "abcdebcadxbc";
	        regex = ".bc";
	        Pattern pattern2 = Pattern.compile(regex);
	        matcher = pattern2.matcher(text);

	        while(matcher.find()) {
	            System.out.println(matcher.group()+"****");
	            System.out.println("start: " + matcher.start());
	            System.out.println("end: " + matcher.end());
	        }// 返回结果的是多个的
	        System.out.println("*************");
	        // 下面是两个非常重要的
	        pattern = Pattern.compile("<.+?>", Pattern.DOTALL); 
	        matcher = pattern.matcher("<a href=\"index.html\">主页</a>"); 
	        String string = matcher.replaceAll(""); 
	        System.out.println(string); 
	        
	        pattern = Pattern.compile("href=\"(.+?)\""); 
	        matcher = pattern.matcher("<a href=\"index.html\">主页</a>"); 
	        if(matcher.find()) 
	          System.out.println(matcher.group(1)); 
	    }

(2)Java 正则表达式(此部分为转载)


 现在通过一些实验来说明正则表达式的匹配规则,这儿是Greedy方式
  .              任何字符
 a?             a一次或一次也没有
 a*             a零次或多次
 a+            a一次或多次
 a{n}?      a恰好 n 次
 a{n,}?       a至少n次 
 a{n,m}?   a至少n次,但是不超过m次


//初步认识. * + ?
        p("a".matches("."));//true
        p("aa".matches("aa"));//true
        p("aaaa".matches("a*"));//true
        p("aaaa".matches("a+"));//true
        p("".matches("a*"));//true
        p("aaaa".matches("a?"));//false
        p("".matches("a?"));//true
        p("a".matches("a?"));//true
        p("1232435463685899".matches("\\d{3,100}"));//true
        p("192.168.0.aaa".matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"));//false
        p("192".matches("[0-2][0-9][0-9]"));//true [abc]                        a、b 或 c(简单类)
[^abc]                      任何字符,除了 a、b 或 c(否定)
[a-zA-Z]                   a 到 z 或 A 到 Z,两头的字母包括在内(范围)
[a-d[m-p]]                a 到 d 或 m 到 p:[a-dm-p](并集)
[a-z&&[def]]             d、e 或 f(交集)
[a-z&&[^bc]]             a 到 z,除了 b 和 c:[ad-z](减去)
[a-z&&[^m-p]]            a 到 z,而非 m 到 p:[a-lq-z](减去)
//范围
        p("a".matches("[abc]"));//true
        p("a".matches("[^abc]"));//false
        p("A".matches("[a-zA-Z]"));//true
        p("A".matches("[a-z]|[A-Z]"));//true
        p("A".matches("[a-z[A-Z]]"));//true
        p("R".matches("[A-Z&&[RFG]]"));//true
\d                          数字:[0-9]
\D                         非数字: [^0-9]
\s                          空白字符:[ \t\n\x0B\f\r]
\S                         非空白字符:[^\s]
\w                         单词字符:[a-zA-Z_0-9]
\W                        非单词字符:[^\w]
         //认识\s \w \d \
        p("\n\r\t".matches("\\s(4)"));//false
        p(" ".matches("\\S"));//false
        p("a_8 ".matches("\\w(3)"));//false
        p("abc888&^%".matches("[a-z]{1,3}\\d+[&^#%]+"));//true
        p("\\".matches("\\\\"));//true

 边界匹配器
      ^                                          行的开头
      $                                          行的结尾
      \b                                        单词边界
      \B                                        非单词边界
      \A                                        输入的开头
      \G                                       上一个匹配的结尾
      \Z                                       输入的结尾,仅用于最后的结束符(如果有的话)
      \z                                       输入的结尾
//边界匹配
        p("hello sir".matches("^h.*"));//true
        p("hello sir".matches(".*ir$"));//true
        p("hello sir".matches("^h[a-z]{1,3}o\\b.*"));//true
        p("hellosir".matches("^h[a-z]{1,3}o\\b.*"));//false
        //空白行:一个或多个(空白并且非换行符)开头,并以换行符结尾
        p(" \n".matches("^[\\s&&[^\\n]]*\\n$"));//true

重点说明
表示<>之间有任意一个(含)字符以上,括号表示捕获组,匹配后可以单独提取出括号内的内容,?代表最短匹配,比如<asdf>>>这样的输入,有?会匹配成<asdf>,没有?会匹配整个<asdf>>>。

str.ReplactAll("<(.)+?>","")就是把所有<>间有一个字符以上的文字都替换为空。比如
asdf<1234>jkl<>会变成asdfjkl<>
另外要是str_line.replaceAll("&(.)+?;"," ") 是将&开头的包含任意字符的右面的最短匹配并以;结束的都替换成为空

四:java 字节码

(1)

 ldc将int, float或String型常量值从常量池中推送至栈顶
 iload 将指定的int型本地变量推送至栈顶
 lload 将指定的long型本地变量推送至栈顶
 (2)
 fstore 将栈顶float型数值存入指定本地变量
 dstore 将栈顶double型数值存入指定本地变量
 astore 将栈顶引用型数值存入指定本地变量
 istore_1 将栈顶int型数值存入第二个本地变量
 astore_1 将栈顶引用型数值存入第二个本地变量
 astore_2 将栈顶引用型数值存入第三个本地变量

 pop 将栈顶数值弹出 (数值不能是long或double类型的)
 dup 复制栈顶数值并将复制值压入栈顶
 
 iadd 将栈顶两int型数值相加并将结果压入栈顶
 ladd 将栈顶两long型数值相加并将结果压入栈顶
 isub 将栈顶两int型数值相减并将结果压入栈顶
 imul 将栈顶两int型数值相乘并将结果压入栈顶
(3)
 ireturn 从当前方法返回int
 areturn 从当前方法返回对象引用
 return 从当前方法返回void
 
 getstatic 获取指定类的静态域,并将其值压入栈顶
 putstatic 为指定的类的静态域赋值
 getfield 获取指定类的实例域,并将其值压入栈顶
 putfield 为指定的类的实例域赋值
 
 invokevirtual 调用实例方法
 invokespecial 调用超类构造方法,实例初始化方法,私有方法
 invokestatic 调用静态方法
 invokeinterface 调用接口方法
 (4)
 new 创建一个对象,并将其引用值压入栈顶
 newarray 创建一个指定原始类型(如int, float, char…)的数组,并将其引用值压入栈顶
 
 if_icmpgt 比较栈顶两int型数值大小,当结果大于0时跳转
 if_icmple 比较栈顶两int型数值大小,当结果小于等于0时跳转

五:String类的源码分析

<strong> (</strong>1)  subString()  ---  public String substring(int beginIndex, int endIndex)
 
     /**
     * Returns a new string that is a substring of this string. The
     * substring begins at the specified <code>beginIndex</code> and
     * extends to the character at index <code>endIndex - 1</code>.
     * Thus the length of the substring is <code>endIndex-beginIndex</code>.
     * <p>
     * Examples:
     * <blockquote><pre>
     * "hamburger".substring(4, 8) returns "urge"
     * "smiles".substring(1, 5) returns "mile"
     * </pre></blockquote>
     *
     * @param      beginIndex   the beginning index, inclusive.
     * @param      endIndex     the ending index, exclusive.
     * @return     the specified substring.
     * @exception  IndexOutOfBoundsException  if the
     *             <code>beginIndex</code> is negative, or
     *             <code>endIndex</code> is larger than the length of
     *             this <code>String</code> object, or
     *             <code>beginIndex</code> is larger than
     *             <code>endIndex</code>.
     */
    public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > count) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        if (beginIndex > endIndex) {
            throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
        }
        return ((beginIndex == 0) && (endIndex == count)) ? this :
            new String(offset + beginIndex, endIndex - beginIndex, value);
    }
  
 (2) -- indexOf(ch)  indexOf(String str) 匹配整个str才返回首位置
/**
     * Code shared by String and StringBuffer to do searches. The
     * source is the character array being searched, and the target
     * is the string being searched for.
     *
     * @param   source       the characters being searched.
     * @param   sourceOffset offset of the source string.
     * @param   sourceCount  count of the source string.
     * @param   target       the characters being searched for.
     * @param   targetOffset offset of the target string.
     * @param   targetCount  count of the target string.
     * @param   fromIndex    the index to begin searching from.
     */
    static int indexOf(char[] source, int sourceOffset, int sourceCount,
                       char[] target, int targetOffset, int targetCount,
                       int fromIndex) {
        if (fromIndex >= sourceCount) {
            return (targetCount == 0 ? sourceCount : -1);
        }
        if (fromIndex < 0) {
            fromIndex = 0;
        }
        if (targetCount == 0) {
            return fromIndex;
        }

        char first  = target[targetOffset];
        int max = sourceOffset + (sourceCount - targetCount);

        for (int i = sourceOffset + fromIndex; i <= max; i++) {
            /* Look for first character. */
            if (source[i] != first) {
                while (++i <= max && source[i] != first);
            }

            /* Found first character, now look at the rest of v2 */
            if (i <= max) {
                int j = i + 1;
                int end = j + targetCount - 1;
                for (int k = targetOffset + 1; j < end && source[j] ==
                         target[k]; j++, k++);

                if (j == end) {
                    /* Found whole string. */
                    return i - sourceOffset;
                }
            }
        }
        return -1;
    } 
(3)总之,C++的string类里面的find_first_of() 和 Java里的indexOf()不一样的,substring()  貌似没有

3楼u0107003351小时前
[code=java]n//想法比较随意,想到哪里就写到了哪里,大家不要见怪 ~ 下面是IO小结n( 一 )以字节为导向的 stream------InputStream/OutputStream;( 二 )以字符为导向的 stream Reader/Writer;InputStreamReader 和 OutputStreamReader :把一个以字节为导向的 stream 转换成一个以字符为导向的 stream n1 --.InputStream、OutputStreamn处理字节流的抽象类nInputStream 是字节输入流的所有类的超类,一般我们使用它的子类,如FileInputStream等.nOutputStream是字节输出流的所有类的超类,一般我们使用它的子类,如FileOutputStream等.n n2 --.InputStreamReader OutputStreamWritern处理字符流的抽象类nInputStreamReader 是字节流通向字符流的桥梁,它将字节流转换为字符流.nOutputStreamWriter是字符流通向字节流的桥梁,它将字符流转换为字节流.n n3 --.BufferedReader BufferedWriternBufferedReader 由Reader类扩展而来,提供通用的缓冲方式文本读取,readLine读取一个文本行,n从字符输入流中读取文本,缓冲各个字符,从而提供字符、数组和行的高效读取。nBufferedWriter 由Writer 类扩展而来,提供通用的缓冲方式文本写入, newLine使用平台自己的行分隔符,n将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。n[/code]
2楼zhang1xiao1abc7小时前
怎么讲着讲着 Pattern Matcher类的正则表达式,到了String类的源码啦,不过还是多谢分析
1楼u0139455688小时前
喜欢 String类 和 Pattern类的对比部分