从部分或驼峰式字符串中匹配的正则表达式?

3
我需要一个正则表达式来匹配部分或驼峰式字符串。例如,如果搜索集包含字符串“MyPossibleResultString”,我希望能够与以下内容匹配:
- MyPossibleResultString - MPRS - MPRString - MyPosResStr - M
我还想包括通配符匹配,例如:
- MyP*RString - *PosResString - My*String
如果不清楚我的意思,我唯一能想到的例子就是Eclipse的“打开类型”对话框,这几乎是我要寻找的确切行为。我对使用正则表达式并不太了解,所以我不确定是否重要,我正在寻找Java中的解决方案。

如果Eclipse几乎具有您正在寻找的确切行为,那么您是否可以查看Eclipse的源代码? - Michael Myers
我会考虑这个选项,同时继续寻找答案 :-) - Grundlefleck
5个回答

4

好的,如果您已经可以支持第一个例子中描述的匹配功能,我不太清楚为什么您需要通配符功能。以下是我总结的方法:给定查询字符串query,您可以使用正则表达式创建另一个正则表达式:

String re = "\\b(" + query.replaceAll("([A-Z][^A-Z]*)", "$1[^A-Z]*") + ".*?)\\b";

例如查询MyPosResStr将变成正则表达式:
\\b(My[^A-Z]*Pos[^A-Z]*Res[^A-Z]*Str[^A-Z]*.*?)\\b

然后,您可以使用Matcher.find方法来匹配此正则表达式,得到类似以下结果:

public static String matchCamelCase(String query, String str) {
    query = query.replaceAll("\\*", ".*?");
    String re = "\\b(" + query.replaceAll("([A-Z][^A-Z]*)", "$1[^A-Z]*") + ".*?)\\b";

    System.out.println(re);
    Pattern regex = Pattern.compile(re);

    Matcher m = regex.matcher(str);

    if  (m.find()) {
        return m.group();
    } else return null;
}

这将在字符串str中返回与您的驼峰查询第一个匹配项。

编辑:我添加了一行代码来处理通配符,因为在疲惫的状态下我没有意识到需要它们。


第一个例子没有包括我想让“*PRS”匹配的情况,其中“PRS”不应该匹配。 - Grundlefleck
在你编辑之前,我本来想说它对于一些模式不起作用,现在唯一不匹配的示例是没有显式悬挂通配符的'M'/'MP'/'MPR'之类的。我相信这只是一个小因素,我喜欢它更简洁(我相信我很快就能读懂它:-D)+1 - Grundlefleck
谢谢你发现了这个问题!你是对的,那是我漏掉的另一个情况。马上就来。 - Il-Bhima
如果目标字符串以小写的“my”开头,正则表达式会是什么样子?例如 mPosResStr 应该匹配。 - msteiger

2

正如danbruc所说,您需要为每个新的查询生成一个新的正则表达式。这段代码应该能够实现您想要的功能。

public Pattern queryToPattern(String query) {
    StringBuilder sb = new StringBuilder();
    char[] chars = query.toCharArray();
    boolean incamel = false;
    for (int i=0; i < chars.length; i++) {
        if (chars[i] == '*') {
                            if (!incamel)
                    sb.append(".*");
        } else if (Character.isUpperCase(chars[i])) {
            if (incamel) {
                sb.append(".*");
            }
            sb.append(chars[i]);
            incamel = true;
        } else {
            sb.append(chars[i]);
        }

    }
    sb.append(".*");
    return Pattern.compile(sb.toString());
}

查询:MyP*RString

创建模式:My.* P.* R.* String.*


这个方法很有效,现在加一分,但我会等待时间的考验,看看是否有更好的答案(由于我的无知,我目前无法判断)。谢谢。 - Grundlefleck

1

使用单个正则表达式无法实现此操作。您需要基于输入构建一个正则表达式并使用它进行搜索。很容易看出,您不能使用单个正则表达式 - 用户可以搜索任何(驼峰式)字符串,因此您的正则表达式需要匹配任何(驼峰式)字符串,但这不再是搜索了。


所以你的意思是,例如,输入字符串“MyPosRS”将被解析以生成用于匹配的正则表达式“MyPosRS”? - Grundlefleck
是的,您必须根据用户输入生成一个基于正则表达式的搜索模式,并使用它进行搜索。您现在无法构建一个搜索模式,因为您不知道输入内容,也就不知道正则表达式应该匹配什么。 - Daniel Brückner
好的,那很有道理。现在看起来相当显而易见...+1 - Grundlefleck

0

Il-Bhima的回答很好,但我发现这段代码对我来说更有效(请原谅我的C#,但它是一样的):

pattern = Regex.Escape(pattern);
pattern = pattern.Replace(@"\*", ".*?");
pattern = Regex.Replace(pattern, "([A-Z][^A-Z]*)", "$1[^A-Z]*?") + ".*";

注意末尾的“.*”,它允许不完整的“startof”短语(也允许不指定所有大写字母) 此外,“[^A-Z]*”匹配器后面的星号修复了工具包答案中q4的问题,其中在大写字母之后提供了小写字母(它们应直接出现在大写字母之后,而不是下一个字母之前)。

0
你可以尝试类似这样的代码:
class RegexTransformer {
    public String fromQuery(String query) {
        StringBuilder sb = new StringBuilder();
        sb.append("^");
        sb.append(query.replaceAll("(\\p{Upper}(?!\\p{Lower}))",
                "$1\\\\p{Alpha}*?"));
        sb.append("$");
        return sb.toString();
    }
}

请参阅Pattern API,了解负向先行断言(?!pat)、POSIX字符类\p{class}和勉强量词*?的描述。
示例测试用例:
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import org.junit.Test;

public class RegexTransformerTest {

    private RegexTransformer rt = new RegexTransformer();

    @Test
    public void testQueries() {
        String in = "MyPossibleResultString";
        String q1 = "MyPossibleResultString";
        String q2 = "MPRS";
        String q3 = "MPRString";
        String q4 = "MyPosResStr"; // this wont work
        String q5 = "M";

        test(in, q1, "^MyPossibleResultString$");
        test(in, q2, "^M\\p{Alpha}*?P\\p{Alpha}*?R\\p{Alpha}*?S\\p{Alpha}*?$");
        test(in, q3, "^M\\p{Alpha}*?P\\p{Alpha}*?R\\p{Alpha}*?String$");
        test(in, q5, "^M\\p{Alpha}*?$");
    }

    private void test(String in, String query, String expected) {
        assertEquals("transform", expected, rt.fromQuery(query));
        assertTrue("match", in.matches(rt.fromQuery(query)));
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接