【问题标题】:Match word by its prefix通过前缀匹配单词
【发布时间】:2019-10-03 15:31:31
【问题描述】:

我正在尝试通过以特定字符结尾的前缀来匹配字符串。例如,如果我的字符串是"abcd" 并以# 结尾,那么任何前缀为"abcd" 的单词都应该匹配,只要它以# 结尾。以下是一些有助于说明模式的示例:

输入"ab#" 给出true(因为"ab""abcd" 的前缀并以# 结尾)。

输入"abcd#" 给出true(因为"abcd""abcd" 的前缀并以# 结尾)。

输入"bc#" 给出false(因为"bc" 不是"abcd"前缀)。

输入"ab@" 给出false(而"ab""abcd"前缀,它不以# 结尾)。

输入"ac#" 给出false(虽然"ac" 包含在"abcd" 中,但它不以"abcd" 的前缀开头)。

到目前为止,我已经设法提出了以下似乎工作正常的表达式:

/(abcd|abc|ab|a)#/

虽然这有效,但不是很实用,因为长度较大的词 n 会使表达式变得非常大:

/(n|n-1|n-2| ... |1)#/

有没有办法重写这个表达式,使其更具可扩展性和简洁性?

我的尝试示例(在 JS 中):

const regex = /(abcd|abc|ab|a)#/; 

console.log(regex.test("abcd#")); // true
console.log(regex.test("ab#")); // true
console.log(regex.test("abc#")); // true
console.log(regex.test("abz#")); // false
console.log(regex.test("abc@")); // false

编辑:提供的一些解决方案很好,可以做我想做的事,但是,对于这个特定的问题,我正在使用纯正则表达式来匹配前缀的解决方案.

【问题讨论】:

  • 你为什么不使用基本的字符串操作来检查这个而不是正则表达式?
  • @VLAZ 嗨 vlaz,你是对的 - 我可以。但是,我正在尝试将其作为正则表达式练习(我可能不应该标记 JS,我现在已经删除了标记)
  • 您是在测试字符串是否包含与您的具体情况相匹配的单词,还是尝试提取该单词?我们在谈论多少个前缀?
  • @JonP 我只是测试一下是不是前缀,不用解压
  • 字符串是整个“单词”还是字符串是“here is my preword# here”?

标签: regex prefix


【解决方案1】:

只需在此处使用String#startsWithString#endsWith

String input = "abcd";
String prefix = "ab#";

if (input.startsWith(prefix.replaceAll("#$", "")) && prefix.endsWith("#")) {
    System.out.println("MATCH");
}
else {
    System.out.println("NO MATCH");
}

编辑:上述的 JavaScript 版本:

var input = "abcd";
var prefix = "ab#";

if (input.startsWith(prefix.replace(/#$/, "")) && prefix.endsWith("#")) {
   console.log("MATCH");
}
else {
    console.log("NO MATCH");
}

【讨论】:

  • 感谢您抽出宝贵时间回答。这肯定会起作用,并且是一个很好的解决方案。但是,我目前正在寻找使用纯正则表达式的答案(上面我刚刚使用 JS 作为环境来测试我的正则表达式)。
  • Regex 并不是万能的最佳工具。虽然我倾向于尽可能使用正则表达式,但它确实往往不如仅使用基本字符串函数的解决方案。
  • 我明白了,这是有道理的。感谢您抽出宝贵时间为我提供帮助。
【解决方案2】:

试试^ab?c?d?#$

解释:

`^` - match beginning of a string

`b?` - match match zero or one `b`

Rest 与上述类似。

Demo

【讨论】:

  • 这将匹配“abd#”、“acd#”、“ac#”和“ad#”。我相信这些都是误报。
  • 感谢您的回答,但这将匹配 ac#(虽然 ac 包含在前缀中,但它应该只匹配 ac,如果该词在该词的开头看起来像“acbe”在这种情况下使用“ac”,但是在上面的情况下,它以"ab") 开始
  • 我认为您需要^((((ab)?)c)?)d)?$,但这很快就会变得笨拙。
【解决方案3】:

这是一个左字段 JavaScript 选项。构建和有效前缀数组,在数组上使用 join 来制作您的正则表达式模式。

var validPrefixes = ["abcd",
"abc", 
"ab", 
"a", 
"areallylongprefix"];

var regexp = new RegExp("^(" + validPrefixes.join("|") + ")#$");


console.log(regexp.test("abcd#"));// true
console.log(regexp.test("ab#")); // true
console.log(regexp.test("abc#")); // true
console.log(regexp.test("abz#")); // false
console.log(regexp.test("abc@")); // false
console.log(regexp.test("areallylongprefix#")); //true

这可以适应旅游选择的语言,如果您的前缀是从数据库或类似数据库中动态检索的,这也很方便。

【讨论】:

    【解决方案4】:

    这是我的 c# 尝试:

    private static bool test(string v)
    {
       var pattern = "abcd#";
       //No error handling
       return v.EndsWith(pattern[pattern.Length-1])
          && pattern.Replace("#", "").StartsWith(v.Replace("#",""));
    }
    
    Console.WriteLine(test("abcd#")); // true
    Console.WriteLine(test("ab#")); // true
    Console.WriteLine(test("abc#")); // true
    Console.WriteLine(test("abz#")); // false
    Console.WriteLine(test("abc@")); // false
    Console.WriteLine(test("abc")); //false
    

    【讨论】:

    • test("abc") 将返回 true 但这是错误的。与test("bcd#") 相同
    • @VLAZ 我不是想展示一种防弹解决方案,而是一种非正则表达式方法。话虽如此,我扩展了代码以涵盖abc 的情况。
    【解决方案5】:
    /a(b(cd?)?)?#/
    

    或者对于更长的示例,匹配“abcdefg#”的前缀:

    /a(b(c(d(e(fg?)?)?)?)?)?#/
    

    生成这个正则表达式并不是很简单,但一些选项是:

    function createPrefixRegex(s) {
        // This method creates an unnecessary set of parentheses
        // around the last letter, but that won't harm anything.
        return new RegExp(s.slice(0,-1).split('').join('(') + ')?'.repeat(s.length - 2) + '#');
    }
    
    function createPrefixRegex2(s) {
        var r = s[0];
        for (var i = 1; i < s.length - 2; ++i) {
            r += '(' + s[i];
        }
        r += s[s.length - 2] + '?' + ')?'.repeat(s.length - 3) + '#';
        return new RegExp(r);
    }
    
    function createPrefixRegex3(s) {
        var recurse = function(i) {
            if (i >= s.length - 1) {
                return '';
            }
            if (i === s.length - 2) {
                return s[i] + '?';
            }   
            return '(' + s[i] + recurse(i + 1) + ')?';
        }   
    
        return new RegExp(s[0] + recurse(1) + '#');
    }
    

    如果输入字符串在 '#' 字符之前没有前缀,它们可能会失败,并且它们假定字符串中的最后一个字符是 '#'。

    【讨论】:

    • @Tim Biegeleisen 的评论是在我输入此内容时添加的,并且几乎相同。
    • 我不认为这比使用的替代 OP 更具可扩展性。
    • @VLAZ Meh,对于较长的字符串,它会更好一些,至少就长度而言。 OP 解决方案的正则表达式的长度增长为 O(n^2),而这应该增长为 O(n)。不过,OP 确实具有更易读的优势。
    • 它也更容易自动生成,因为您只需将完整前缀分解为所有子前缀,反向并与"|" 连接。因此,即使模式的长度增长得更快,它的创建也可以很容易地自动化。这也可以自动化,但更烦人。
    • 这也是真的。
    猜你喜欢
    • 2012-02-21
    • 2014-10-21
    • 2013-04-17
    • 1970-01-01
    • 1970-01-01
    • 2013-08-26
    • 1970-01-01
    相关资源
    最近更新 更多