在Java中双向字符串匹配两侧的通配符

时间:2016-05-24 16:45:47

标签: java regex string

我的情况是我有一个包含8个字符长字符串的列表,其中包含通配符(由?表示),我需要匹配一个也带有通配符的输入字符串。所述字符串由四个字母字符(A-Z)和四个数字(0-9)组成。为了便于理解,这是一组字符串的示例:

ABCD1234
A??D123?
A???????
?BC1234?

如果我将A?CD12?4作为输入,则比较应在每次比较中解析为true。

我当前的实现是使用Hashmap,使用字符串作为键,并将与regex相同的字符串解析为对象映射。例如,A?CD12?4变为A([A-Z]|\\?)CD12([0-9]|\\?)4,然后使用以下代码获取一组兼容字符串:

Map<String, String> map = new HashMap<String, String>();

map.put("A???????", "A([A-Z]|\\?)([A-Z]|\\?)([A-Z]|\\?)([0-9]|\\?)([0-9]|\\?)([0-9]|\\?)([0-9]|\\?)");
map.put("ABCD1234", "ABCD1234");
map.put("A??D123?", "A([A-Z]|\\?)([A-Z]|\\?)D123([0-9]|\\?)");
map.put("?BCD123?", "([A-Z]|\\?)BC123([0-9]|\\?)");


String str = "A?CD12?4";
String strReg = "A([A-Z]|\\?)CD12([0-9]|\\?)4";

Set<Object> set = map.keySet()
               .stream()
               .filter(s -> str.matches(map.get(s)) || s.matches(strReg) )
               .collect(Collectors.toSet());

然而,这仍然错过了输入,其中str中的通配符问号但未显示在地图字符串上(例如,输入A?CD1234无法解析为?BCD1234和-versa)。

虽然我知道通过遍历String来解决这个问题很简单,但我的解决方案需要将输入与 50000 字符串进行比较,并且我以大于的速率读取输入30 /秒,所以表现很关键。

此处理在Thread内部进行,外部交互可以更改输入将检查的字符串列表(仅添加或删除)。

2 个答案:

答案 0 :(得分:2)

作为一般规则,比较字符串时可以忽略?通配符。由于这可以用于所有模式,因此不需要在地图中存储正则表达式替代,可以在迭代时推断可以跳过该字符。这是使用并行流的更快的解决方案:

Set<String> patterns = new HashSet<>();

patterns.add("A???????");
patterns.add("ABCD1234");
patterns.add("A??D123?");
patterns.add("?BCD123?");

String s = "A?CD12?4";

Set<String> matches = patterns.parallelStream() // the main benefit of this
                              .filter(p -> {
                                  for (int i = 0; i < s.length(); i++) {
                                      char a = s.charAt(i),
                                           b = p.charAt(i);
                                      if (a != '?' && b != '?' && a != b)
                                          return false;
                                  }
                                  return true;
                              }).collect(Collectors.toSet());

答案 1 :(得分:1)

我不会使用正则表达式。只需直接比较两个字符串的字符:

boolean formatCorrect(String a) {
 if (a.length() != 8) return false;
  for (int i = 0; i < 4; ++i) {
    char ca = a.charAt(i);
    if (ca != '?' && !Character.isLetter(ca)) {
      return false;
    }
  }
  for (int i = 4; i < 8; ++i) {
    char ca = a.charAt(i);
    if (ca != '?' && !Character.isDigit(ca)) {
      return false;
    }
  }
  return true;
}

boolean stringsMatch(String a, String b) {
  if (!formatCorrect(a) || !formatCorrect(b)) {
    // Handle this. Maybe an IllegalArgumentException?
  }
  for (int i = 0; i < 8; ++i) {
    char ca = a.charAt(i);
    char cb = b.charAt(i);
    if (ca != '?' && cb != '?' && ca != cb) return false;
  }
  return true;
}

这将非常快,因为它没有分配任何对象。

您可以通过将一些检查移出循环来优化它(例如,检查字符串ab的格式是正确的格式。)