Подтвердить что ты не робот

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

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

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

должен оцениваться как true, а

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

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

4b9b3361

Ответ 1

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

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

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

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

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

И чтобы соответствовать фактической точке, убегите в ней как косая черта

\.

Ответ 2

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

Что делает его несколько сложным, так это то, что вам также придется избегать любых символов, которые Java рассматривает как особые. Поскольку вы пытаетесь сделать это аналогичным SQL, я предполагаю, что ^$[]{}\ не должен появляться в строке регулярного выражения. Но вам придется заменить "." на "\\." перед выполнением любых других замен. ( Изменить: 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);
}

Ответ 3

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

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

Ответ 4

Каждая ссылка 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.

Ответ 5

Вы можете превратить '%string%' в contains(), 'string%' в startsWith() и '%string"' в endsWith().

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

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

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

Ответ 6

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

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

Ответ 7

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

Ответ 8

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

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

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

Ответ 10

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

может помочь вам

Ответ 11

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

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

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

Ответ 13

Я решил эту проблему с использованием Java 8 в следующем коде ниже

public List<String> search(String value) {

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

Ответ 14

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, ""));

Ответ 15

Ладно, это немного странное решение, но я подумал, что все же стоит упомянуть.

Вместо воссоздания подобного механизма мы можем использовать существующую реализацию, уже доступную в любой базе данных!

(Единственное требование - ваше приложение должно иметь доступ к любой базе данных).

Просто каждый раз запускайте очень простой запрос, который возвращает истину или ложь в зависимости от результата подобного сравнения. Затем выполните запрос и прочитайте ответ прямо из базы данных!

Для Oracle db:

SELECT
CASE 
     WHEN 'StringToSearch' LIKE 'LikeSequence' THEN 'true'
     ELSE 'false'
 END test
FROM dual 

Для MS SQL Server

SELECT
CASE 
     WHEN 'StringToSearch' LIKE 'LikeSequence' THEN 'true'
     ELSE 'false'
END test

Все, что вам нужно сделать, это заменить "StringToSearch" и "LikeSequence" параметрами привязки и установить значения, которые вы хотите проверить.