2

我有一个形式的字符串:

canonical_class_name[key1="value1",key2="value2",key3="value3",...] 

目的是在一个组中捕获 canonical_class_name,然后交替 key=value 组。目前它不匹配测试字符串(在下面的程序中,testString)。

必须至少有一个键/值对,但可能有很多这样的对。

问题:目前正则表达式正确地抓取了规范的类名和第一个键,但是它吞噬了所有内容,直到最后一个双引号,我如何让它变得懒惰地抓取键值对?

这是以下程序放在一起的正则表达式:

(\S+)\[\s*(\S+)\s*=\s*"(.*)"\s*(?:\s*,\s*(\S+)\s*=\s*"(.*)"\s*)*\]

根据您的喜好,您可能会发现程序版本更易于阅读。

如果我的程序传递了字符串:

org.myobject[key1=\"value1\", key2=\"value2\", key3=\"value3\"]

...这些是我得到的组:

Group1 contains: org.myobject<br/>
Group2 contains: key1<br/>
Group3 contains: value1", key2="value2", key3="value3<br/>

还有一点需要注意,使用String.split()I 可以简化表达式,但我将其作为一种学习经验来更好地理解正则表达式,所以我不想使用这样的捷径。

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class BasicORMParser {
     String regex =
            "canonicalName\\[ map (?: , map )*\\]"
            .replace("canonicalName", "(\\S+)")
            .replace("map", "key = \"value\"")
            .replace("key", "(\\S+)")
            .replace("value", "(.*)")
            .replace(" ", "\\s*"); 

    List<String> getGroups(String ormString){
        List<String> values = new ArrayList();
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(ormString);
        if (matcher.matches() == false){
            String msg = String.format("String failed regex validiation. Required: %s , found: %s", regex, ormString);
            throw new RuntimeException(msg);
        }
        if(matcher.groupCount() < 2){
            String msg = String.format("Did not find Class and at least one key value.");
            throw new RuntimeException(msg);
        }
        for(int i = 1; i < matcher.groupCount(); i++){
            values.add(matcher.group(i));
        }
        return values;
    }
}
4

2 回答 2

4

您实际上自己回答了这个问题:让他们变得懒惰。也就是说,使用惰性(又名非贪婪不情愿)量词。只需将每个更改(\S+)(\S+?),并将每个更改(.*)(.*?)。但是如果是我,我会改变那些子表达式,这样它们就永远不会匹配太多,不管贪婪。例如,您可以([^\s\[]+)用于类名、([^\s=]+)键和"([^"]*)"值。

不过,我认为这不会解决您的真正问题。一旦你得到它以正确匹配所有键/值对,你会发现它只捕获第一对(组 #2 和 #3)和最后一对(组 #4 和 #5)。这是因为,每次(?:\s*,\s*(\S+)\s*=\s*"(.*)"\s*)*重复时,这两个组的内容都会被覆盖,而他们在前一次迭代中捕获的任何内容都会丢失。没有办法绕过它,这至少是一个两步操作。例如,您可以将所有键/值对作为一个块进行匹配,然后拆分各个对。

还有一件事。这一行:

if(matcher.groupCount() < 2){

......可能没有做你认为它做的事情。 groupCount()是 Pattern 对象的静态属性;它告诉正则表达式中有多少个捕获组。无论匹配成功还是失败,groupCount()都将始终返回相同的值——在本例中为 5。如果匹配成功,一些捕获组可能为空(表示他们没有参与匹配),但总会有五个。


编辑:我怀疑这是你最初尝试的:

Pattern p = Pattern.compile(
    "(?:([^\\s\\[]+)\\[|\\G)([^\\s=]+)=\"([^\"]*)\"[,\\s]*");

String s = "org.myobject[key1=\"value1\", key2=\"value2\", key3=\"value3\"]";
Matcher m = p.matcher(s);
while (m.find())
{
  if (m.group(1) != null)
  {
    System.out.printf("class : %s%n", m.group(1));
  }
  System.out.printf("key : %s, value : %s%n", m.group(2), m.group(3));
}

输出:

class : org.myobject
key : key1, value : value1
key : key2, value : value2
key : key3, value : value3

理解正则表达式的关键是这部分:(?:([^\s\[]+)\[|\G). 在第一次通过时,它匹配类名和左方括号。之后,\G接管,将下一场比赛锚定到上一场比赛结束的位置。

于 2011-07-19T04:09:46.657 回答
2

对于非贪婪匹配,?在模式后附加一个。例如,.*?匹配尽可能少的字符。

于 2011-07-19T01:35:31.787 回答