如何使用正则表达式匹配方法块?

时间:2016-03-10 09:50:29

标签: java regex

举个例子。

 public static FieldsConfig getFieldsConfig(){
    if(xxx) {
      sssss;
    }
   return;
}

我写了一个正则表达式,"\\s*public\\s*static.*getFieldsConfig\\(.*\\)\\s*\\{"

它只能匹配第一行。但是如何正确匹配最后的"}"方法?

帮帮我。感谢。

编辑: 未指定方法{}的内容。但模式肯定是这样的,

  public static xxx theKnownMethodName(xxxx) {
    xxxxxxx
  }

7 个答案:

答案 0 :(得分:3)

我决定更进一步;)

这是一个正则表达式,它将为您提供不同捕获组中函数的修饰符,类型,名称和正文:

((?:(?:public|private|protected|static|final|abstract|synchronized|volatile)\s+)*)
\s*(\w+)\s*(\w+)\(.*?\)\s*({(?:{[^{}]*}|.)*?})

它处理嵌套大括号(@callOfCode (半)可能使用正则表达式;)和一组固定的修饰符。

它不处理复杂的东西,如评论中的大括号和类似的东西,但它适用于最简单的。

此致

Regex101 sample here

编辑:并回答你的问题;),你感兴趣的是捕获组4。

编辑2:正如我所说 - 简单。但是你可以使处理更复杂的方法变得更复杂。 Here's an updated handling one more level of nesting

((?:(?:public|private|protected|static|final|abstract|synchronized|volatile)\s+)*)
\s*(\w+)\s*(\w+)\(.*?\)\s*({(?:{[^{}]*(?:{[^{}]*}|.)*?[^{}]*}|.)*?})

你可以在另一个层面...而另一个......但是正如有人评论 - 这不应该由正则表达式完成。然而,这会处理简单方法。

答案 1 :(得分:2)

正则表达式绝对不是最好的工具,但如果你想要正则表达式,并且你的代码缩进,你可以试试:

^(?<indent>\s*)(?<mod1>\w+)\s(?<mod2>\w+)?\s*(?<mod3>\w+)?\s*(?<return>\b\w+)\s(?<name>\w+)\((?<arg>.*?)\)\s*\{(?<body>.+?)^\k<indent>\}

DEMO

它有其他命名组,您可以删除它们。它使用缩进级别来查找最后}

答案 2 :(得分:1)

您需要启用DOTALL模式。然后dot将匹配newLine字符。只需在正则表达式的开头添加(?s)

 String s = "   public static FieldsConfig getFieldsConfig(){\n"
             + "   if(xxx) {\n"
             + "              sssss;\n"
             + "   }\n"
             + "      return;\n"
             +"}";
 Matcher m = Pattern.compile("(?s)\\s*public\\s+static\\s+\\w+?\\sgetFieldsConfig\\(\\s*\\).*").matcher(s);
 m.find();
 System.out.println(m.group());

Outpup是你想要的所有方法体。没有(?s),它只匹配第一行。但你不能用正则表达式解析java代码。其他人已经说过了。此正则表达式将匹配从方法签名开始到文件结尾的所有内容。如何匹配才能到达方法体的末尾?方法可以包含许多{....}以及许多return;。正则表达式不是一个魔术棒。

答案 3 :(得分:1)

试试这个

((?<space>\h+)public\s+static\s+[^(]+\([^)]*?\)\s*\{.*?\k<space>\})|(public\s+static\s+[^(]+\([^)]*?\)\s*\{.*?\n\})

<强>解释
我们将按关键字public结束}结束方法块开始,public}必须具有相同的\s字符,因此您的代码必须格式正确: )https://en.wikipedia.org/wiki/Indent_style

\h:匹配空格但不匹配换行符 (?<space>\h+):在public之前获取所有空格,然后在space名称中分组 public\s+static\s公共静态
[^(]:任何字符,但不是(
([^)]:任何但不是) \k<space>\}}结束number of whitespace,然后}

Demo

输入:

public static FieldsConfig getFieldsConfig(){
    if(xxx) {
      sssss;
    }
   return;
}

NO CAPTURE

public static FieldsConfig getFieldsConfig2(){
    if(xxx) {
      sssss;
    }
   return;
}

NO CAPTURE

    public static FieldsConfig getFieldsConfig3(){
        if(xxx) {
          sssss;
        }
       return;
    }

NO CAPTURE

        public static FieldsConfig getFieldsConfig4(){
            if(xxx) {
              sssss;
            }
           return;
        }

输出:

MATCH 1
3.  [0-91]  `public static FieldsConfig getFieldsConfig(){
    if(xxx) {
      sssss;
    }
   return;
}`

MATCH 2
3.  [105-197]   `public static FieldsConfig getFieldsConfig2(){
    if(xxx) {
      sssss;
    }
   return;
}`

MATCH 3
1.  [211-309]   `   public static FieldsConfig getFieldsConfig3(){
        if(xxx) {
          sssss;
        }
       return;
    }`

MATCH 4
1.  [324-428]   `       public static FieldsConfig getFieldsConfig4(){
            if(xxx) {
              sssss;
            }
           return;
        }`

答案 4 :(得分:1)

谢谢大家。经过一番考虑后,我在某种程度上找到了可靠的方法。现在分享。

String regex ="\\s*public\s+static\s+[\w\.\<\>,\s]+\s+getFieldsConfig\\(.*?\\)\\s*\\{.*?\\}(?=\\s*(public|private|protected|static))";

String regex2 = "\\s*public\s+static\s+[\w\.\<\>,\s]+\s+getFieldsConfig\\(.*?\\)\\s*\\{.*?\\}(?=(\\s*}\\s*$))";

regex = "(" + regex +")|("+ regex2 + "){1}?";

Pattern pattern = Pattern.compile(regex, Pattern.DOTALL)

它可以很好地匹配我的方法体。

PS 是的,正则表达式可能不是非常严格地解析方法的合适方法。一般来说,正则表达式比编程更省力,在特定情况下工作正常。调整它,确保它适合你。

答案 5 :(得分:1)

维克多,你已经让我推荐你的答案了。所以我决定花点时间写一篇完整的评论并给出一些提示。我不是某种正则表达式专业人士,也不是非常喜欢它。目前我正在开发一个大量使用正则表达式的项目,所以我已经看到并写了它的内容,可以非常可靠地回答你的问题并且厌倦了正则表达式。 所以,让我们开始你的正则表达式分析:

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您允许任意数字,包括publicstatic之间的零空格。它可以匹配publicstatic。每次在必须部分数量的空格分隔的单词之间使用\\s+
  • (.\*?\\)\\s\*\\{.\*\\}您允许在第一批之间出现任何内容。它将匹配任何符号,直到)。现在我们达到了使你的正则表达式工作的部分不像你想要的那样。 \\{.*\\}是一个重大错误。它会匹配所有内容 } 最后一个文件 public private protected {{1} } 到达了。我已将您的static方法粘贴到java文件并进行了测试。仅使用正则表达式的第一部分(getFieldsConfig)来处理从方法到文件中的最后一个方法的所有内容。

没有必要逐步分析其他部分,因为"\\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(); // `\\A` marks beginning of file. Since file has only one beginning, it will scan file from start to beginning. 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}?"; // I've included (?s) since we reading from file newline chars are not excluded. Without (?s) it would match anything unless your method is written in a single line. 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<>\\[\\]\\.]参数类型后跟至少一个空格和参数名称。如果method包含多个参数,则参数名称后跟\\s+\\w+\\s*,?

那么关于缺陷的是什么?主要缺陷是方法中定义的类。方法可以在其中包含类定义。考虑一下这种情况:

,

这很好地解释了为什么正则表达式不适合这样的工作。无法从java文件可靠地解析方法,因为方法可能是嵌套结构。方法可以包含类定义,这些类可以包含具有其他类定义的方法,依此类推。正则表达式被无限递归捕获并失败。

另一个案例是正则表达式会失败是评论。在评论中,您可以输入任何内容

public void regexIsAGoodThing(){
  //some code
  new RegexIsNotSoGoodActually(){
    void dissapontingMethod(){
       //Efforts put in writing this regex was pointless because of this dissapointing method.
    }
  }
}

我们不能忘记的另一件事是注释。如果下一个方法被注释怎么办?那个正则表达式几乎可以匹配,除了它也会匹配注释。这可以避免,但正则表达式将更大。

void happyRegexing(){
     return void;
     // public void happyRegexingIsOver(){....}
}

另一个案例是块。如果两个方法之间包含静态或实例块会怎么样?

public void goodDay(){

}

@Zzzzz //This annotation can be carried out by making our regex even more larger
public void goodNight(){

}

P.S 它有另一个缺陷 - 它匹配public void iWillNotDoThisAnyMore(){ } static{ //some code } public void iWillNotParseCodeWithRegex(){ //end of story } 以及下一个方法签名之前的所有内容。你可以解决这个问题,但是再一次 - 这可以解决,但不是优雅的代码。我还没有包含文件匹配的结尾。如果您有兴趣,也许我明天会添加编辑。现在去睡觉,它在欧洲接近早晨。 正如您所看到的,正则表达式是几乎大多数任务的好工具。但我们程序员讨厌几乎。我们的词汇表中甚至没有它。我们不是吗?

答案 6 :(得分:0)

我必须根据自己的需要修改此答案。我想要整个方法的捕获组以及文件中每个方法的名称。我只需要这两个捕获组。这需要PCRE中的单行标志。在其他REGEX解析中,将需要全局(g)标志来捕获完整文件,而不仅仅是一个匹配。我嵌套了括号捕获@SamWhan,显示允许五个级别的嵌套。这应该可以完成工作,因为更多工作​​违反了大多数推荐标准。因此,此REGEX确实非常昂贵,因此请注意。

(?:public|private|protected|static|final|abstract|synchronized|volatile)\s*(?:(?:(?:\w*\s)?(\w+))|)\(.*?\)\s*(?:\{(?:\{[^{}]*(?:\{[^{}]*(?:\{[^{}]*(?:\{[^{}]*(?:\{[^{}]*(?:\{[^{}]*}|.)*?[^{}]*}|.)*?[^{}]*}|.)*?[^{}]*}|.)*?[^{}]*}|.)*?[^{}]*}|.)*?})