由于最近的项目中用到了正则表达式,正好也遇到了一个面试题,题中也有对正则表达式就行考察,所以简单学习一下Regular Expression,然后以一道笔试题的实现来作为练习。
首先可以熟悉一下正则表达式的概念:http://baike.baidu.com/view/94238.htm
常用的正则表达式 | ||
正则表达式 | 匹配 | 举例 |
x | 指定字符x | Java 与 Java 匹配 |
. | 任意单个字符 | Java 与 J..a匹配 |
(ab|cd) | ab或cd | ten与t(en|im)匹配 |
[abc] | a,b或c | Java与Ja[uvwx]a匹配 |
[^abc] | 除a,b或c以外的任何字符 | Java与Ja[^ars]a |
[a-z] | a到z | Java与[A-M]av[a-d]匹配 |
[^a-z] | 除a-z之间的任何字符 | Java与Java[^b-d]匹配 |
[a-e[m-p]] | a到e或m到p | Java与[A-G[I-M]]av[a-d]匹配 |
[a-e&&[c-p]] | a-e与c-p的交 | Java与[A-P&&[I-M]]av[a-d]匹配 |
\d | 一个数字,[1-9] | Java2与"Java[\\d]"匹配 |
\D | 非数字 | $Java与"[\\D][\\D]ava"匹配 |
\w | 词的一个字符 | Java与"[\\w]ava"匹配 |
\W | 非词的一个字符 | $Java与"[\\W][\\w]ava"匹配 |
\s | 一个空白字符 | "Java 2"与"Java\\s2"匹配 |
\S | 一个非空白字符 | |
p* | 模式p的0次或多次出现 | |
p+ | 模式p的1次或多次出现 | |
p? | 模式p的0次或1次出现 | |
p{n} | 模式p的n次精确出现 | |
p{n,} | 模式p的至少n次出现 | |
p{n,m} | 模式p的n到m次(包含m和n)出现 |
下面是某个IT公司的笔试题:
Please write a program to complete the following task (C++/Perl/Python/Shell allowed):
Read several lines of text from STDIN,and
1)filter out all the email addresses from the input text;
2)print a statistic table of how many times each email server is used.
Example:
Input:
CEO of NVIDIA: jenhsun.huang@nvidia.com (the address if fake :)
Some of my favorite writers (they are all fake :)
scott.meyers@brown.edu
herb_sutter@dev.microsoft.com
ericgamma@google.com - known as GoF
jcarmack@idsoftware.com
mark_kilgard@nvidia.com
andrew.rubin@google.com
NVIDIA recruiting team: hr@nvidia.com
Output:
1)
jenhsun.huang@nvidia.com
scott.meyers@brown.edu
herb_sutter@dev.microsoft.com
ericgamma@google.com
jcarmack@idsoftware.com
mark_kilgard@nvidia.com
andrew.rubin@google.com
hr@nvidia.com
2)
nvidia.com: 3
google.com: 2
brown.edu: 1
dev.microsoft.com: 1
idsoftware.com 1
程序流程:
1)将文本文件标准读入。
2)对于输入的字符串判断是不是和正则表达式匹配(Email的正则表达式),如果匹配进入3),否则2)。
3)将匹配的字符串(说明是Email)的"@"字符之后的子串提取出来,然后用map存储子串。
Java中的正则表达式判断:
Java中的正则表达式判断很简单,假设目标串为target,正则表达式模式串为pattern,那么判断二者匹配的方法为:target.match(pattern),如果返回真则表示匹配成功。
C中的正则表达式的判断:
C中的正则表达式的判断则相对复杂,首先要将正则表达式模式串pattern编译成为一个结构体compiled,然后将目标串target与compiled进行匹配。主要的两个函数为:
int regcomp(regex_t *compiled,const char *pattern,int cflags)
功能:将要进行匹配的正则表达式pattern进行编译,做匹配前的准备工作
int regexec(const regex_t *compiled,const char *target,size_t nmatch,regmatch_t pmatch[],int eflags)
功能:用来检测字符串target是否匹配正则表达式compiled
那么,现在就根据那道题目将代码附上(其中regular_expression.txt文件就是题目中input的内容):
Java实现:
package String; import java.io.File; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Scanner; import java.util.Set; import java.util.Map.Entry; public class Regex { private static class ValueComparator implements Comparator<Map.Entry<String,Integer>>{ @Override public int compare(Map.Entry<String,Integer> m,Map.Entry<String,Integer> n) { return n.getValue()-m.getValue(); } } public static void main(String [] args) throws FileNotFoundException{ //6~18个字符,可使用字母、数字、下划线,需以字母开头 String pattern = "^[a-zA-Z][\\d\\w\\.]{1,17}@[\\d\\w\\.]{1,}"; //定义模式串pattern File file = new File("regular_expression.txt"); Scanner input = new Scanner(file); HashMap map = new HashMap<String,Integer>(); int val; while(input.hasNext()){ //依次读入文件中的字符串 String s = input.next(); s.trim(); if(s.matches(pattern)){ //如果是匹配则说明是email System.out.println(s); s = s.substring(s.lastIndexOf('@')+1); //提取"@"字符后面的子串 if(map.get(s)==null){ map.put(s,1); }else{ val = (Integer)map.get(s)+1; map.put(s,val); } } } ArrayList<Map.Entry<String,Integer>> list = new ArrayList<Map.Entry<String,Integer>>(map.entrySet()); Regex.ValueComparator vc = new ValueComparator(); Collections.sort(list,vc); //按照value值进行排序 Iterator listItr = list.iterator(); //迭代输出结果 while(listItr.hasNext()){ Map.Entry<String,Integer> entry = (Map.Entry<String,Integer>) listItr.next(); System.out.println(entry.getKey()+" : "+entry.getValue()); } } }
C/C++实现:
#include<iostream> #include<stdio.h> #include<regex.h> #include<string.h> #include<fstream> #include<map> #include<vector> #include<algorithm> using namespace std; int cflags = REG_EXTENDED,valid,i; regex_t compiled; const char *pattern = "[\\d\\w\\.\\_]*+@+[\\d\\w\\.]*"; //定义模式串pattern char *target = new char[100]; ifstream fin; ofstream fout; map<string,int> map_regex; typedef pair<string,int> PAIR; struct CmpByValue { bool operator()(const PAIR& lhs,const PAIR& rhs) { return lhs.second > rhs.second; } }; void OutputResults() { vector<PAIR> vector_regex(map_regex.begin(),map_regex.end());//将map_regex中的内容转存到vector_regex中去。 sort(vector_regex.begin(),vector_regex.end(),CmpByValue());//根据比较函数进行排序 for(int i=0;i<vector_regex.size();i++) { fout<<vector_regex[i].first<<" : "<<vector_regex[i].second<<endl; } } void RegexMatch(char * target) { regcomp(&compiled,pattern,cflags); valid = regexec(&compiled,target,NULL,0); if(valid == REG_NOMATCH) { //cout<<"no match"<<endl; } else if(valid == 0) { cout<<target<<" matches "<<pattern<<endl; string::size_type pos; string target_str(target); string sub_str; pos = target_str.find("@"); sub_str = target_str.substr(pos+1,target_str.length()-pos-1);//提取子串 //统计子串出现的频率 if(map_regex[sub_str]==0) { map_regex[sub_str]=1; } else { map_regex[sub_str]++; } } regfree(&compiled); } int main(int argc,char ** argv) { fin.open("regular_expression.txt"); if(!fin) { cout<<"open the file failure"<<endl; return 0; } fout.open("result.txt"); while(fin>>target) { RegexMatch(target); } OutputResults(); fin.close(); fout.close(); return 0; }
屏幕打印信息:
jenhsun.huang@nvidia.com scott.meyers@brown.edu herb_sutter@dev.microsoft.com ericgamma@google.com jcarmack@idsoftware.com mark_kilgard@nvidia.com andrew.rubin@google.com hr@nvidia.com nvidia.com : 3 google.com : 2 brown.edu : 1 dev.microsoft.com : 1 idsoftware.com : 1
参考: