字符串匹配—正则表达式
正则表达式(Regular Expression)是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符")。
正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。
正则表达式是繁琐的,但它是强大的,学会之后的应用会让你除了提高效率外,会给你带来绝对的成就感。只要认真阅读本教程,加上应用的时候进行一定的参考,掌握正则表达式不是问题。
许多程序设计语言都支持利用正则表达式进行字符串操作。
正则表达式的使用,可以通过简单的办法来实现强大的功能。下面先给出一个简单的示例:
^ 为匹配输入字符串的开始位置。
[0-9]+匹配多个数字, [0-9] 匹配单个数字,+ 匹配一个或者多个。
匹配字母 abc 并以 abc 结尾,$ 为匹配输入字符串的结束位置。
比如我们在写用户注册表单时,用户名只允许包含字符、数字、下划线和连接字符(-),并设置用户名的长度,我们就可以使用以下正则表达式来设定。
以上的正则表达式可以匹配 junge、junge1、jun_ge、jun-ge, 但不匹配 ge,因为它包含的字母太短了,小于 3 个无法匹配。也不匹配 junge@, 因为它包含特殊字符。
一个字符串其实就是一个简单的正则表达式,例如 Hello World 正则表达式匹配 "Hello World" 字符串。
.(点号)也是一个正则表达式,它匹配任何一个字符如:"a" 或 "1"。
下表列出了一些正则表达式的实例及描述:
java.util.regex 包主要包括以下三个类:
- Pattern 类:
pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共构造方法。要创建一个 Pattern 对象,你必须首先调用其公共静态编译方法,它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数。
- Matcher 类:
Matcher 对象是对输入字符串进行解释和匹配操作的引擎。与Pattern 类一样,Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。
- PatternSyntaxException:
PatternSyntaxException 是一个非强制异常类,它表示一个正则表达式模式中的语法错误。
实例1:
import java.util.regex.*; public class RegexExample1 { public static void main(String args[]) { String content = "Life is short,I use Java"; String pattern = ".*short.*"; boolean isMatch = Pattern.matches(pattern, content); System.out.println("字符串中是否包含了 'short' 子字符串? " + isMatch); } }
实例输出结果为:
字符串中是否包含了 'short' 子字符串? true
捕获组
捕获组是把多个字符当一个单独单元进行处理的方法,它通过对括号内的字符分组来创建。
例如,正则表达式 (dog) 创建了单一分组,组里包含"d","o",和"g"。
捕获组是通过从左至右计算其开括号来编号。例如,在表达式((A)(B(C))),有四个这样的组:
((A)(B(C)))
(A)
(B(C))
(C)
可以通过调用 matcher 对象的 groupCount 方法来查看表达式有多少个分组。groupCount 方法返回一个 int 值,表示matcher对象当前有多个捕获组。
还有一个特殊的组(group(0)),它总是代表整个表达式。该组不包括在 groupCount 的返回值中。
实例2:
下面的例子说明如何从一个给定的字符串中找到数字串:import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexMatches { public static void main(String args[]) { // 按指定模式在字符串查找 String line = "This order was placed for QT3000! OK?"; String pattern = "(\\D*)(\\d+)(.*)"; // 创建 Pattern 对象 Pattern r = Pattern.compile(pattern); // 现在创建 matcher 对象 Matcher m = r.matcher(line); if (m.find()) { System.out.println("Found value: " + m.group(0)); System.out.println("Found value: " + m.group(1)); System.out.println("Found value: " + m.group(2)); System.out.println("Found value: " + m.group(3)); } else { System.out.println("NO MATCH"); } } }
以上实例编译运行结果如下:
Found value: This order was placed for QT3000! OK? Found value: This order was placed for QT Found value: 3000 Found value: ! OK?
正则表达式语法
在其他语言中,\\表示:我想要在正则表达式中插入一个普通的(字面上的)反斜杠,请不要给它任何特殊的意义。
在 Java 中,\\ 表示:我要插入一个正则表达式的反斜线,所以其后的字符具有特殊的意义。
所以,在其他的语言中(如Perl),一个反斜杠 \ 就足以具有转义的作用,而在 Java 中正则表达式中则需要有两个反斜杠才能被解析为其他语言中的转义作用。也可以简单的理解在 Java 的正则表达式中,两个 \\代表其他语言中的一个 \,这也就是为什么表示一位数字的正则表达式是 \\d,而表示一个普通的反斜杠是 \\\\。
Matcher 类的方法
索引方法
索引方法提供了有用的索引值,精确表明输入字符串中在哪能找到匹配:
研究方法
研究方法用来检查输入字符串并返回一个布尔值,表示是否找到该模式:
替换方法
替换方法是替换输入字符串里文本的方法:
start 和 end 方法
下面是一个对单词 "cat" 出现在输入字符串中出现次数进行计数的例子:
实例3:
import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexMatches { public static void main(String args[]) { String REGEX = "\\bcat\\b"; String INPUT = "cat cat cat cattie cat"; Pattern p = Pattern.compile(REGEX); Matcher m = p.matcher(INPUT); // 获取 matcher 对象 int count = 0; while (m.find()) { count++; System.out.println("Match number " + count); System.out.println("start(): " + m.start()); System.out.println("end(): " + m.end()); } } }
以上实例编译运行结果如下:
Match number 1 start(): 0 end(): 3 Match number 2 start(): 4 end(): 7 Match number 3 start(): 8 end(): 11 Match number 4 start(): 19 end(): 22
可以看到这个例子是使用单词边界,以确保字母 "c" "a" "t" 并非仅是一个较长的词的子串。它也提供了一些关于输入字符串中匹配发生位置的有用信息。
Start 方法返回在以前的匹配操作期间,由给定组所捕获的子序列的初始索引,end 方法最后一个匹配字符的索引加 1。
matches 和 lookingAt 方法
matches 和 lookingAt 方法都用来尝试匹配一个输入序列模式。它们的不同是 matches 要求整个序列都匹配,而lookingAt 不要求。
lookingAt 方法虽然不需要整句都匹配,但是需要从第一个字符开始匹配。
这两个方法经常在输入字符串的开始使用。
我们通过下面这个例子,来解释这个功能:
实例4:
import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexMatches { public static void main(String args[]) { String REGEX = "foo"; String INPUT = "fooooooooooooooooo"; String INPUT2 = "ooooofoooooooooooo"; Pattern pattern; Matcher matcher; Matcher matcher2; pattern = Pattern.compile(REGEX); matcher = pattern.matcher(INPUT); matcher2 = pattern.matcher(INPUT2); System.out.println("Current REGEX is: " + REGEX); System.out.println("Current INPUT is: " + INPUT); System.out.println("Current INPUT2 is: " + INPUT2); System.out.println("lookingAt(): " + matcher.lookingAt()); System.out.println("matches(): " + matcher.matches()); System.out.println("lookingAt(): " + matcher2.lookingAt()); } }
以上实例编译运行结果如下:
Current REGEX is: foo Current INPUT is: fooooooooooooooooo Current INPUT2 is: ooooofoooooooooooo lookingAt(): true matches(): false lookingAt(): false
replaceFirst 和 replaceAll 方法
replaceFirst *和 *replaceAll 方法用来替换匹配正则表达式的文本。不同的是,replaceFirst 替换首次匹配,replaceAll 替换所有匹配。
下面的例子来解释这个功能:
实例5:
import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexMatches{ public static void main(String[] args) { String REGEX = "dog"; String INPUT = "The dog says meow,All dogs say meow."; String REPLACE = "cat"; Pattern p = Pattern.compile(REGEX); // get a matcher object Matcher m = p.matcher(INPUT); INPUT = m.replaceAll(REPLACE); System.out.println(INPUT); } }
以上实例编译运行结果如下:
The cat says meow. All cats say meow.
appendReplacement 和 appendTail 方法
Matcher 类也提供了appendReplacement 和 appendTail 方法用于文本替换:
看下面的例子来解释这个功能:
实例6:
import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexMatches { public static void main(String[] args) { String REGEX = "a*b"; String INPUT = "aabfooaabfooabfoobkkk"; String REPLACE = "-"; Pattern p = Pattern.compile(REGEX); // 获取 matcher 对象 Matcher m = p.matcher(INPUT); StringBuffer sb = new StringBuffer(); while (m.find()) { m.appendReplacement(sb, REPLACE); } //当遇到a*b格式子串,将其换成- m.appendTail(sb); System.out.println(sb.toString()); } }
以上实例编译运行结果如下:
-foo-foo-foo-kkk
PatternSyntaxException 类的方法
PatternSyntaxException 是一个非强制异常类,它指示一个正则表达式模式中的语法错误。
PatternSyntaxException 类提供了下面的方法来帮助我们查看发生了什么错误。
- 注意:正则表达式写好后,没有错对之分,返回结果只是true和false
示例7:
校验QQ号,要求:必须是5~10位数字,0不能开头。
import java.util.Scanner; public class RegexMatches { public static void main(String[] args) { Scanner sc = new Scanner(System.in); String qq = sc.next(); boolean bool = checkQQ(qq); System.out.println(bool == true ? "合法QQ号码" : "非法QQ号码"); } public static boolean checkQQ(String qq) { String reg = "[1-9][0-9]{4,9}"; return qq.matches(reg); } }
以上实例编译运行结果如下:
012345678 非法QQ号码 1647342291 合法QQ号码 16473422911 非法QQ号码 1234 非法QQ号码
示例8:
小明同学最近开发了一个网站,在用户注册账户的时候,需要设置账户的密码,为了加强账户的安全性,小明对密码强度有一定要求:
密码只能由大写字母,小写字母,数字构成;
密码不能以数字开头;
密码中至少出现大写字母,小写字母和数字这三种字符类型中的两种;
密码长度至少为8
现在小明受到了n个密码,他想请你写程序判断这些密码中哪些是合适的,哪些是不合法的。import java.util.Scanner; public class Main { public static String PanDuan(String str) { String regex = "[a-zA-Z]([a-zA-Z0-9]+)"; String regexB = "[a-z]*"; String regexC = "[A-Z]*"; String Result = null; if (!str.matches(regex)) { Result = "NO"; } else if (!(str.length() >= 8 && str.length() <= 100)) { Result = "NO"; } else if (((str.matches(regexB)) || (str.matches(regexC)))) { Result = "NO"; } else { Result = "YES"; } return Result; } public static void main(String[] args) { Scanner s = new Scanner(System.in); int n = s.nextInt(); String[] k = new String[n]; for (int i = 0; i < n; i++) { k[i] = s.next(); } for (int i = 0; i < n; i++) { System.out.println(PanDuan(k[i])); } } }
以上实例编译运行结果如下:
2 CdKfIfsiBgohWsydFYlMVRrGUpMAL 25sijudhTvhusk YES NO
下面列出一些正则表达式示例:
常用正则表达式
一、校验数字的表达式
二、校验字符的表达式
三、特殊需求表达式