Victor,你让我参考你的答案。所以我决定花时间写一份完整的审查并给出一些提示。我不是某种正则表达式专业人士,也不是很喜欢它。目前,我正在一个大量使用正则表达式的项目上工作,因此我已经看到并编写了足够多的内容来相当可靠地回答你的问题,并对正则表达式感到厌烦。
因此,让我们开始分析你的正则表达式:
String regex ="\\s*public\\s*static.*getFieldsConfig\\(.*?\\)\\s*\\{.*\\}(?=\\s*(public|private|protected|static))";
String regex2 = "\\s*public\\s*static.*getFieldsConfig\\(.*?\\)\\s*\\{.*\\}(?=(\\s*}\\s*$))";
regex = "(" + regex +")|("+ regex2 + "){1}?";
我看到你将内容分为三部分以便阅读。这是个好主意。我将从第一部分开始:
\\s\*public\\s\*static.*getFieldsConfig
你允许在public
和static
之间包括零个或多个空格。每次在必须用一些空格分隔的单词之间使用\\s+
。
(.\*?\\)\\s\*\\{.\*\\}
你允许在第一个括号中出现任何字符,直到匹配到)
为止。现在我们来到了使你的正则表达式无法按照你想要的方式工作的部分。 \\{.*\\}
是一个重大错误。它将匹配直到文件中最后一个 }
之前的所有内容,在到达任意一个public
、private
、protected
或static
之前。我将你的getFieldsConfig
方法粘贴到Java文件中并进行了测试。仅使用你的正则表达式的第一部分("\\s*public\\s*static.*getFieldsConfig\\(.*?\\)\\s*\\{.*\\}(?=\\s*(public|private|protected|static))"
),匹配到了从你的方法一直到文件中的最后一个方法的所有内容。
没有必要逐步分析其他部分,因为\\{.*\\}
会破坏一切。在第二部分(regex2
)中,您已将任何内容与文件中的最后一个}
匹配。您尝试过打印正则表达式匹配的内容吗?试试:
package com.tryRegex;
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TryRegex{
public static void main(String[] args) throws IOException{
File yourFile = new File("tryFile.java");
Scanner scanner = new Scanner(yourFile, "UTF-8");
String text = scanner.useDelimiter("\\A").next();
String regex ="\\s*public\\s*static.*getFieldsConfig\\(.*?\\)\\s*\\{.*\\}(?=\\s*(public|private|protected|static))";
String regex2 = "\\s*public\\s*static.*getFieldsConfig\\(.*?\\)\\s*\\{.*\\}(?=(\\s*}\\s*$))";
regex = "(?s)(" + regex +")|("+ regex2 + "){1}?";
Matcher m = Pattern.compile(regex).matcher(text);
System.out.println(m.find() ? m.group() : "No Match found");
}
}
一个简短而简单的代码片段,展示正则表达式的工作原理。如果需要,可以处理异常。只需将yourFile.java
放入项目文件夹并运行即可。
现在我将展示正则表达式有多混乱:
String methodSignature = "(\\s*((public|private|protected|static|final|abstract|synchronized|volatile)\\s+)*[\\w<>\\[\\]\\.]+\\s+\\w+\\s*\\((\\s*[\\w<>\\[\\]\\.]*\\s+\\w+\\s*,?)*\\s*\\))";
String regex = "(?s)" + methodSignature + ".*?(?="+ methodSignature + ")";
基本上这个正则表达式匹配每个方法。但它也有
缺陷。我将探讨它以及它的缺陷。
\\s*((public|private|protected|static|final|abstract|synchronized|volatile)\\s+)*
匹配指定修饰符(至少一个空格),可以出现任意次数,包括零,因为方法可能没有修饰符。(为了简单起见,我保留了允许的修饰符数量不受限制。在真正的解析器中,我也不会允许这样做,也不会使用正则表达式来完成这样的任务。)
[\\w<>\\[\\]\\.]+
这是方法的返回类型。它可以包含单词字符,用于泛型类型的<>
,用于数组的[]
和用于嵌套类表示法的.
。
\\s+\\w+\\s*\\
方法名。
\\((\\s*[\\w<>\\[\\]\\.]*\\s+\\w+\\s*,?)*\\s*\\))
特别棘手的部分是方法参数。首先,您可能认为可以将此部分轻松替换为(
。我也这样想。但是,我注意到它不仅匹配方法,还匹配匿名类,例如new Anonymous(someVariable){....}
避免这种情况最简单和最有效的方法是指定方法参数结构。[\\w<>\\[\\]\\.]
是参数类型可以由的可能符号。\\s+\\w+\\s*,?
参数类型后跟至少一个空格和参数名。如果方法包含多个参数,则参数名称后面可以跟随,
。
那么缺陷是什么呢?主要缺陷是在方法中定义类。方法可以包含类定义。考虑以下情况:
public void regexIsAGoodThing(){
new RegexIsNotSoGoodActually(){
void dissapontingMethod(){
}
}
}
这很好地解释了为什么正则表达式不是处理此类任务的合适工具。由于方法可能是嵌套结构,因此无法
可靠地从Java文件中解析方法。方法可能包含类定义,而这些类可能包含具有另一个类定义的方法,依此类推。正则表达式被无限递归所捕获并失败。
另一个正则表达式失败的情况是注释。在注释中,您可以输入任何内容。
void happyRegexing(){
return void;
}
我们不能忘记的另一件事是注解。如果下一个方法被注释了呢?那么这个正则表达式几乎可以匹配成功,除了它也会匹配注释。虽然可以避免这种情况,但正则表达式将变得更加庞大。
public void goodDay(){
}
@Zzzzz
public void goodNight(){
}
另一个案例是代码块。如果两个方法之间包含静态或实例块怎么办?
public void iWillNotDoThisAnyMore(){
}
static{
}
public void iWillNotParseCodeWithRegex(){
}
P.S 它还有另一个缺陷 - 它匹配
new SomeClass()
和下一个方法签名之间的所有内容。你可以解决这个问题,但是这只是一种变通方法,而不是优雅的代码。我还没有包括文件结尾的匹配。如果您感兴趣,也许明天我会添加编辑。现在要睡觉了,在欧洲已经接近早晨了。
正如您所看到的,正则表达式几乎是大多数任务的好工具。但是我们程序员讨厌“几乎”这个词。我们甚至没有将其纳入我们的词汇表中。难道不是吗?