数据库笔记14:Java正则表达式

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介:

我们先从简单的开始。假设你要搜索一个包含字符“cat”的字符串,搜索用的正则表达式就是“cat”。如果搜索对大小写不敏感,单词“catalog”、“Catherine”、“sophisticated”都可以匹配。也就是说:

1.1 句点符号
假设你在玩英文拼字游戏, 想要找出三个字母的单词,而且这些单词必须以“t”字母开头,以“n”字母结束。另外,假设有一本英文字典,你可以用正则表达式搜索它的全部内容。要构造 出这个正则表达式,你可以使用一个通配符——句点符号“.”。这样,完整的表达式就是“t.n”,它匹配“tan”、“ten”、“tin”和 “ton”,还匹配“t#n”、“tpn”甚至“t n”,还有其他许多无意义的组合。这是因为句点符号匹配所有字符,包括空格、Tab字符甚至换行符:
1.2 方括号符号
为了解决句点符号匹配范围过于广泛这一问题,你可以在方括号(“[]”)里面指定看来有意义的字符。此时,只有方括号里 面指定的字符才参与匹配。也就是说,正则表达式“t[aeio]n”只匹配“tan”、“Ten”、“tin”和“ton”。但“Toon”不匹配,因为 在方括号之内你只能匹配单个字符:
1.3 “或”符号
如果除了上面匹配的所有单词之外,你还想要匹配“toon”,那么,你可以使用“|”操作符。“|”操作符的基本意义就 是“或”运算。要匹配“toon”,使用“t(a|e|i|o|oo)n”正则表达式。这里不能使用方扩号,因为方括号只允许匹配单个字符;这里必须使用 圆括号“()”。圆括号还可以用来分组,具体请参见后面介绍。
1.4 表示匹配次数的符号
表一显示了表示匹配次数的符号,这些符号用来确定紧靠该符号左边的符号出现的次数:

假设我们要在文本文件中搜索美国的社会安全号码。这个号码的格式是999-99-9999。用来匹配它的正则表达式如图一所示。在正则表达式中,连字符(“-”)有着特殊的意义,它表示一个范围,比如从0到9。因此,匹配社会安全号码中的连字符号时,它的前面要加上一个转义字符“\”。

图一:匹配所有123-12-1234形式的社会安全号码

假设进行搜索的时候,你希望连字符号可以出现,也可以不出现——即,999-99-9999和999999999都属于正确的格式。这时,你可以在连字符号后面加上“?”数量限定符号,如图二所示:

                                                     


图二:匹配所有123-12-1234和123121234形式的社会安全号码

下面我们再来看另外一个例子。美国汽车牌照的一种格式是四个数字加上二个字母。它的正则表达式前面是数字部分“[0-9]{4}”,再加上字母部分“[A-Z]{2}”。图三显示了完整的正则表达式。

图三:匹配典型的美国汽车牌照号码,如8836KV

1.5 “否”符号
“^”符号称为“否”符号。如果用在方括号内,“^”表示不想要匹配的字符。例如,图四的正则表达式匹配所有单词,但以“X”字母开头的单词除外。

图四:匹配所有单词,但“X”开头的除外

1.6 圆括号和空白符号
假设要从格式为“June 26, 1951”的生日日期中提取出月份部分,用来匹配该日期的正则表达式可以如图五所示:

图五:匹配所有Moth DD,YYYY格式的日期

新出现的“\s”符号是空白符号,匹配所有的空白字符,包括Tab字符。如果字符串正确匹配,接下来如何提取出月份部分呢?只需在月份周围加上一个圆括号创建一个组,然后用ORO API(本文后面详细讨论)提取出它的值。修改后的正则表达式如图六所示:

图六:匹配所有Month DD,YYYY格式的日期,定义月份值为第一个组

1.7 其它符号
为简便起见,你可以使用一些为常见正则表达式创建的快捷符号。如表二所示:
表二:常用符号

例如,在前面社会安全号码的例子中,所有出现“[0-9]”的地方我们都可以使用“\d”。修改后的正则表达式如图七所示:

图七:匹配所有123-12-1234格式的社会安全号码

 

 

三、应用实例
下面我们来看看Jakarta-ORO库的一些应用实例。
3.1 日志文件处理
任务:分析一个Web服务器日志文件,确定每一个用户花在网站上的时间。在典型的BEA WebLogic日志文件中,日志记录的格式如下:
分析这个日志记录,可以发现,要从这个日志文件提取的内容有两项:IP地址和页面访问时间。你可以用分组符号(圆括号)从日志记录提取出IP地址和时间标记。
首先我们来看看IP地址。IP地址有4个字节构成,每一个字节的值在0到255之间,各个字节通过一个句点分隔。因此,IP地址中的每一个字节有至少一个、最多三个数字。图八显示了为IP地址编写的正则表达式:

图八:匹配IP地址

IP地址中的句点字符必须进行转义处理(前面加上“\”),因为IP地址中的句点具有它本来的含义,而不是采用正则表达式语法中的特殊含义。句点在正则表达式中的特殊含义本文前面已经介绍。
日志记录的时间部分由一对方括号包围。你可以按照如下思路提取出方括号里面的所有内容:首先搜索起始方括号字符(“[”),提取出所有不超过结束方括号字符(“]”)的内容,向前寻找直至找到结束方括号字符。图九显示了这部分的正则表达式。

图九:匹配至少一个字符,直至找到“]”

现在,把上述两个正则表达式加上分组符号(圆括号)后合并成单个表达式,这样就可以从日志记录提取出IP地址和时间。注意,为了匹配“- -”(但不提取它),正则表达式中间加入了“\s-\s-\s”。完整的正则表达式如图十所示。

图十:匹配IP地址和时间标记

现在正则表达式已经编写完毕,接下来可以编写使用正则表达式库的Java代码了。
为使用java,首先创建正则表达式字符串和待分析的日志记录字符串:
这里使用的正则表达式与图十的正则表达式差不多完全相同,但有一点例外:在Java中,你必须对每一个向前的斜杠 (“\”)进行转义处理。图十不是Java的表示形式,所以我们要在每个“\”前面加上一个“\”以免出现编译错误。遗憾的是,转义处理过程很容易出现错 误,所以应该小心谨慎。你可以首先输入未经转义处理的正则表达式,然后从左到右依次把每一个“\”替换成“\\”。如果要复检,你可以试着把它输出到屏幕 上。

 

 

一个正则表达式,也就是一串有特定意义的字符,必须首先要编译成为一个Pattern类的实例,这个Pattern对象将会使用matcher()方法来生成一个Matcher实例,接着便可以使用该 
Matcher实例以编译的正则表达式为基础对目标字符串进行匹配工作,多个Matcher是可以共用一个Pattern对象的。

 

代码如下:

Java代码  收藏代码

  1. public static void groupString (String rexp,String s) {  

  2.         Pattern p = Pattern.compile(rexp);   

  3.         Matcher m = p.matcher(s);   

  4.         boolean result = m.find();   

  5.         System.out.println("该次查找获得匹配组的数量为:"+m.groupCount());   

  6.             while (result) {  

  7.               

  8.                         System.out.println(m.group(1));  

  9.               

  10.                     System.out.println(m.group(2));  

  11.             result = m.find();   

  12.         }  

  13.               

  14.               

  15.     }  

 

Java代码  收藏代码

  1. public static void main(String[] args) {  

  2. String s = "172.26.22.221 - - [26/Feb/2001:10:56:03 -0500]\"get/isAlive.html Http/1.0\"200 15";  

  3. String rexp="([\\d]{1,3}\\.[\\d]{1,3}\\.[\\d]{1,3}\\.[\\d]{1,3})\\s\\-\\s\\-\\s\\[([^\\]]+)\\]";  

  4. groupString(rexp,s);  

  5.     }  

 

运行结果:

 该次查找获得匹配组的数量为:2
172.26.22.221
26/Feb/2001:10:56:03 -0500

 

 正则表达式的第二用途:查找某个字符串,用指定的字符串替换掉查找到的字符串

Java代码  收藏代码

  1. public static void replace() {  

  2.         //生成Pattern对象并且编译一个简单的正则表达式"Kelvin"   

  3.         Pattern p = Pattern.compile("Kelvin");   

  4. //      用Pattern类的matcher()方法生成一个Matcher对象   

  5.         Matcher m = p.matcher("Kelvin Li and Kelvin Chan are both working in Kelvin Chen's KelvinSoftShop company");          

  6.         StringBuffer sb = new StringBuffer();   

  7.         int i=0;   

  8. //      使用find()方法查找第一个匹配的对象   

  9.         boolean result = m.find();   

  10. //      使用循环将句子里所有的kelvin找出并替换再将内容加到sb里   

  11.         while(result) {   

  12.         i++;   

  13. //      将当前匹配子串替换为指定字符串,并且将替换后的子串以及其之前到上次匹配子串之后的字符串段添加到一个StringBuffer对象里  

  14.         m.appendReplacement(sb, "Kevin");   

  15.         System.out.println("第"+i+"次匹配后sb的内容是:"+sb);   

  16. //      继续查找下一个匹配对象   

  17.         result = m.find();   

  18.         }   

  19. //      最后调用appendTail()方法将最后一次匹配后的剩余字符串加到sb里;   

  20.         m.appendTail(sb);   

  21.         System.out.println("调用m.appendTail(sb)后sb的最终内容是:"+   

  22.         sb.toString());   

  23.   

  24.         }   

 

Java代码  收藏代码

  1. public static void main(String[] args) {  

  2.         public static void main(String[] args) {  

  3.           

  4.         replace();  

  5.     }   }  

 

运行结果:

第1次匹配后sb的内容是:Kevin
第2次匹配后sb的内容是:Kevin Li and Kevin
第3次匹配后sb的内容是:Kevin Li and Kevin Chan are both working in Kevin
第4次匹配后sb的内容是:Kevin Li and Kevin Chan are both working in Kevin Chen's Kevin
调用m.appendTail(sb)后sb的最终内容是:Kevin Li and Kevin Chan are both working in Kevin Chen's KevinSoftShop company

正则表达式的第三用途:判断整个字符串是否匹配某种模式

Java代码  收藏代码

  1. public static void isPattern(String rexp,String s) {  

  2.     Pattern p = Pattern.compile(rexp);  

  3.     Matcher m = p.matcher(s);  

  4.     if (m.matches()) {  

  5.         System.out.println("true");  

  6.     } else {  

  7.         System.out.println("false");  

  8.     }  

  9.       

  10.    }  

 

Java代码  收藏代码

  1. public static void main(String[] args) {  

  2.         isPattern("[A-Za-z]+""44lskdjksdlj");  /* 是否为字母*/  

  3.         isPattern("\\d+""44215"); /* 是否为数字*/  

  4.         isPattern("[^A-Za-z\\d]+""-==1"); /* 是否不是数字和字母*/  

  5.         isPattern("[A-Za-z\\d]+@[^p]+\\.com""joezheng1314@sianp\\+.com"); /* 是否是email地址*/  

  6.         group ("(ca).(t)","one ca,twot cabt int the yard");  

  7.             }  

 运行结果:

false
true
false
false

 正则表达式的第四用途:返回匹配的字符串

例如:一任意的字符串,输出是字母的字符串,并统计各个字母有几个;有两种方法:

第一种

Java代码  收藏代码

  1. public static Map  mat3() {  

  2.         Map map = new HashMap();  

  3.         String s = "sdfsdfdsfaaalllk3222ddddddd";  

  4.         Pattern pattern = Pattern.compile("([a-zA-Z])");  

  5.         Matcher mat= pattern.matcher(s);  

  6.         boolean result = mat.find();  

  7.           

  8.         while (result) {  

  9.          String pat =mat.group(1);  //得到匹配的字符  

  10.         // System.out.println(mat.group(1));  

  11.          if (map.containsKey(pat)) {  

  12.              Integer obj = (Integer)map.get(pat);  

  13.              int num = obj.intValue();  

  14.              map.put(pat, new Integer(++num));             

  15.          } else {  

  16.              map.put(pat, new Integer(1));  

  17.          }  

  18.            

  19.          result = mat.find();  

  20.         }  

  21.         return map;  

  22.     }  

 

 第二种方法:不统计各个字母个数了,参照第一种

mat.start(); 返回匹配的字符串的初始索引

mat.end();   返回最后匹配字符之后的偏移量。

s.substring(start, end))  返回匹配的字符串

Java代码  收藏代码

  1. public static void mat2() {  

  2.         String s = "sdfsdfdsf3222";  

  3.         Pattern pattern = Pattern.compile("[a-zA-Z]");  

  4.         Matcher mat= pattern.matcher(s);  

  5.         boolean result = mat.find();  

  6.         while (result) {  

  7.             int start = mat.start();  

  8.             int end = mat.end();     

  9.             System.out.println("start="+start+"end="+end+" "+s.substring(start, end));  

  10.             result = mat.find();  

  11.         }  

本文转自    风雨萧条 博客,原文链接:   http://blog.51cto.com/1095221645/1429647      如需转载请自行联系原作者

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
4天前
|
XML Java 数据库连接
性能提升秘籍:如何高效使用Java连接池管理数据库连接
在Java应用中,数据库连接管理至关重要。随着访问量增加,频繁创建和关闭连接会影响性能。为此,Java连接池技术应运而生,如HikariCP。本文通过代码示例介绍如何引入HikariCP依赖、配置连接池参数及使用连接池高效管理数据库连接,提升系统性能。
27 5
|
5天前
|
Java
探索Java中的Lambda表达式
【10月更文挑战第37天】本文将带你深入理解Java的Lambda表达式,从基础语法到高级特性,通过实例讲解其在函数式编程中的应用。我们还将探讨Lambda表达式如何简化代码、提高开发效率,并讨论其在实际项目中的应用。
|
8天前
|
Java API
Java中的Lambda表达式与函数式编程####
【10月更文挑战第29天】 本文将深入探讨Java中Lambda表达式的实现及其在函数式编程中的应用。通过对比传统方法,我们将揭示Lambda如何简化代码、提高可读性和维护性。文章还将展示一些实际案例,帮助读者更好地理解和应用Lambda表达式。 ####
|
8天前
|
JSON 自然语言处理 Java
这款轻量级 Java 表达式引擎,真不错!
AviatorScript 是一个高性能、轻量级的脚本语言,基于 JVM(包括 Android 平台)。它支持数字、字符串、正则表达式、布尔值等基本类型,以及所有 Java 运算符。主要特性包括函数式编程、大整数和高精度运算、完整的脚本语法、丰富的内置函数和自定义函数支持。适用于规则判断、公式计算、动态脚本控制等场景。
|
12天前
|
Java API 开发者
Java中的Lambda表达式与函数式编程####
在Java的演变过程中,Lambda表达式和函数式编程的引入无疑是一次重大的飞跃。本文将深入探讨Lambda表达式的定义、用法及优势,并结合实例说明如何在Java中利用Lambda表达式进行函数式编程。通过对比传统编程方式,揭示Lambda表达式如何简化代码、提高开发效率和可维护性。 ####
|
14天前
|
安全 Java 编译器
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
|
14天前
|
Java 开发工具 Android开发
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
|
14天前
|
Java 编译器 Android开发
Kotlin教程笔记(28) -Kotlin 与 Java 混编
Kotlin教程笔记(28) -Kotlin 与 Java 混编
|
16天前
|
SQL Java 数据库连接
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率。本文介绍了连接池的工作原理、优势及实现方法,并提供了HikariCP的示例代码。
30 3
|
16天前
|
存储 Java 关系型数据库
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接创建、分配、复用和释放等操作,并通过电商应用实例展示了如何选择合适的连接池库(如HikariCP)和配置参数,实现高效、稳定的数据库连接管理。
33 2