如何在java中实现类似'LIKE'运算符的SQL?

Chr*_*ris 37 java regex sql string wildcard

我需要一个java中的比较器,它与sql'like'运算符具有相同的语义.例如:

myComparator.like("digital","%ital%");
myComparator.like("digital","%gi?a%");
myComparator.like("digital","digi%");
Run Code Online (Sandbox Code Playgroud)

应评估为真,并且

myComparator.like("digital","%cam%");
myComparator.like("digital","tal%");
Run Code Online (Sandbox Code Playgroud)

应评估为假.任何想法如何实现这样的比较器或任何人都知道具有相同语义的实现?可以使用正则表达式完成吗?

Bob*_*Bob 33

.*将匹配正则表达式中的任何字符

我认为java语法会是

"digital".matches(".*ital.*");
Run Code Online (Sandbox Code Playgroud)

对于单个字符匹配,只需使用一个点.

"digital".matches(".*gi.a.*");
Run Code Online (Sandbox Code Playgroud)

并匹配一个实际的点,将其作为斜线点逃脱

\.
Run Code Online (Sandbox Code Playgroud)


Mic*_*ers 22

是的,这可以通过正则表达式完成.请记住,Java的正则表达式与SQL的"like"具有不同的语法.而不是" %",你会有" .*",而不是" ?",你会有" .".

让它有点棘手的是你还必须逃避Java认为特殊的任何字符.既然你试图使这类似于SQL,我猜测不^$[]{}\应该出现在正则表达式字符串中.但在进行任何其他替换之前,您必须将" ." 替换为" " \\..(编辑: Pattern.quote(String)通过用" \Q"和" \E" 包围字符串来逃避一切,这将导致表达式中的所有内容被视为文字(根本没有通配符).所以你绝对不想使用它.)

此外,正如Dave Webb所说,你还需要忽略案例.

考虑到这一点,这里有一个示例:

public static boolean like(String str, String expr) {
    expr = expr.toLowerCase(); // ignoring locale for now
    expr = expr.replace(".", "\\."); // "\\" is escaped to "\" (thanks, Alan M)
    // ... escape any other potentially problematic characters here
    expr = expr.replace("?", ".");
    expr = expr.replace("%", ".*");
    str = str.toLowerCase();
    return str.matches(expr);
}
Run Code Online (Sandbox Code Playgroud)


Pet*_*rey 19

正则表达式是最通用的.但是,可以在没有正则表达式的情况下形成一些LIKE函数.例如

String text = "digital";
text.startsWith("dig"); // like "dig%"
text.endsWith("tal"); // like "%tal"
text.contains("gita"); // like "%gita%"
Run Code Online (Sandbox Code Playgroud)


Ala*_*ore 12

我能找到的每个SQL引用都说"任何单个字符"通配符是下划线(_),而不是问号(?).这简化了一些事情,因为下划线不是正则表达式元字符.但是,Pattern.quote()由于mmyers给出的理由,你仍然无法使用.我有另一种方法来逃避正则表达式,之后我可能想要编辑它们.除此之外,该like()方法变得非常简单:

public static boolean like(final String str, final String expr)
{
  String regex = quotemeta(expr);
  regex = regex.replace("_", ".").replace("%", ".*?");
  Pattern p = Pattern.compile(regex,
      Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
  return p.matcher(str).matches();
}

public static String quotemeta(String s)
{
  if (s == null)
  {
    throw new IllegalArgumentException("String cannot be null");
  }

  int len = s.length();
  if (len == 0)
  {
    return "";
  }

  StringBuilder sb = new StringBuilder(len * 2);
  for (int i = 0; i < len; i++)
  {
    char c = s.charAt(i);
    if ("[](){}.*+?$^|#\\".indexOf(c) != -1)
    {
      sb.append("\\");
    }
    sb.append(c);
  }
  return sb.toString();
}
Run Code Online (Sandbox Code Playgroud)

如果您真的想要使用?通配符,最好的办法是将其从quotemeta()方法中的元字符列表中删除.替换其转义形式 - replace("\\?", ".")- 不安全,因为原始表达式中可能存在反斜杠.

这就把我们带到了真正的问题:大多数SQL风格似乎都支持表单中的字符类[a-z]和/ [^j-m][!j-m]它们都提供了一种逃避通配符的方法.后者通常通过ESCAPE关键字完成,该关键字允许您每次定义不同的转义字符.可以想象,这使事情变得相当复杂.转换为正则表达式可能仍然是最好的选择,但解析原始表达式会更加困难 - 实际上,您要做的第一件事就是将LIKE类似表达式本身的语法形式化.


Mit*_*ari 5

要在Java中实现sql的LIKE函数,您无需在中使用正则表达式。它们可以通过以下方式获得:

String text = "apple";
text.startsWith("app"); // like "app%"
text.endsWith("le"); // like "%le"
text.contains("ppl"); // like "%ppl%"
Run Code Online (Sandbox Code Playgroud)

  • 本质上,这只是[许多年前发布的现有答案](/sf/answers/80493381/)的重复。 (2认同)
  • 哦真的吗?如果文本是“我喜欢苹果但不喜欢橙子”并且搜索类似于“%oranges%apples%”怎么办? (2认同)