1.常规判断一个字符串是以什么开头,是否是数字的判断方式有:
a.通过比较每个字符,注意比较是字符值(ASc码值),不是字面值
String s="1234567890098765432101234567890";
boolean startsWith = s.startsWith("168"); //判断每个字符数组的每个字符 char[] charArray = s.toCharArray(); for(int i=0;i<charArray.length;i++){ //特别注意每个数字字符对应的asc码并不是自生的值,所以必须比较他们字符值,而不是字面值 System.out.println((int)'0');//ASC码值:48 if(charArray[i]<'0'&&charArray[i]>'9')//而不是小于0;大于9,是字符 //不是数字 break; }
b.通过类型转换抛出异常来确定,但是数值类型都有大小的局限性:
// maxLong= 9223372036854775807 也只有19位 // maxInt= 2147483647 也只有10位 try { int parseInt = Integer.parseInt(s); long parseLong = Long.parseLong(s); } catch (NumberFormatException e) { // 不是数字异常 }
而正则表达式,是通过一些特定的符号,简化了对字符串的复杂操作。
缺点:就是定义的符号越多,表达式越长,可读性就越差
String regex="[0-9][1-9]{4,22}"; boolean matches = s.matches(regex); System.out.println(matches);
2.符号意义:
转义符,特别注意:表示字符的时,用x时要用\x,单反斜杠的组合都被认为是一些有意义效果,(即直接体现出效果了,而不是代表该意义的字符,如果是不存的效果就报错)
\ 反斜线字符 制表符 ('u0009') 新行(换行)符 ('u000A') 回车符 ('u000D') f 换页符 ('u000C') e 转义符 ('u001B')
2.1表示单个字符,用中括号括起来,多个中括号嵌套也是一个字符:^:表示除某某以外;-:表示从某到某之间;&&:且满足;
字符类 [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](减去)
2.2预定义的单个字符:(简化一些,数字(digital),空字符(space),命名字符(word),的表示
预定义字符类 . 任何字符(与行结束符可能匹配也可能不匹配) d 数字:[0-9] D 非数字: [^0-9] s 空白字符:[ x0Bf ] S 非空白字符:[^s] w 单词字符:[a-zA-Z_0-9] W 非单词字符:[^w]
2.3表示数量的符号:(表示出现x的次数)与确切数量相关的用大括号加数字。
X? X,一次或一次也没有 X* X,零次或多次 X+ X,一次或多次 X{n} X,恰好 n 次 X{n,} X,至少 n 次 X{n,m} X,至少 n 次,但是不超过 m 次
Logical 运算符 XY X 后跟 Y X|Y X 或 Y (X) X,作为组 比如:((A)(B(C)) ,数左组括号有几个就是几个组,第一个括号的内容就是第一个,一组:((A)(B(C))); 二组:(A); 三组:(B(C)); 四组:(C) Back 引用 任何匹配的组 //比如两个任意字符可以这样表示: (.)1
2.4边界:
边界匹配器 ^ 行的开头 //在中括号中这个符号表示非 $ 行的结尾 //这个符号还表示对前一个组的引用比如:$1; 对本组的引用:1; 单词边界 //单词边界即中间有空格认为是单词边界如下3.4例子 B 非单词边界 A 输入的开头 G 上一个匹配的结尾 输入的结尾,仅用于最后的结束符(如果有的话) z 输入的结尾
3.正则表达式功能:可以用string提供的方法
3.1匹配:match(String regex)
s="18609876532"; //比如匹配186开头手机号(11位) String regex="186\d{8}"; boolean matches = s.matches(regex);
3.2切割:split(String regex)
1//需求要切去不确定的空时 s="wo shi wo bu yi yang de"; //不能直接使用空格因为切出空的字符串 //[wo, shi, wo, , bu, , , yi, , , , , yang, , , , , , , , , de] System.out.println(Arrays.toString(s.split(" "))); //可以用正则表示一或多个空格 String[] split = s.split(" +"); //[wo, shi, wo, bu, yi, yang, de] System.out.println(Arrays.toString(split)); 2.1//注意分割符号是特殊意义的时
//需要反斜杠转义,而反斜杠也是特殊符号,又要转义,就变成双反斜杠了(\) s="wo.li.hai.ba";//按.拆分 String[] split1 = s.split(".");//错误因为.代表了任意字符 System.out.println(Arrays.toString(split1));//[] String[] split2 = s.split("\."); System.out.println(Arrays.toString(split2));//[wo, li, hai, ba]
2.2//按双反斜杠拆分
s="c:\myfile\test.txt"; System.out.println(s);//实际效果是c:myfile est.txt //考虑到转义需要\\ System.out.println(Arrays.toString(s.split("\\")));//拆分时并不考虑原字符串实际效果
3//按两个重叠出现的字符拆分(用到了组的概念) s="abbbcdeffg11hi3338kkloll35mn"; System.out.println(Arrays.toString(s.split(".{2}")));//错误,表示只是任意两个字符 System.out.println(Arrays.toString(s.split("(.)\1")));//分组,再引用的方式
//按只要重叠(两个或者多个)出现的字符拆分,[a, cde, g, hi, 8, lo, 35mn]
System.out.println(Arrays.toString(s.split("(.)\1+")));
3.3替换:replaceAll(String regex,String replacement):同一个表达式获得组,获取前一个表达式的组,特别是:$符号
// 将替换5个以上的数字替换为# s = "aaa12ccccc36554yz33"; String replace = s.replaceAll("\d{5,}", "#"); System.out.println(replace); // 将重叠替换# System.out.println(s.replaceAll("(.)\1+", "#")); // 将重叠字符替换一个字符 //需要用到特殊符号$,表示前一个组的内容 System.out.println(s.replaceAll("(.)\1+", "$1"));
输出:
aaa12ccccc#yz33 #12#36#4yz# a12c3654yz3
补充记错的一个地方:
String str = " a b c "; System.out.println(str.trim());//a b c ;曾经记错了以为去除所有空格;这个方法只能去除首尾的空格 System.out.println(str.replaceAll(" ",""));//abc
3.4获取,查找:对满足正则的字符串内容的查找,获取(对正则表达式封装的类Pattern也叫模式对象)
//需要处理的字符串 s="wo xi huan ni ning zhi dao ma"; //匹配的正则规则,空格认为是单词边界 String regex1="\b[a-z]{2}\b"; //编译正则生成模式对象(正则的封装对象) Pattern pattern = Pattern.compile(regex1); //正则和字符串进行关联生成匹配器,(字符串的匹配替换方法就对匹配器的封装) Matcher matcher = pattern.matcher(s); //find方法每次执行会依次向后匹配一个满足正则的字符串,存在返回true while(matcher.find()){ //group返回一个find刚匹配的字符串 String group = matcher.group(); //start刚匹配字符串的开始位置,end是结束位置。(位置包括起始位置,不包括结束位置) System.out.println(matcher.start()+"----"+matcher.end()+group); }
输出:
0----2wo 3----5xi 11----13ni 27----29ma
匹配器两个匹配方法的区别:
//字符串的内容是否存在满足规则,可以多次执行,这次开始匹配的位置是上次匹配满足结束的位置 boolean find = matcher.find(); //整个字符串是否满足正则的规则,多次执行,每次从头开始匹配的 boolean flag = matcher.matches(); //共同点:都是是执行后,匹配游标会移动到匹配后的位置。
4. 一个综合实例:判断一个字符串是否是“错过年华,我已白发”,如果不是改正
s="错过年华,我已白发"; //里面有重复有空格有特殊符号. String txt="错过过过 年年华,,,我...已白.发发"; //首先判断是否匹配 System.out.println(txt.matches("错过年华,我已白发")); //去掉重复 String quchongfu = txt.replaceAll("(.)\1+","$1"); System.out.println(quchongfu); //去掉空格 String qukong = quchongfu.replaceAll(" ",""); System.out.println(qukong); //去掉特殊符号点 System.out.println(qukong.replaceAll("\.",""));
//两句可以合并一句:去空格和去点
System.out.println(quchongfu.replaceAll("[\.[ ]]",""));
输出:
false 错过 年华,我.已白.发 错过年华,我.已白.发 错过年华,我已白发
5.关于匹配的范围的一个问题:(当带有数量意义(*,+)的正则)
s="100.001.000.010 1.001.000.010"; //本想只将0开头一段替换掉,如果使用+对于100被破坏成1#(实际下面这种写法并没有开头是0的意思) System.out.println(s.replaceAll("0+(\d+)","#"));//1#.#.#.# 1.#.#.# //本想只将0开头多余0一段去掉,如果使用+对于100被破坏成10(实际下面这种写法并没有开头是0的意思) System.out.println(s.replaceAll("0+(\d+)","$1"));//10.1.0.10 1.1.0.10 a. System.out.println(s.replaceAll("0*(\d+)","#"));//#.#.#.# #.#.#.# b. System.out.println(s.replaceAll("0*(\d+)","$1"));//100.1.0.10 1.1.0.10
从a看出匹配范围是从左开始尽可能大,而不是之前想100,只是匹配00变成1#;然后依次往后
从b看出匹配的正则后面一个规则意义包含有前面规则的时候尽量先满足前面的规则,但并不绝对,000被规则拆分为00和0而不是000(即:后面规则最小满足前面尽量大满足)
6.一个对一组ip地址的字符串进行排序:
常规方式:是拆分ip字符串后,然后再把每个ip拆成4部分然后再进行依依比较(比较麻烦)
利用正则的方式:
1.不能直接用字符的方法比较的原因是两两比较的位置(个位可能和十位,百位比较)不一致。那么如果将每段不足三位的前面加0,那么就可以直接用字符串的自带比较方法来比较了
2.貌似没有补充合适0的办法,那么每个字段都加上00,对于超过3位的,然后刚好都保留3位,去掉多余的0,最后去掉所有的0
s="100.12.0.10 5.1.0.10 120.1.0.10 31.1.0.10 2.1.0.10"; //每一段前面都加上00保证每段最少3个数 String add0 = s.replaceAll("(\d+)","00$1"); //00100.0012.000.0010 005.001.000.0010 00120.001.000.0010 0031.001.000.0010 002.001.000.0010 System.out.println(add0);// //保证每段只有3个数,前面多余的0去掉 String jian0 = add0.replaceAll("0+(\d{3})","$1");// //100.012.000.010 005.001.000.010 120.001.000.010 031.001.000.010 002.001.000.010 System.out.println(jian0); //按一个或多个空格拆分 String[] ips = jian0.split(" +"); Arrays.sort(ips); //利用自然排序,也可以把其加入treeSet中因为树结构是有序的 String sort = Arrays.toString(ips); //[002.001.000.010, 005.001.000.010, 031.001.000.010, 100.012.000.010, 120.001.000.010] System.out.println(sort); //去掉每段中多余的0(只考虑0开头,实际下面这种写法并没有开头是0的意思) //[2.1.0.10, 5.1.0.10, 31.1.0.10, 10.12.0.10, 120.1.0.10] System.out.println(sort.replaceAll("0+(\d+)","$1"));//错误,没有考虑到100的情况被替换成10 //[2.1.0.10, 5.1.0.10, 31.1.0.10, 100.12.0.10, 120.1.0.10] System.out.println(sort.replaceAll("0*(\d+)","$1"));//替换所有的
7.对邮件地址的匹配判断
一般:some_body3@sina.com.cn
一般:some_body3@sina.com
一般:some_body3@163.com
// s="some_body3@sina.com.cn"; s = "some_body3@sina.com"; boolean mail = s.matches("\w+@[a-zA-Z0-9]+(\.[a-zA-Z]+){1,2}");// 比较精确的匹配 boolean mail2 = s.matches("\w+@\w+(\.\w+)+");// 粗略匹配 boolean mail3 = s.contains("@"); boolean mail4 = s.indexOf("@") != -1; System.out.println(mail); System.out.println(mail2); System.out.println(mail3);