【发布时间】: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$&%$--...”?
我们还阅读了:
- Can Java String.indexOf() handle a regular expression as a parameter?
- https://howtodoinjava.com/regex/match-any-set-of-characters/
- Wildcard matching in Java
- Java string matching with wildcards
编辑:
我们遵循@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;
}
}
这是通过测试的代码。
【问题讨论】: