如何在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化类LIKE
expression式本身的语法。
Javastring有.startsWith()和.contains()方法,这些方法可以帮你实现。 对于任何更复杂的事情,你必须使用正则expression式或编写自己的方法。
您可以将'%string%'
contains()
, 'string%'
'%string"'
作为endsWith()
startsWith()
和'%string"'
。
您也应该在string和模式上运行toLowerCase()
,因为LIKE
不区分大小写。
不知道如何处理'%string%other%'
除了正则expression式。
如果您使用正则expression式:
- 在replace
%
字符之前引用该string - 注意
LIKE
string中的转义字符
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()); }