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

我需要一个在java中的比较器,它具有与sql“like”操作符相同的语义。 例如:

myComparator.like("digital","%ital%"); myComparator.like("digital","%gi?a%"); myComparator.like("digital","digi%"); 

应该评估为真实的

 myComparator.like("digital","%cam%"); myComparator.like("digital","tal%"); 

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

。*将匹配正则expression式中的任何字符

我认为Java语法将是

 "digital".matches(".*ital.*"); 

而对于单个字符匹配只需使用一个点。

 "digital".matches(".*gi.a.*"); 

并匹配一个实际的点,逃避它作为斜线点

 \. 

是的,这可以用正则expression式来完成。 请记住,Java的正则expression式与SQL的“like”有不同的语法。 你可以用“ .* ”代替“ ? ”,而不用“ ? ”。

有些棘手的是,你也必须逃避Java认为特殊的字符。 既然你试图做类似于SQL,我猜猜^$[]{}\不应该出现在正则expression式string。 但是,在做任何其他replace之前,您必须将“ . ”replace为“ \\. ”。 ( 编辑: Pattern.quote(String)通过围绕string“ \Q ”和“ \E ”来转义所有东西,这将导致expression式中的所有内容都被当作文字(根本不使用通配符)。不想使用它。)

此外,正如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); } 

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

 String text = "digital"; text.startsWith("dig"); // like "dig%" text.endsWith("tal"); // like "%tal" text.contains("gita"); // like "%gita%" 

我可以find的每个SQL引用都说“任何单个字符”通配符是下划线( _ ),而不是问号( ? )。 这简化了一些事情,因为下划线不是正则expression式元字符。 但是,你仍然不能使用Pattern.quote() ,因为mmyers给出的原因。 我在这里有另一种方法来逃避正则expression式,当我可能要编辑它们之后。 用这个方法, 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(); } 

如果你真的想用? 对于通配符,最好的办法是从quotemeta()方法中的元字符列表中删除它。 replace其转义forms – replace("\\?", ".") – 将是不安全的,因为原始expression式中可能有反斜杠。

这就给我们带来了真正的问题:大多数SQL风格似乎都支持forms为[az][^jm][!jm]字符类,它们都提供了一种转义通配符的方法。 后者通常是通过一个ESCAPE关键字来完成的,它可以让你每次定义一个不同的转义字符。 正如你所想象的,这使事情变得相当复杂。 转换为正则expression式可能仍然是最好的select,但parsing原始expression式会更困难 – 事实上,首先要做的就是forms化类LIKEexpression式本身的语法。

Javastring有.startsWith()和.contains()方法,这些方法可以帮你实现。 对于任何更复杂的事情,你必须使用正则expression式或编写自己的方法。

您可以将'%string%' contains()'string%' '%string"'作为endsWith() startsWith()'%string"'

您也应该在string和模式上运行toLowerCase() ,因为LIKE不区分大小写。

不知道如何处理'%string%other%'除了正则expression式。

如果您使用正则expression式:

  • 在replace%字符之前引用该string
  • 注意LIKEstring中的转义字符

Apache Cayanne ORM有一个“ 内存评估 ”

它可能不适用于未映射的对象,但看起来很有希望:

 Expression exp = ExpressionFactory.likeExp("artistName", "A%"); List startWithA = exp.filterObjects(artists); 

http://josql.sourceforge.net/有你所需要的。; 寻找org.josql.expressions.LikeExpression。

要在java中实现sql的LIKE函数,你不需要正则expression式,它们可以被获得为:

 String text = "apple"; text.startsWith("app"); // like "app%" text.endsWith("le"); // like "%le" text.contains("ppl"); // like "%ppl%" 

我不知道这个贪婪的问题,但是如果它适合你,请试试这个:

 public boolean like(final String str, String expr) { final String[] parts = expr.split("%"); final boolean traillingOp = expr.endsWith("%"); expr = ""; for (int i = 0, l = parts.length; i < l; ++i) { final String[] p = parts[i].split("\\\\\\?"); if (p.length > 1) { for (int y = 0, l2 = p.length; y < l2; ++y) { expr += p[y]; if (i + 1 < l2) expr += "."; } } else { expr += parts[i]; } if (i + 1 < l) expr += "%"; } if (traillingOp) expr += "%"; expr = expr.replace("?", "."); expr = expr.replace("%", ".*"); return str.matches(expr); } 

Comparator和Comparable接口在这里可能不适用。 他们处理sorting,并返回任何符号或0的整数。您的操作是关于find匹配,并返回true / false。 那不一样。

 public static boolean like(String toBeCompare, String by){ if(by != null){ if(toBeCompare != null){ if(by.startsWith("%") && by.endsWith("%")){ int index = toBeCompare.toLowerCase().indexOf(by.replace("%", "").toLowerCase()); if(index < 0){ return false; } else { return true; } } else if(by.startsWith("%")){ return toBeCompare.endsWith(by.replace("%", "")); } else if(by.endsWith("%")){ return toBeCompare.startsWith(by.replace("%", "")); } else { return toBeCompare.equals(by.replace("%", "")); } } else { return false; } } else { return false; } } 

可能会帮助你

我在下面的代码中使用Java 8解决了这个问题

 public List<String> search(String value) { return listaPersonal.stream() .filter(p->(p.toUpperCase()).startsWith(value.toUpperCase())) .collect(Collectors.toList()); }