在工作中由于自己的任务分配经常处理正则表达式的问题,不过如果要我正经写一个文章可能比较困难,这篇博文就当作日常积累吧。
(2010.03.26)占有优先量词的使用:
想要取出文档中所有以#开头,ascii字符为内容,空格或者{为结束的字符串,比如对"#loading {"就是取出loading,方法当然很容易,用java写的匹配Pattern就是
Pattern p = Pattern.compile("#(\\w+)[\\s{]?");
因为考虑不周,发现这个会把"#health:focus"中的health也取出,但我不想让这种类型匹配成功,所以决定对所有后面有":"的不予匹配,先试着用了否定正向环视
Pattern p = Pattern.compile("#(\\w+)(?!:)[\\s{]?");
但由于即使+是贪婪的,可还是以整个正则的的匹配成功为前提,所以导致会取出"headth:"的"healt"部分,仍然匹配成功。我想了一下,如果不怕麻烦,可以把(\\w+)后的字符取出来判断是否为":",然则这就需要两步了,用perl的话语法很方便倒还可以,但java的Pattern方法不太合适。
翻了翻精通正则表达式,正好找到我这里需要的即Possessive quantifiers,一旦占有就不会交出已匹配字符,即使这样会导致整个正则的匹配失败,那么就很简单了
Pattern p = Pattern.compile("#(\\w++)(?!:)[\\s{]?");
(2010.04.02)高级查找替换对比原地查找替换
在正则的日常运用中,不接触替换几乎是不可能的,一般来说,仅使用String方法的replaceAll就可以满足大部分需求,但如果想要对替换的部分进行处理(可能只是简单的大小写替换)就非常困难,必须要使用Matcher的一些方法,对比一下:
1) 对字符串的数字(仅整形)前后加上括号,这个只要replaceAll就可以
String s= "this is a number 123 and 456";
s = s.replaceAll("\\d+", "\"$0\"");
2) 将字符串内的大写转为小写,由于无法对$n进行操作,所以必须借助Matcher
StringBuffer sb = new StringBuffer("there are DAXIE letters XIXI");
Pattern p = Pattern.compile("[A-Z]");
Matcher m = p.matcher(sb);
while(m.find())
{
sb.replace(m.start(), m.end(), m.group().toLowerCase());
}
3) 对字符串里的浮点数字进行四舍五入,只考虑小数点两遍都有数字的标准形式,因为存在变长,所以用replace的时候就必须重设Matcher的偏移,这里不仅有去掉分数部分的差异还有可能进位的差异
StringBuffer sb = new StringBuffer("this is a number 123.456 and 456.789 or 999.999");
Pattern p = Pattern.compile("(\\d+)\\.(\\d+)");
Matcher m = p.matcher(sb);
int index = 0;
while(m.find(index))
{
String integer = m.group(1);
String fraction = m.group(2);
int i1 = Integer.parseInt(integer);
int i2 = Integer.parseInt(fraction.substring(0, 1));
if (i2 >= 5)
{
i1++;
}
String replace = String.valueOf(i1);
index = m.end() - (m.group().length() - replace.length());
sb.replace(m.start(), m.end(), replace);
}
请注意最后index的赋值以及次序,有些微妙的错误可能会发生
4) 需求同上,不过使用了Matcher的高级替换方法,相应就明了了很多,最重要的是不用在意那该死的偏移了
StringBuffer sb = new StringBuffer();
Pattern p = Pattern.compile("(\\d+)\\.(\\d+)");
String s = "this is a number 123.456 and 456.789 or 999.999";
Matcher m = p.matcher(s);
while(m.find())
{
String integer = m.group(1);
String fraction = m.group(2);
int i1 = Integer.parseInt(integer);
int i2 = Integer.parseInt(fraction.substring(0, 1));
if (i2 >= 5)
{
i1++;
}
m.appendReplacement(sb, String.valueOf(i1));
}
m.appendTail(sb);
(2010.4.30)Java正则的逆向环视之殇:
需求仍然是相当明确,为了要取出html内嵌js代码段内容,我们使用了类似<scirpt [^>]*>(.*?)</script)的正则,当然这里是极度简化了的,如果是按照html规范来的话,只要用户写的格式正确,那么是可以取出内容的,可用户会写不合规范的自闭合script标签,比如这种情况
<script type="text/javascript" src="script/CodeAssist.js" />
<script type="text/javascript">
var i = 100;
</script>
那么取出来的就成了
<script type="text/javascript">
var i = 100;
处理方法也不复杂,只要script开始标签的那个闭合>左边不是/就可以了,但实际上/到>间是可以随意写空格的,而java的逆向环视不能是不定数目的元素,一般来说折中处理方法是用比如{0, 100}来模拟不定数目,谁也不会吃饱了撑的写一百个以上的空格吧,但这毕竟在理论上就不对了。
(2010.6.21)Xcode项目中使用正泽表达式
首先下载正则包
RegexKitLite,解压后只需要将其中的RegexKitLite的m和h文件放入项目,再在项目的链接选项Other Linker Flags里添加-licucore即可。用起来方法也差不多,比如验证一个字符串是否为仅有空字符组成:
+ (BOOL)isBlankString:(NSString*)string
{
return [string isMatchedByRegex:@"^\\s*$"];
}
当然也有微妙的地方啦,比如这里的Match方法就必须要加上头和尾的标记,否则只相当于Java正则里面的find方法,其他就自己揣摩吧。