在这里记录一下关于软件构造课程Lab3中关于正则表达式的应用。
在实验内容中,要求用正则表达式来匹配读入文件的内容,从而取得构建图需要的相关信息。
举个例子,读入的文件(GraphPoetTestFile.txt)内容如下:
先说一下文件内容中的数据格式:
①一定有一行,第一个单词为"GraphType",后面是一个空格、一个'='、一个空格,加上一个"类型名"
②一定有一行,第一个单词为"GraphName",后面是一个空格、一个'='、一个空格,加上一个"图的名字"
③一定有一行,第一个单词为"VertexType"/"EdgeType",后面是一个空格、一个'='、一个空格,加上若干个"顶点类型",不同顶点类型之间用','和空格隔开
④一定有若干行,第一个单词为"Vertex",后面是一个空格,一个'='、一个空格、一个左尖括号'<',加上若干个"数据信息",最后用右尖括号'>'结束(中间可能仍有尖括号来界定属性信息)
⑤一定有若干行,第一个单词为"Edge",后面是一个空格,一个'='、一个空格、一个左尖括号'<',加上若干个"数据信息",信息项之间用','和空格隔开,最后用右尖括号'>'结束
⑥可能存在若干行,第一个单词为"HyperEdge",后面是一个空格,一个'='、一个空格、一个左尖括号'<',加上若干个"数据信息",信息项之间用','和空格隔开,包含一组大括号'{''}'来界定的属性信息,最后用右尖括号'>'结束
下面说一下对于这个问题而言,我的解决思路:
首先,分析读入文件的格式,每一行读入引号中的数据项个数可能不一致,有的行只包含一项,有的行却包含多项,因此没有一种完美的正则表达式能直接全部读取所有引号的数据项。同时,考虑到还有尖括号'<>'以及大括号'{}'来界定的情况,这种直接构建完整正则表达式进行匹配的方式似乎不存在或者说使用情况比较复杂。
先说明两种基本的匹配字符串的方式,一种是最大化地进行匹配字符串,一种是最小化地进行匹配字符串。
比如,对于一个字符串/Introduction = "I", "Am", "Steven", "Shen"/(这里用'/'来表示字符串界定符以避免产生歧义),最大化地进行匹配的话,我要得到的就是引号区间界定的整个字符串匹配,即/"I", "Am", "Steven", "Shen"/;最小化地进行匹配的话,我要得到的就是引号区间界定的一小部分字符串匹配,即/I/,再次重复进行匹配就会依次得到/Am/、/Steven/、/Shen/。这两种正则表达式在Java中的具体字符串表示格式就是max = "(".+")",min = ""([^"]+)""。
下面回到问题上来,我的解决思路有两种:
第一种方式,通过max和min的使用,细致化地获取数据
对每一行先进行这样的匹配String pattern1 = "(\w+) = (".+")"; 即每行先是一个单词,一个空格,一个'=',一个空格,后加一个max型的匹配,得到'='后所有信息项。匹配上面的那个文件,第一行匹配结果就是/GraphType/、/"MovieGraph"/,第三行匹配结果就是/VertexType/、/"Movie", "Actor", "Director"/。此时,我们判断第一个读入的单词的内容,即可确定该如何处理'='后面的信息项。此时再对第二个获取的串(通过max匹配得到的串)进行最小化地匹配,第一行就得到了/MovieGraph/,第三行就得到了/Movie/、/Actor/、/Director/三个项。类似地,把界定范围改成尖括号内的内容,同样也可以获取Vertex以及Edge的信息。
这种解决方式总的思路就是通过匹配每行第一个单词来确定如何处理这行数据'='后的数据项。然后通过最大化地匹配'='后的数据,进一步对其用最小化匹配进行划分,得到具体的数据项。
第二种方式,通过max的使用,结合对字符串的操作达成目标
类似于上面说的,得到每行第一个单词后,用最大化匹配来得到'='后的数据项,此时匹配的内容就是/"I", "Am", "Steven", "Shen"/,然后对这个字符串使用String的split方法(当然,split的参数列表实际上接收的就是一个正则表达式的字符串)。这里的split方法就是这样split(",\s")。这样,/"I", "Am", "Steven", "Shen"/就变成了/"I"/、/"Am"/、/"Steven"/、/"Shen"/,他们存储在一个String数组中,这时还要数组中每个串求子串,从而去掉每个串两端的引号(")。即通过String的subString方法,得到/I/、/Am/、/Steven/、/Shen/。
这种解决方式总的思路就是先获取要读取的数据块,在对数据块用String的操作来完成每个需要读取的数据项。这种方式相比于前面那种比较麻烦,原因在于求子串、分割过程比较繁琐,不如上面那种方法的用最小化匹配方便快捷。
这两种方法的正则表达式匹配串并获取,都用到了group方法,这些内容在Java的String和正则表达式部分都有详细介绍,这里就不再赘述了。
至于文件中可能存在的匹配不同界定符的情况,可以类似地考虑如何实现。主要思路就在上面,具体实现时可以通过每行数据项的个数等确定性信息来进行信息读取,使得处理更灵活。
下面附上针对这种MovieGraph的处理代码。
1 package factory; 2 3 import java.util.*; 4 import java.util.regex.*; 5 import java.io.*; 6 import vertex.*; 7 import edge.*; 8 import graph.*; 9 10 public class MovieGraphFactory { 11 public static MovieGraph createGraph(String filePath) { 12 String GraphType = ""; 13 String GraphName = ""; 14 String VertexType = ""; 15 List<String> VertexTypes = new ArrayList<>(); 16 String EdgeType = ""; 17 List<String> EdgeTypes = new ArrayList<>(); 18 Set<String> fullVertexInfo = new HashSet<>(); 19 Set<String> fullEdgeInfo = new HashSet<>(); 20 Set<String> fullHyperEdgeInfo = new HashSet<>(); 21 List<Vertex> vertexList = new ArrayList<>(); 22 List<Edge> edgeList = new ArrayList<>(); 23 List<Edge> hyperList = new ArrayList<>(); 24 String pattern1 = "(\w+) = (".+")"; 25 String pattern2 = "(\w+) = <(.+)>"; 26 String pattern3 = ",? ?"([^"]+)""; 27 Pattern p = Pattern.compile(pattern1); 28 Matcher parse1 = p.matcher(""); 29 p = Pattern.compile(pattern2); 30 Matcher parse2 = p.matcher(""); 31 MovieGraph graph = null; 32 try { 33 File file = new File(filePath); 34 InputStreamReader readin = new InputStreamReader(new FileInputStream(file)); 35 BufferedReader bufReadin = new BufferedReader(readin); 36 String line; 37 int i = 1; 38 while((line = bufReadin.readLine()) != null) { 39 parse1.reset(line); 40 parse2.reset(line); 41 if(parse1.find()) { 42 if(parse1.groupCount() == 2) { 43 if(parse1.group(1).equals("GraphType")) { 44 GraphType = parse1.group(2); 45 if(!GraphType.equals(""MovieGraph"")) { 46 System.out.println("Error! the graph must be MovieGraph rather be " + GraphType); 47 bufReadin.close(); 48 throw new RuntimeException(); 49 } 50 } 51 if(parse1.group(1).equals("GraphName")) 52 GraphName = parse1.group(2); 53 if(parse1.group(1).equals("VertexType")) 54 VertexType = parse1.group(2); 55 if(parse1.group(1).equals("EdgeType")) 56 EdgeType = parse1.group(2); 57 } 58 } else if(parse2.find()) { 59 if(parse2.groupCount() == 2) { 60 if(parse2.group(1).equals("Vertex")) 61 fullVertexInfo.add(parse2.group(2)); 62 if(parse2.group(1).equals("Edge")) 63 fullEdgeInfo.add(parse2.group(2)); 64 if(parse2.group(1).equals("HyperEdge")) 65 fullHyperEdgeInfo.add(parse2.group(2)); 66 } 67 } 68 } 69 p = Pattern.compile(pattern3); 70 Matcher parse3 = p.matcher(GraphType); 71 if(parse3.find()) 72 GraphType = parse3.group(1); 73 parse3 = p.matcher(GraphName); 74 if(parse3.find()) 75 GraphName = parse3.group(1); 76 parse3 = p.matcher(VertexType); 77 while(parse3.find()) { 78 VertexTypes.add(parse3.group(1)); 79 } 80 parse3 = p.matcher(EdgeType); 81 while(parse3.find()) { 82 EdgeTypes.add(parse3.group(1)); 83 } 84 85 String label = "", type = ""; 86 String[] fields = null; 87 88 for(String temp : fullVertexInfo) { 89 i = 0; 90 Vertex point = null; 91 parse3 = p.matcher(temp); 92 if(parse3.find()) 93 label = parse3.group(1); 94 if(parse3.find()) { 95 type = parse3.group(1); 96 } 97 switch(type) { 98 case "Actor" : 99 fields = new String[2]; 100 break; 101 case "Director" : 102 fields = new String[2]; 103 break; 104 case "Movie" : 105 fields = new String[3]; 106 break; 107 default : 108 System.out.println("Error. The graph can't contain other vertex " + type); 109 bufReadin.close(); 110 throw new RuntimeException(); 111 } 112 while(parse3.find()) { 113 fields[i++] = parse3.group(1); 114 } 115 switch(type) { 116 case "Actor" : 117 point = ActorVertexFactory.createVertex(label, fields); 118 break; 119 case "Director" : 120 point = DirectorVertexFactory.createVertex(label, fields); 121 break; 122 case "Movie" : 123 point = MovieVertexFactory.createVertex(label, fields); 124 break; 125 default : 126 break; 127 } 128 vertexList.add(point); 129 } 130 131 for(String temp : fullEdgeInfo) { 132 parse3 = p.matcher(temp); 133 Double weight = 0.0; 134 Vertex point1 = null, point2 = null; 135 Edge edge = null; 136 if(parse3.find()) 137 label = parse3.group(1); 138 if(parse3.find()) 139 type = parse3.group(1); 140 if(parse3.find()) 141 weight = Double.parseDouble(parse3.group(1)); 142 if(parse3.find()) 143 for(Vertex start : vertexList) { 144 if(parse3.group(1).equals(start.getLabel())) { 145 point1 = start; 146 break; 147 } 148 } 149 if(parse3.find()) 150 for(Vertex end : vertexList) { 151 if(parse3.group(1).equals(end.getLabel())) { 152 point2 = end; 153 break; 154 } 155 } 156 if(parse3.find()) 157 if(!parse3.group(1).equals("No")) { 158 bufReadin.close(); 159 throw new RuntimeException(); 160 } 161 List<Vertex> arg = new ArrayList<>(); 162 arg.add(point1); arg.add(point2); 163 switch(type) { 164 case "MovieActorRelation" : 165 edge = MovieActorRelationFactory.createEdge(label, arg, weight); 166 break; 167 case "MovieDirectorRelation" : 168 edge = MovieDirectorRelationFactory.createEdge(label, arg, -1.0); 169 break; 170 default : 171 System.out.println("Error, the graph can't contain other edge " + type); 172 bufReadin.close(); 173 throw new RuntimeException(); 174 } 175 edgeList.add(edge); 176 } 177 178 for(String temp : fullHyperEdgeInfo) { 179 parse3 = p.matcher(temp); 180 Edge hyper = null; 181 if(parse3.find()) { 182 label = parse3.group(1); 183 } 184 if(parse3.find()) 185 type = parse3.group(1); 186 List<Vertex> hyperVertices = new ArrayList<>(); 187 while(parse3.find()) { 188 for(Vertex temppoint : vertexList) { 189 if(parse3.group(1).equals(temppoint.getLabel())) { 190 hyperVertices.add(temppoint); 191 break; 192 } 193 } 194 } 195 hyper = SameMovieHyperEdgeFactory.createEdge(label, hyperVertices, -1.0); 196 hyperList.add(hyper); 197 } 198 199 graph = new MovieGraph(GraphName); 200 for(Vertex temp : vertexList) { 201 graph.addVertex(temp); 202 } 203 for(Edge temp : edgeList) { 204 graph.addEdge(temp); 205 } 206 for(Edge temp : hyperList) { 207 graph.addEdge(temp); 208 } 209 210 System.out.println("***** vertexList's length : " + vertexList.size()); 211 System.out.println("***** edgeList's length : " + edgeList.size()); 212 System.out.println("***** hyperList's length : " + hyperList.size()); 213 System.out.println("Vertex:" + fullVertexInfo.size()+ " " + fullVertexInfo + " " + "Edge:" + fullEdgeInfo.size() + " " + fullEdgeInfo + " " + "HyperEdge:" + fullHyperEdgeInfo.size() + " " + fullHyperEdgeInfo); 214 bufReadin.close(); 215 } catch(Exception e) { 216 e.printStackTrace(); 217 } 218 return graph; 219 } 220 } 221 222 MovieGraphFactory.java