2009-05-22 3 views
34

Мне нужен компаратор в java, который имеет ту же семантику, что и оператор sql 'like'. Например:Как реализовать SQL как «LIKE» в java?

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

должны оценить истинно, и

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

должны оценить ложь. Любые идеи о том, как реализовать такой компаратор, или кто-либо знает реализацию с той же семантикой? Можно ли это сделать с помощью регулярного выражения?

ответ

30

. * Будет соответствовать символы в регулярных выражениях

Я думаю, что синтаксис Java будет

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

И единственный матч персонаж просто использовать одну точку.

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

И чтобы соответствовать фактической точки, бежать как слэш точка

\. 
+0

Да, спасибо! Но в случае, если слово не так просто, как «% dig%», а строка требует некоторого escping? Есть ли что-то уже выходящее? Что насчет '?' ? – Chris

+0

Я отредактировал свой ответ для оператора вопросительного знака. Я немного смущен остальными вашими комментариями. Вы говорите, что строка подходит вам в синтаксисе sql, и вы хотите оценить ее как есть? Если это так, я думаю, вам нужно будет заменить синтаксис sql вручную. – Bob

+0

Что делать, если строка, которая используется в качестве шаблона поиска, содержит группировку символов, таких как '(' или ')', чтобы избежать их? как mayn другие персонажи должны убегать? – Chris

2

Java строки имеют .startsWith() и .Contains() методы, которые помогут вам большую часть пути. Для чего-то более сложного вам придется использовать регулярное выражение или написать собственный метод.

2

Вы можете обратиться к '%string%'contains(), 'string%' к startsWith() и '%string"' к endsWith().

Вы также должны запустить toLowerCase() как для строки, так и для рисунка, так как LIKE является вложенным в регистр.

Не знаете, как обращаться с '%string%other%', за исключением регулярного выражения.

Если вы используете регулярные выражения:

+0

Что такое abot "% this% string%"? разделить на знак «%», перебрать массив и проверить каждую запись? Я думаю, что это может быть сделано лучше ... – Chris

18

Да, это может быть сделано с помощью регулярного выражения. Имейте в виду, что регулярные выражения Java имеют различный синтаксис из «похожих» SQL. Вместо «%» у вас будет «.*», а вместо «?» у вас будет «.».

Что делает его несколько сложным, так это то, что вам также придется скрывать любые символы, которые Java рассматривает как особые. Поскольку вы пытаетесь сделать это аналогичным SQL, я предполагаю, что ^$[]{}\ не должен появляться в строке регулярного выражения. Но вам придется заменить «.» на «\\.» перед выполнением любых других замен. (Edit:Pattern.quote(String) ускользает все, окружив строку с «\Q» и «\E», который заставит все в выражении следует рассматривать как буквальное (без маски вообще) Так что вы определенно не хотят. использовать его.)

Кроме того, как говорит Дейв Вебб, вам также необходимо игнорировать случай.

Имея это в виду, вот пример того, что она может выглядеть следующим образом:

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

существует метод, который ускоряет каждый charachter со специальным значением в java regex? – Chris

+1

Да, Pattern.quote (http://java.sun.com/javase/6/docs/api/java/util/regex/Pattern.html#quote%28java.lang.String%29) сделает это. По какой-то причине я думал, что это может вызвать проблему, но теперь я не знаю, почему я не включил ее в ответ. –

+0

О да, теперь я помню. Это потому что ? является специальным символом регулярного выражения, поэтому его можно было бы избежать, прежде чем мы могли бы его заменить. Я полагаю, мы могли бы использовать Pattern.quote, а затем expr = expr.replace ("\\?", "."); –

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); 
} 
+0

Ваш внутренний split() и loop заменяет любые \? последовательность с точкой - я этого не понимаю. Зачем выделять эту последовательность, только чтобы заменить ее точкой точно так же, как одинокий вопросительный знак? –

+0

он заменяет '?' с '.' потому как '?' является держателем места для одного произвольного символа. я знаю '\\\\\\?' выглядит странно, но я тестировал его, и для моих тестов он работает. – tommyL

12

Регулярные выражения являются самыми универсальными. Однако некоторые функции LIKE могут быть сформированы без регулярных выражений. например

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

Каждый 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(); 
} 

Если вы действительно хотите использовать ? для шаблона, лучше всего было бы, чтобы удалить его из списка метасимволов в методе quotemeta(). Замена его экранированной формы - replace("\\?", ".") - не будет безопасной, поскольку в исходном выражении могут быть обратные косые черты.

И это подводит нас к реальным проблемам: большинство SQL ароматизаторов, кажется, поддерживают классы символов в формах [a-z] и [^j-m] или [!j-m], и все они обеспечивают способ избежать подстановочных символов. Последнее обычно выполняется с помощью ключевого слова ESCAPE, которое позволяет каждый раз определять другой escape-символ. Как вы можете себе представить, это усложняет ситуацию. Преобразование в регулярное выражение, вероятно, по-прежнему является лучшим вариантом, но синтаксический анализ исходного выражения будет намного сложнее - на самом деле, первое, что вам нужно сделать, это формализовать синтаксис самих выражений LIKE.

+0

Да, вы правы. Мне нравится ваше решение лучше, чем у меня. – tommyL

+0

if (s == null) throw new IllegalArgumentException ("String не может быть null"); else if (s.isEmpty()) return ""; – Leo

2

Apache Cayanne ОРМ имеет "In memory evaluation"

Он не может работать для неотображенного объекта, но выглядит многообещающе:

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

Знаете ли вы, поддерживает ли hibernate эту функцию? я имею в виду, чтобы фильтровать объекты, находящиеся в настоящее время в памяти, с помощью такого выражения? – tommyL

1

Comparator В и Comparable интерфейсов, скорее всего, здесь неприменимы. Они занимаются сортировкой и возвращают целые числа любого знака, или 0. Ваша операция заключается в поиске совпадений и возврате true/false. Это другое.

+0

, вы можете предложить лучшее имя для оператора. Мне не нравятся критики без предложений об улучшениях, кстати. – Chris

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

Для реализации LIKE функций SQL в Java, вам не нужно регулярное выражение в Они могут быть получены как:

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

Это, по сути, просто повторение [это существующие ответы, опубликованные много лет назад] (https://stackoverflow.com/a/1149905). – Pang

Смежные вопросы