2009-05-22 54 views
34

我需要在Java具有相同的语义“喜欢”操作者的SQL的比较器。 例如:如何实现像“喜欢”在java中操作一个SQL?

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

应该评估为真,并

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

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

回答

30

。*将匹配任何字符在正则表达式

我认为Java的语法将

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

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

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

而为了配合实际点,逃避它作为斜线点

\. 
+0

是的,谢谢!但是,如果这个单词不像“%dig%”那么简单,而且字符串需要一些保存?有什么已经存在的吗?关于 '?' ? – Chris 2009-05-22 15:21:56

+0

我编辑了问号运算符的答案。尽管如此,我对你的评论的其余部分有点困惑。你是说这个字符串以sql语法来到你身上,并且你想按照原样评估它吗?如果是这种情况,我认为你需要手动替换为sql语法。 – Bob 2009-05-22 15:24:36

+0

如果用作搜索模式的字符串包含像'('或')'这样的分组字符,该怎么办?其他角色如何逃脱? – Chris 2009-05-22 15:25:27

2

Java字符串有.startsWith()及。载有(),这将让你最的方式方法。对于任何更复杂的事情,你必须使用正则表达式或编写自己的方法。

2

你可以把'%string%'contains()'string%'startsWith()'%string"'endsWith()

您还应该对字符串和模式都运行toLowerCase(),因为LIKE是不区分大小写的。

不知道你会如何处理'%string%other%'除非虽然正则表达式。

如果您使用正则表达式:

+0

what abot“%this%string%”?拆分'%'符号,迭代数组并检查每个条目?我认为这可能会做得更好... – Chris 2009-05-22 15:23:56

18

是的,这可以用正则表达式来完成。请记住,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); 
} 
+0

那里存在一种方法,它在Java正则表达式中转义每一个特殊含义的字符? – Chris 2009-05-22 15:28:52

1

我不知道到底对贪婪的问题,但是试试这个,如果你的作品:

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); 
} 
12

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

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

每个SQL参考,我可以找到说,“任何单个字符”通配符是下划线(_),而不是问号(?)。这简化了一些事情,因为下划线不是正则表达式元字符。但是,由于mmyers给出的原因,您仍然不能使用Pattern.quote()。我在这里有另一种方法来转义正则表达式,因为之后我可能想要编辑这些正则表达式。有了这样的方式,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("\\?", ".") - 将不安全,因为原始表达式中可能存在反斜杠。

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

2

阿帕奇Cayanne ORM有一个 “In memory evaluation

它可能不适合未映射对象工作,但看起来很有希望:

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

ComparatorComparable接口很可能不适用在这里。它们处理排序,并返回符号或0的整数。您的操作是关于查找匹配项,并返回true/false。那不一样。

1
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; 
    } 
} 

可能帮助你

0

我解决了使用Java 8中的问题,在接下来的代码下面

public List<String> search(String value) { 

    return listaPersonal.stream() 
         .filter(p->(p.toUpperCase()).startsWith(value.toUpperCase())) 
         .collect(Collectors.toList()); 
} 
2

要实现像Java中你不需要正则表达式的SQL的功能在 他们可以获得如下:

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