it-swarm.com.ru

Как реализовать SQL как оператор "LIKE" в Java?

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

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

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

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

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

35
Chris

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

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

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

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

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

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

\.
31
Bob

Да, это можно сделать с помощью регулярного выражения. Имейте в виду, что регулярные выражения 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);
}
20
Michael Myers

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

String text = "digital";
text.startsWith("Dig"); // like "Dig%"
text.endsWith("tal"); // like "%tal"
text.contains("gita"); // like "%gita%"
16
Peter Lawrey

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

11
Alan Moore

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

String text = "Apple";
text.startsWith("app"); // like "app%"
text.endsWith("le"); // like "%le"
text.contains("ppl"); // like "%ppl%"
4
Mithun Adhikari

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

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

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

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

3
Dave Webb
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
Krishnendu

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

2
job

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

2
Rich MacDonald

Apache Cayanne ORM имеет " В оценке памяти

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

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

я не знаю точно о жадной проблеме, но попробуйте это, если это работает для вас:

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

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

1
John O
public static boolean like(String source, String exp) {
        if (source == null || exp == null) {
            return false;
        }

        int sourceLength = source.length();
        int expLength = exp.length();

        if (sourceLength == 0 || expLength == 0) {
            return false;
        }

        boolean fuzzy = false;
        char lastCharOfExp = 0;
        int positionOfSource = 0;

        for (int i = 0; i < expLength; i++) {
            char ch = exp.charAt(i);

            // 是否转义
            boolean escape = false;
            if (lastCharOfExp == '\\') {
                if (ch == '%' || ch == '_') {
                    escape = true;
                    // System.out.println("escape " + ch);
                }
            }

            if (!escape && ch == '%') {
                fuzzy = true;
            } else if (!escape && ch == '_') {
                if (positionOfSource >= sourceLength) {
                    return false;
                }

                positionOfSource++;// <<<----- 往后加1
            } else if (ch != '\\') {// 其他字符,但是排查了转义字符
                if (positionOfSource >= sourceLength) {// 已经超过了source的长度了
                    return false;
                }

                if (lastCharOfExp == '%') { // 上一个字符是%,要特别对待
                    int tp = source.indexOf(ch);
                    // System.out.println("上一个字符=%,当前字符是=" + ch + ",position=" + position + ",tp=" + tp);

                    if (tp == -1) { // 匹配不到这个字符,直接退出
                        return false;
                    }

                    if (tp >= positionOfSource) {
                        positionOfSource = tp + 1;// <<<----- 往下继续

                        if (i == expLength - 1 && positionOfSource < sourceLength) { // exp已经是最后一个字符了,此刻检查source是不是最后一个字符
                            return false;
                        }
                    } else {
                        return false;
                    }
                } else if (source.charAt(positionOfSource) == ch) {// 在这个位置找到了ch字符
                    positionOfSource++;
                } else {
                    return false;
                }
            }

            lastCharOfExp = ch;// <<<----- 赋值
            // System.out.println("当前字符是=" + ch + ",position=" + position);
        }

        // expr的字符循环完了,如果不是模糊的,看在source里匹配的位置是否到达了source的末尾
        if (!fuzzy && positionOfSource < sourceLength) {
            // System.out.println("上一个字符=" + lastChar + ",position=" + position );

            return false;
        }

        return true;// 这里返回true
    }
Assert.assertEquals(true, like("abc_d", "abc\\_d"));
        Assert.assertEquals(true, like("abc%d", "abc\\%%d"));
        Assert.assertEquals(false, like("abcd", "abc\\_d"));

        String source = "1abcd";
        Assert.assertEquals(true, like(source, "_%d"));
        Assert.assertEquals(false, like(source, "%%a"));
        Assert.assertEquals(false, like(source, "1"));
        Assert.assertEquals(true, like(source, "%d"));
        Assert.assertEquals(true, like(source, "%%%%"));
        Assert.assertEquals(true, like(source, "1%_"));
        Assert.assertEquals(false, like(source, "1%_2"));
        Assert.assertEquals(false, like(source, "1abcdef"));
        Assert.assertEquals(true, like(source, "1abcd"));
        Assert.assertEquals(false, like(source, "1abcde"));

        // 下面几个case很有代表性
        Assert.assertEquals(true, like(source, "_%_"));
        Assert.assertEquals(true, like(source, "_%____"));
        Assert.assertEquals(true, like(source, "_____"));// 5个
        Assert.assertEquals(false, like(source, "___"));// 3个
        Assert.assertEquals(false, like(source, "__%____"));// 6个
        Assert.assertEquals(false, like(source, "1"));

        Assert.assertEquals(false, like(source, "a_%b"));
        Assert.assertEquals(true, like(source, "1%"));
        Assert.assertEquals(false, like(source, "d%"));
        Assert.assertEquals(true, like(source, "_%"));
        Assert.assertEquals(true, like(source, "_abc%"));
        Assert.assertEquals(true, like(source, "%d"));
        Assert.assertEquals(true, like(source, "%abc%"));
        Assert.assertEquals(false, like(source, "ab_%"));

        Assert.assertEquals(true, like(source, "1ab__"));
        Assert.assertEquals(true, like(source, "1ab__%"));
        Assert.assertEquals(false, like(source, "1ab___"));
        Assert.assertEquals(true, like(source, "%"));

        Assert.assertEquals(false, like(null, "1ab___"));
        Assert.assertEquals(false, like(source, null));
        Assert.assertEquals(false, like(source, ""));
0
test love