j2se中一个重要的部分正则表达式很强大,它在处理字符串匹配时强大有余啊。主要涉及到java.util.regex包中的Pattern类与Matcher类。
下面从代码中,走入正则表达式
代码片段一,入门级的:
01 | private static void testRegExpAccidence() { |
02 | //正则表达示第一个内容,点.可以替换任何一个字符。所以下面的应该是true。match(...)代码,是否符合有三个字符. |
03 | sop( "abc" .matches( "..." )); |
05 | //正则表达示2,reaplaceAll第一参数支持正则,而\\d代表的是数字,就是说把所有数字替换成&。 |
06 | sop( "abc1234abc" .replaceAll( "\\d" , "&" )); |
08 | //正则表达示3,compile的玩意。一般需要先把正则规则编译成Pattern,而Pattern无构造方法,只能通过compile方法 |
09 | Pattern pat = Pattern.compile( "[abc]{3}" ); |
10 | Matcher mat = pat.matcher( "abc" ); //通过编译出来的Pattern去匹配一个字符,返回的一个是匹配的结果用Matcher对象 |
11 | sop(mat.matches()); //其实也可以直接用"abc".matches("[abc]{3}") 来判断字符串,是否符合[abc]{3}正则表达示 |
13 | sop( "------------. * ?作用 --------------------" ); |
15 | sop( "a" .matches( "." )); //.匹配任意一个 |
16 | sop( "aaaa" .matches( "a*" )); //X* 代表X出现0次或多次 |
17 | sop( "aaaa" .matches( "a " )); //X 代表X出现1次或多次 |
18 | sop( "aaaa" .matches( "a?" )); //false //X? 代表X出现1次或0次 |
19 | sop( "" .matches( "a*" )); |
20 | sop( "" .matches( "a " )); //false |
21 | sop( "" .matches( "a?" )); |
22 | sop( "a" .matches( "a*" )); |
23 | sop( "a" .matches( "a " )); |
24 | sop( "a" .matches( "a?" )); |
25 | sop( "2343902594" .matches( "\\d{3,100}" )); // \\d代表数字然后{3,100}代表至少出现3次,至多出现100次 |
26 | sop( "192.168.0.aaa" .matches( "\\d{3}\\.\\d{3}\\.\\d{3}\\.\\d{3}" )); //false,其它未标识全部是true |
27 | sop( "192" .matches( "[0-2][0-9][0-9]" )); |
29 | sop( "---------------范围的匹配------------------------" ); |
30 | sop( "a" .matches( "[abc]" )); //true,[abc]代表,或者a或者b或者c,取三个之中的任意一个 |
31 | sop( "a" .matches( "[^abc]" )); //false, [^abc]代表,不得是a或b或c,即不得是三个中的任意一个 |
32 | sop( "A" .matches( "[a-zA-Z]" )); //true 范围[a-zA-Z]代表是a-z或A-Z中的任意一个 |
33 | sop( "A" .matches( "[a-z]|[A-Z]" )); //true,范围[a-z]|[A-Z]也是代表a-z或A-Z中的任意一个 |
34 | sop( "A" .matches( "[a-z[A-Z]]" )); //true,范围[a-z[A-Z]]也是代表a-z或A-Z中的任意一个 |
35 | sop( "R" .matches( "[A-Z&&[RFG]]" )); //true 范围[A-Z&&[RFG]]代表取A-Z与RFG两个范围交集RFG中的任意一个 |
37 | sop( "---------------认识反斜杠s、反斜杠w、反斜杠d、反斜杠------------------------" ); |
38 | //\s \w \d \ \s代表空格 \w代表一个单词(a-z0-9_A-Z) \d代表数字 |
39 | // \ 通常和转义一起用,如\t\r\n之类的,若只当成一个普通字符\,则需要再转义一次 \\。 |
40 | sop( " \n\r\t" .matches( "\\s{4}" )); //\s,转义需要再转义一次即\\s,代表四个空格。 |
41 | sop( " " .matches( "\\S" )); // \S,代表是\s的非取反,即不是空格(非空格)。 规则一般大写是小写的语义相反如\D是非数字 结果肯定false。 |
42 | sop( "a_8" .matches( "\\w{3}" )); // \\w代表单词中的字母,{3}代表出现三次。结果肯定是true了。 |
43 | //sop("\\".matches("\\")); // 注意这里肯定会报错,因为在matches正则中,先java解释两个\\是一个转义, |
44 | // 但一个转义让正则去解释肯定要报错,它像java一样要再用一个转义来转义它,证明它是一个普通的反斜杠。 |
45 | sop( "\\" .matches( "\\\\" )); |
47 | sop( "---------------认识boundary边界 ^及$ \\b------------------------" ); |
48 | sop( "hello sir" .matches( "^h.*" )); //^不在中括号[]中,代表以什么开头,如这里是代表以h开头 |
49 | sop( "hello sir" .matches( ".*ir$" )); //$代表以什么结尾,如这里是以ir结尾 |
50 | // 这里有\\b代表 单词边界,可以以空格 \t或\r \n等来隔开。 |
51 | sop( "hello sir" .matches( "^h[a-z]{1,3}o\\b.*" )); //这里的.要加上,代理单词边界后面还是字符,1个或多个。 |
52 | sop( "hellosir" .matches( "^h[a-z]{1,3}o\\b*" )); //就是false了。 |
55 | sop( "---------几个小练习------------" ); |
56 | sop( "aaa 8888c" .matches( ".*\\d{4}." )); //肯定是true,因为.*代表任意出现0次或多次 |
57 | sop( "aaa 8888c" .matches( ".*\\b\\d{4}." )); //肯定是true,因为.*代表任意出现0次或多次 |
58 | sop( "aaa8888c" .matches( ".*\\d{4}." )); |
59 | sop( "aaa8888c" .matches( ".*\\b\\d{4}." )); //这个就false了,因为\\b是单词边界,后面又跟着\d{4}肯定没戏。 |
61 | sop( "chenssadf@aio7.com" .matches( "\\w @\\w \\.\\w " )); //这个虽然可以用,但似乎对前面的@单词可以用.或-的没有加上哦。 |
62 | sop( "-chenssadf@aio7.com" .matches( "[\\w.-] @[\\w.-] \\.\\w " )); |
代码片段二:掌握Matcher类的 find、start、end、lookingAt方法:
01 | private static void testFindAndLookingAt() { |
02 | sop( "---------------------matcher.find 与 matcher.lookingAt match.reset等方法测试--------------------------" ); |
03 | Pattern p = Pattern.compile( "\\d{3,5}" ); |
04 | Matcher match = p.matcher( "234-32132-4321-00" ); |
05 | sop(match.matches()); //matches方法与find方法相互操作是有影响的,因为两者查看完会记录出下个要操作的指针位置。 |
06 | match.reset(); //比如这里没有reset重置指针位置时,matches方法虽然匹配失败false,但它吐进去的4个字符不会吐出来 |
07 | sop(match.find()); //这样,这里的第一个find从32132开始查了,就不从头了。(除非前面有reset重置)。所以接下来最后两上肯定是false了。 |
08 | sop(match.start() "--" match.end()); //上面的find必须返回是true ,然后start是返回本次找到数据的第一位置,这里肯定是0,最后一个位置3(3是不包括) |
10 | sop(match.start() "--" match.end()); // start肯定就是4,end就是9了。 |
12 | sop(match.start() "--" match.end()); |
14 | //sop(match.start() "--" match.end()); //注意这里,不能打印的。因为上面的find返回的是false,根本没有找到。所以start或end方法 |
15 | //会报IllegalStateException: No match available |
17 | sop(match.lookingAt()); //这下面的lookingAt打印永远都是true,因为lookingAt永远每次都是重新重头打开查。 |
18 | sop(match.lookingAt()); |
代码片段三:掌握强大的replace、replaceAll、appendReplacement方法
01 | private static void testReplacement() { |
02 | sop( "---------------------------match.replece及相关的替换测试---------------------" ); |
03 | //CASE_INSENSITIVE 大小写不敏感即能吃呢 Pattern.CANON_EQ 默认的方法即是敏感必须相同 |
04 | Pattern p = Pattern.compile( "java" ,Pattern.CASE_INSENSITIVE); |
05 | //怎么把下面一串乱七八槽的java替换掉呢。比如只替换正则中规定的java |
06 | Matcher match = p.matcher( "Java java JAva JAVa ILOVEJAVA youhateJaVa asdfasl;kdfj" ); |
07 | //sop(match.replaceAll("SOP")); //可以将java全部替换成SOP,或只把小写java替换都可以。 |
08 | //现有有个需求是,把单次数出现的java大小写均可,替换成SOP,双次数出现的替换成HELP。可以通过while循环find来搞 |
11 | StringBuffer buf = new StringBuffer(); //这里不可以写StringBuilder,因为matcher后面的方法不支持Builder |
14 | match.appendReplacement(buf, "HELP" ); |
16 | match.appendReplacement(buf, "SOP" ); |
19 | sop(match.appendTail(buf)); //把后面没有find上的尾巴asdf等字符添加 进去。 |
代码片段四:url中抓取邮件地址:
02 | * 通过向一网站,发送http请求,把请求回响回来的网页内容进行解析,分析出来e-mail出来。 |
04 | private static void testUrlFetchEmail() throws Exception { |
05 | sop( "-----------------用URL发送一个请求给网站,对返回的内部进行抓取e-mail地址出来----------------------" ); |
06 | //http://dzh.mop.com/whbm/20060220/3/S7gSlI706d198a73.shtml |
07 | URL url = new URL( "http://dzh.mop.com/whbm/20060220/3/S7gSlI706d198a73.shtml" ); |
08 | sop(url.getContent()); |
10 | InputStream is = url.openStream(); |
11 | BufferedReader br = new BufferedReader( new InputStreamReader(is, "utf-8" )); |
12 | Pattern p = Pattern.compile( "([\\w.-] )@([\\w.-] )\\.\\w " ); |
13 | //Pattern p = Pattern.compile("^([\\w.-] )@([\\w.-] )\\.\\w "); //这里把^加上去就挂了,代表这个字符串是以邮件开头 |
14 | String line = null ; //从URL中读出来的字符行,肯定不会是以邮件开头的,至少都是<br>或<P>或<dir>之类的,不会以邮件开头的吧。 |
15 | while ((line=br.readLine())!= null ){ |
16 | Matcher ma = p.matcher(line); |
17 | while (ma.find()){ //group必须与find一起来联系。否则报:java.lang.IllegalStateException: No match found |
18 | sop(ma.group() "这家伙用户名:" ma.group( 1 ) ",用的公司域名:" ma.group( 2 )); |
21 | //java.lang.IllegalStateException: No match found |
代码片段五:代码行统计
02 | * 若是文件夹则递归,若是普通的.java文件则传给统计代码方法。 |
03 | * @param dir 是传入到此方法中 目录的文件。如想查到D:\test文件夹(会以递归查所有文件)中所有代码 |
06 | private static void parseDirCodeNum(File dir) throws Exception{ |
07 | File[] files = dir.listFiles( new FilenameFilter(){ |
09 | public boolean accept(File dir, String name) { |
10 | File tempFile = new File(dir,name); |
11 | if (tempFile.isFile()){ //若是个普通文件的话,则过滤一切不是.java的文件,文件夹为true,以便再次递归 |
12 | if (name.endsWith( ".java" )){ |
26 | parseFileDirNum(f); //是普通java文件则解析出代码行数 |
35 | private static void parseFileDirNum(File f) throws Exception{ |
36 | BufferedReader br = new BufferedReader( new FileReader(f)); |
37 | boolean isComment = false ; |
39 | while ((line = br.readLine())!= null ){ |
40 | line = line.trim(); //一开始就去掉取出来行中的 空格,因为对于注释前面一般有\t影响stratsStart |
41 | if (line.startsWith( "/*" )&&line.endsWith( "*/" )){ //只有一行注释 |
43 | } else if (line.startsWith( "/*" )){ //可能出现了多行注释了,那么就标识注释isComment开始了,true了。 |
46 | } else if (isComment){ //要是标识开始了,未标识出false统统认为是注释行。当然若注释结束了*/了,则去掉标识。 |
48 | if (line.endsWith( "*/" )){ //这里没有问题,因为注释中,不能含有*/的。 |
51 | } else if (line.startsWith( "//" )){ |
54 | else if (line.matches( "^[\\s&&[^\\n]]*$" )){ //空白行,必须是空白开头且不能回车 |
代码片段六:掌握Greedy Reluctant Possessive数量词的区别:
02 | * 测试Greedy Reluctant Possessive数量词 |
03 | * Greedy是贪吃 肯定是一上来,就以最多的吃掉,若不满足且可退时,再退出一个再匹配... |
04 | * Reluctant是 勉强不情意的。 一上来,就吃掉最小的,若不满足且可再吃时,就再吃一个匹配... |
07 | private static void testGreedyReluctantPossessive() { |
08 | sop( "-------------测试 Greedy Reluctant Possessive数量词-----------------------" ); |
09 | Pattern gp = Pattern.compile( ".{3,10}[0-9]" ); //Greedy方式 |
10 | Pattern rp = Pattern.compile( ".{3,10}?[0-9]" ); //Reluctant方式 主要的与Greedy区别是多出一个? |
11 | Pattern pp = Pattern.compile( ".{3,10} [0-9]" ); //Possessive方式 主要的与Greedy区别是多出一个 |
12 | Matcher gm = gp.matcher( "asdf8sdfa9" ); //通过Greedy匹配出来的匹配器 |
13 | Matcher rm = rp.matcher( "asdf8sdfa9" ); //通过Reluctant匹配出来的匹配器 |
14 | Matcher pm = pp.matcher( "asdf8sdfa9" ); //通过Possessive匹配出来的匹配器 |
16 | if (gm.find()){ //因为gm是一次吃掉10个发现后面要求一个数字所以就退回了一个,这样正好满足找到了。 |
17 | sop(gm.start() " " gm.end()); //返回0 10 |
22 | if (rm.find()){ //因为rm是一次吃掉3发现后面要求一个数字,就再吃掉一个,这样正好满足找到了。 |
23 | sop(rm.start() " " rm.end()); //返回0 5 |
28 | if (pm.find()){ //因为pm是一次性吃掉10个,然后正规要求后面一个数字,但它不吐出来(看来更贪婪,难怪是独占式的)。找不到了。 |
29 | sop(pm.start() " " pm.end()); |
31 | sop( "找不到匹配的" ); //返回找不到 |
代码片段七相关测试代码:
01 | private static long normalLine ; |
02 | private static long commentLine ; |
03 | private static long whitespaceLine ; |
05 | public static void main(String[] args) throws Exception{ |
06 | testRegExpAccidence(); |
07 | testFindAndLookingAt(); |
10 | sop( "-----------------match.group及相关的分组测试--------" ); |
11 | Pattern p = Pattern.compile( "([\\w.-] )@([\\w.-] )\\.\\w " ); |
12 | Matcher m = p.matcher( "-chenshufei2@sina.com" ); |
15 | sop(m.group( 1 )); //可以得出用户名-chenshufei2 |
16 | sop(m.group( 2 )); //可以得出域公司名sina |
17 | //sop(m.group(3)); 会报出一个java.lang.IndexOutOfBoundsException: No group 3 |
18 | //从此 可以验证出,分组就是看正则中小括号(出现的位置,而上面的没有第三个(,所以会报异常。 |
21 | //testUrlFetchEmail(); //D:/java_setup_program_file/eclipse/design_pattern/EnhanceJava |
22 | String dirStr = "D:\\360Rec" ; |
23 | File dir = new File(dirStr); |
25 | sop( "解析文件时,共有normalLine::" normalLine " ,commentLine::" commentLine " ,whitespaceLine::" whitespaceLine); |
28 | testGreedyReluctantPossessive(); |
30 | public static void sop(Object obj){ |
31 | System.out.println(obj); |
|