【问题标题】:Searching string with wildcards使用通配符搜索字符串
【发布时间】:2020-03-07 14:48:49
【问题描述】:

我正在做以下编程练习:String searching with wildcard。声明是:

下面的方法,是最简单的字符串搜索算法。它会 查找文本字符串中第一次出现的单词。

haystack = 全文

针 = 搜索词

通配符 = _

find("罢工", "我要打倒你"); // 返回 7

find 方法已经生成。

问题是在针中实现通配符。如果你有一个_ 在针中,它将匹配大海捞针中的任何字符。

普通的字符串搜索算法会找到第一次出现的 文本(干草堆)中的单词(针),从索引 0 开始。像这样:

find("strike", "我要打击你");返回 7

needle 中的通配符将匹配大海捞针中的任何字符。这 方法应该适用于任何类型的针和干草任务。你可以 假设针比干草堆短(或等于)。

find("g__d", "这就是当总统的好处"); // 返回 11

如果不匹配,该方法应该返回 -1

我们编写了以下代码:

import java.util.regex.*;
public class SearchEngine {
    static int find(String needle, String haystack){
      System.out.println("needle: "+needle);
      System.out.println("haystack: "+haystack);
      String regex = needle.replace("_",".");
      if(regex.equals(needle)){
        return haystack.indexOf(needle);
      }
      System.out.println("regex: "+regex);
      Matcher m = Pattern.compile(regex).matcher(haystack);
      int pos = -1;
      if(m.find()){
        pos = m.start();
      }
      System.out.println("pos: "+pos);
      return pos;
    }
}

我们发现了一个奇怪的测试,它没有通过。作为测试用例:

import org.junit.Test;
import static org.junit.Assert.assertEquals;

public class WildsTest {
    String haystack = "Once upon a midnight dreary, while I pondered, weak and weary";    
    @Test
    public void normalSearchTest(){
        assertEquals(0,SearchEngine.find("Once", haystack));
        assertEquals(12, SearchEngine.find("midnight", haystack));
        assertEquals(-1, SearchEngine.find("codewars", haystack));
    }
    @Test
    public void wildSearchTest(){
        assertEquals(5, SearchEngine.find("_po_", haystack));
        assertEquals(12, SearchEngine.find("___night", haystack));
        assertEquals(3, SearchEngine.find("___4$&%$--___", "-..,.44$&%$--,.,"));
    }
 }

在最后一种情况下失败:

needle: ___4$&%$--___
haystack: -..,.44$&%$--,.,
regex: ...4$&%$--...
pos: -1

为什么正则表达式不匹配“-..,.44$&%$--,.”内的“...4$&%$--...”?

我们还阅读了:

编辑:

我们遵循@Alex 的建议,并尝试使用 Pattern.quote:

import java.util.regex.*;
public class SearchEngine {
    static int find /*????*/ (String needle, String haystack){
      System.out.println("needle: "+needle);
      System.out.println("haystack: "+haystack);
      String regex = needle.replace("_",".");
      if(regex.equals(needle)){
        return haystack.indexOf(needle);
      }
      System.out.println("regex: "+regex);
      String quotedRegex = Pattern.quote(regex);
      System.out.println("quotedRegex: "+quotedRegex);
      Matcher m = Pattern.compile(quotedRegex).matcher(haystack);
      int pos = -1;
      if(m.find()){
        pos = m.start();
      }
      System.out.println("pos: "+pos);
      return pos;
    }
}

但是我们发现了以下痕迹:

needle: _po_
haystack: Once upon a midnight dreary, while I pondered, weak and weary
regex: .po.
quotedRegex: \Q.po.\E
pos: -1
expected:<5> but was:<-1>

我们如何使用 Pattern.quote 实现通配符搜索?

此外,我们遵循@s.fuhrm 的建议,将具有特殊含义的字符替换为“\\$”

import java.util.regex.*;
public class SearchEngine {
    static int find /*????*/ (String needle, String haystack){
      System.out.println("needle: "+needle);
      System.out.println("haystack: "+haystack);
      String regex = needle.replace("_",".");
      if(regex.equals(needle)){
        return haystack.indexOf(needle);
      }
      System.out.println("regex: "+regex);

      Matcher m = Pattern.compile(regex.replace("$","\\$")).matcher(haystack);
      int pos = -1;
      if(m.find()){
        pos = m.start();
      }
      System.out.println("pos: "+pos);
      return pos;
    }
}

这是通过测试的代码。

【问题讨论】:

    标签: java regex string


    【解决方案1】:

    “needle”中有一些字符在正则表达式中具有特殊含义,即美元符号 $,在正则表达式中表示“行尾”。在制作正则表达式时,您应该转义这些特殊字符以推进文字字符串。您可以使用 Pattern.quote 方法来执行此操作。

    【讨论】:

      【解决方案2】:

      为什么正则表达式不匹配“...4$&%$--...”里面的原因 "-..,.44$&%$--,.,"?

      至少$ 是在行尾匹配的regular expression。这不是你想要的。您需要将$分别替换为\$"\\$"

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-19
        • 1970-01-01
        • 2017-07-17
        • 2018-08-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多