java like a_如何在Java中实现类似“ LIKE”运算符的SQL?
我需要Java中的比較器,該比較器的語義與sql"贊"運算符相同。
例如:
myComparator.like("digital","%ital%");
myComparator.like("digital","%gi?a%");
myComparator.like("digital","digi%");
應該評估為真,并且
myComparator.like("digital","%cam%");
myComparator.like("digital","tal%");
應該評估為假。 關于如何實現這樣的比較器的任何想法,或者有人知道具有相同語義的實現嗎? 可以使用正則表達式嗎?
請參閱Apache Cayenne項目中的RegexUtil#sqlPatternToRegex(String)。
。*將匹配正則表達式中的任何字符
我認為Java語法是
"digital".matches(".*ital.*");
對于單個字符匹配,只需使用單個點。
"digital".matches(".*gi.a.*");
為了匹配實際的點,請將其轉為斜線點
\.
是的,謝謝!但是,如果單詞inst如此簡單,例如"%dig%",并且字符串需要一些escping鍵?已經有東西了嗎?怎么樣 ? ?
我為問號運算符編輯了答案。我對您的其余評論感到有些困惑。您是說要使用sql語法顯示字符串,并且要按原樣對其求值?如果是這種情況,我認為您將需要手動替換為sql語法。
如果用作搜索模式的字符串也包含諸如(或)的分組字符怎么辦?其他字符怎么需要轉義?
我認為這取決于您允許多少選擇。
只是要注意。*是貪婪的(。*?可能更合適)。我不認為正則表達式中的。*與SQL中的%語義完全相同。
很好,請參閱以下問題進行爆炸stackoverflow.com/questions/255815/
是的,可以使用正則表達式來完成。請記住,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);
}
是否存在一種方法,可以在Java正則表達式中轉義具有特殊含義的每個字符?
是的,Pattern.quote(java.sun.com/javase/6/docs/api/java/util/regex/)可以做到。出于某種原因,我認為這可能會導致問題,但是現在我不知道為什么不將其包括在答案中。
哦,是的,現在我記得了。這是因為 ?是一個特殊的正則表達式字符,因此在替換它之前將其轉義。我想我們可以改為使用Pattern.quote,然后使用expr = expr.replace(" \?","。");
第三行應顯示為replace(".","\\.");
你是對的。我應該在發布之前先在點上對其進行測試。
您還可以添加expr = expr.replaceAll("(?,因為"\_"可以在SQL中轉義,因此在這種情況下不應替換為"."。 (對于一個字符,我使用了_而不是?。)
另外,對于%,此替換會更好:expr = expr.replaceAll("(?
正則表達式是最通用的。但是,某些LIKE函數可以不使用正則表達式而形成。例如
String text ="digital";
text.startsWith("dig"); // like"dig%"
text.endsWith("tal"); // like"%tal"
text.contains("gita"); // like"%gita%"
我可以找到的每個SQL參考都說"任何單個字符"通配符是下劃線(_),而不是問號(?)。因為下劃線不是正則表達式元字符,所以這簡化了一些事情。但是,由于mmyers給出的原因,您仍然不能使用Pattern.quote()。當我以后想編輯正則表達式時,這里還有另一種轉義正則表達式的方法。這樣,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表達式本身的語法。
是的,你是對的。我比我更喜歡您的解決方案。
如果(s == null)拋出新的IllegalArgumentException("字符串不能為null");否則if(s.isEmpty())返回";
要在Java中實現sql的LIKE函數,您不需要在
它們可以通過以下方式獲得:
String text ="apple";
text.startsWith("app"); // like"app%"
text.endsWith("le"); // like"%le"
text.contains("ppl"); // like"%ppl%"
這本質上只是多年前發布的現有答案的重復。
該答案已經發布
您可以將'%string%'更改為contains(),將'string%'更改為startsWith(),將'%string"'更改為endsWith()。
您還應該在字符串和模式上同時運行toLowerCase(),因為LIKE是區分大小寫的。
除了使用正則表達式外,不確定如何處理'%string%other%'。
如果您使用正則表達式:
在替換%字符之前,請給字符串加引號
注意LIKE字符串中的轉義字符
什么是abot"%this%string%"?分割%符號,遍歷數組,然后檢查每個條目?我認為這可以做得更好...
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;
}
}
可能會幫助你
http://josql.sourceforge.net/具有您所需要的。查找org.josql.expressions.LikeExpression。
Apache Cayanne ORM具有"內存中的評估"
它可能不適用于未映射的對象,但看起來很有希望:
Expression exp = ExpressionFactory.likeExp("artistName","A%");
List startWithA = exp.filterObjects(artists);
您知道休眠是否支持此功能嗎?我的意思是,使用這樣的表達式過濾當前內存中的對象?
Java字符串具有.startsWith()和.contains()方法,它們將使您獲得更多收益。對于更復雜的事情,您必須使用正則表達式或編寫自己的方法。
比較器和可比較接口在這里可能不適用。它們處理排序,并返回符號或0的整數。您的操作是查找匹配項,并返回true / false。那不一樣。
歡迎您為運營商建議一個更好的名稱。我不喜歡批評家,沒有改進的建議,順便說一句。
我不完全了解貪婪的問題,但是如果它對您有用,請嘗試以下操作:
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);
}
您的內部split()和循環會替換任何?帶點的順序-我不明白。為什么要選擇該序列,而只用一個點代替它,就像一個單獨的問號?
它取代了?與。因為?是單個任意字符的占位符。我知道 \\\?看起來很奇怪,但是我進行了測試,并且對于我的測試來說似乎可行。
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++;// <<
} 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,""));
好的,這是一個奇怪的解決方案,但我認為仍然應該提到它。
無需重新創建類似的機制,我們可以利用任何數據庫中已有的現有實現!
(僅要求您的應用程序必須有權訪問任何數據庫)。
每次只運行一個非常簡單的查詢,根據同類比較的結果返回true或false。 然后執行查詢,并直接從數據庫中讀取答案!
對于Oracle數據庫:
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",并設置要檢查的值。
總結
以上是生活随笔為你收集整理的java like a_如何在Java中实现类似“ LIKE”运算符的SQL?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java上传大文件_Java超大文件上传
- 下一篇: linux死锁的例子,操作系统教程—Li