43

SQLの「like」演算子と同じセマンティクスを持つJavaのコンパレータが必要です。例えば:

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

true と評価される必要があります。

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

false と評価する必要があります。そのようなコンパレータを実装する方法についてのアイデアはありますか、または同じセマンティクスを持つ実装を知っている人はいますか? これは正規表現を使用して行うことができますか?

4

17 に答える 17

36

.* は正規表現の任意の文字と一致します

Java構文は次のようになると思います

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

単一の文字の一致には、単一のドットを使用します。

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

実際のドットに一致させるには、スラッシュドットとしてエスケープします

\.
于 2009-05-22T15:16:15.007 に答える
24

はい、これは正規表現で行うことができます。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);
}
于 2009-05-22T15:26:02.740 に答える
21

正規表現は最も汎用性があります。ただし、一部の LIKE 関数は、正規表現なしで作成できます。例えば

String text = "digital";
text.startsWith("dig"); // like "dig%"
text.endsWith("tal"); // like "%tal"
text.contains("gita"); // like "%gita%"
于 2009-07-19T13:30:41.510 に答える
14

私が見つけることができるすべてのSQL参照は、「任意の1文字」ワイルドカードが_疑問符()ではなくアンダースコア()であると述べてい?ます。アンダースコアは正規表現のメタ文字ではないため、これにより少し単純化されます。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キーワードを使用して実行されます。これにより、毎回異なるエスケープ文字を定義できます。ご想像のとおり、これは物事をかなり複雑にします。正規表現への変換はおそらく依然として最良のオプションですが、元の式の解析ははるかに困難になります。実際、最初に行う必要があるのは、LIKE-like式自体の構文を形式化することです。

于 2009-07-20T11:32:23.580 に答える
3

Java 文字列には .startsWith() および .contains() メソッドがあり、ほとんどの場合に役立ちます。より複雑な場合は、正規表現を使用するか、独自のメソッドを作成する必要があります。

于 2009-05-22T15:18:00.993 に答える
3

、 、、および'%string%'を参照できます。contains()'string%'startsWith()'%string"'endsWith()

また、大文字と小文字を区別しないtoLowerCase()ため、文字列とパターンの両方で実行する必要があります。LIKE

'%string%other%'ただし、正規表現以外でどのように処理するかはわかりません。

正規表現を使用している場合:

于 2009-05-22T15:20:08.930 に答える
3
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;
    }
}

あなたを助けるかもしれません

于 2013-01-12T14:31:35.973 に答える
2

http://josql.sourceforge.net/には必要なものがあります。org.josql.expressions.LikeExpression を探します。

于 2011-03-01T19:32:42.633 に答える
2

ComparatorおよびComparableインターフェイスは、ここでは適用できない可能性があります。それらは並べ替えを処理し、符号または 0 の整数を返します。操作は、一致を見つけて true/false を返すことです。それは違う。

于 2009-08-16T19:11:51.300 に答える
2

Apache Cayanne ORM には「メモリー内評価」があります。

マップされていないオブジェクトでは機能しない可能性がありますが、有望に見えます:

Expression exp = ExpressionFactory.likeExp("artistName", "A%");   
List startWithA = exp.filterObjects(artists); 
于 2009-07-20T20:30:06.127 に答える
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);
}
于 2009-07-19T12:42:53.880 に答える