Как реализовать SQL как оператор «LIKE» в Java? - PullRequest
36 голосов
/ 22 мая 2009

Мне нужен компаратор в Java, который имеет ту же семантику, что и оператор sql 'like'. Например:

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

следует оценить как истинное, а

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

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

Ответы [ 14 ]

32 голосов
/ 22 мая 2009

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

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

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

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

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

И чтобы соответствовать реальной точке, экранируйте ее как косую точку

\.
21 голосов
/ 22 мая 2009

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

Что делает его несколько сложным, так это то, что вам также придется избегать любых символов, которые 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);
}
18 голосов
/ 19 июля 2009

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

String text = "digital";
text.startsWith("dig"); // like "dig%"
text.endsWith("tal"); // like "%tal"
text.contains("gita"); // like "%gita%"
11 голосов
/ 20 июля 2009

Каждая ссылка на 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 -подобных выражений.

4 голосов
/ 24 июня 2017

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

String text = "apple";
text.startsWith("app"); // like "app%"
text.endsWith("le"); // like "%le"
text.contains("ppl"); // like "%ppl%"
3 голосов
/ 22 мая 2009

Вы можете включить '%string%' в contains(), 'string%' в startsWith() и '%string"' в endsWith().

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

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

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

2 голосов
/ 12 января 2013
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;
    }
}

может быть вам поможет

2 голосов
/ 01 марта 2011

http://josql.sourceforge.net/ есть то, что вам нужно. Ищите org.josql.expressions.LikeExpression.

2 голосов
/ 21 июля 2009

Apache Cayanne ORM имеет « в оценке памяти »

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

Expression exp = ExpressionFactory.likeExp("artistName", "A%");   
List startWithA = exp.filterObjects(artists); 
2 голосов
/ 22 мая 2009

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

...