12

如何LIKE在 JavaScript 中模拟 SQL 关键字?

对于那些不知道是什么的人LIKE来说,这是一个非常简单的正则表达式,它只支持通配符%,它​​匹配 0 个或多个字符,并且_只匹配一个字符。

但是,不仅可以执行以下操作:

var match = new RegEx(likeExpr.replace("%", ".*").replace("_", ".")).exec(str) != null;

...因为该模式可能包含点、星和任何其他特殊的正则表达式字符。

4

10 回答 10

11

只要您首先转义模式中的正则表达式字符,您所拥有的就会起作用。以下是Simon Willison 博客中的一个示例:

RegExp.escape = function(text) {
  if (!arguments.callee.sRE) {
    var specials = [
      '/', '.', '*', '+', '?', '|',
      '(', ')', '[', ']', '{', '}', '\\'
    ];
    arguments.callee.sRE = new RegExp(
      '(\\' + specials.join('|\\') + ')', 'g'
    );
  }
  return text.replace(arguments.callee.sRE, '\\$1');
}

然后,您可以将代码实现为:

likeExpr = RegExp.escape(likeExpr);
var match = new RegEx(likeExpr.replace("%", ".*").replace("_", ".")).exec(str) != null;
于 2009-08-21T20:35:37.233 回答
4

我正在寻找相同问题的答案,并在阅读了 Kip 的回复后提出了这个问题:

String.prototype.like = function(search) {
    if (typeof search !== 'string' || this === null) {return false; }
    // Remove special chars
    search = search.replace(new RegExp("([\\.\\\\\\+\\*\\?\\[\\^\\]\\$\\(\\)\\{\\}\\=\\!\\<\\>\\|\\:\\-])", "g"), "\\$1");
    // Replace % and _ with equivalent regex
    search = search.replace(/%/g, '.*').replace(/_/g, '.');
    // Check matches
    return RegExp('^' + search + '$', 'gi').test(this);
}

然后您可以按如下方式使用它(注意它忽略了大写/小写):

var url = 'http://www.mydomain.com/page1.aspx';
console.log(url.like('%mydomain.com/page_.asp%')); // true

注意 29/11/2013:RegExp.test()根据下面的 Lucios 评论更新了性能改进。

于 2013-08-24T12:05:30.430 回答
2

这是我使用的一个函数,基于PHP 的 preg_quote 函数

function regex_quote(str) {
  return str.replace(new RegExp("([\\.\\\\\\+\\*\\?\\[\\^\\]\\$\\(\\)\\{\\}\\=\\!\\<\\>\\|\\:\\-])", "g"), "\\$1");
}

所以你的行现在是:

var match = new RegEx(regex_quote(likeExpr).replace("%", ".*").replace("_", ".")).exec(str) != null;
于 2009-08-21T20:51:29.273 回答
2

一个老问题,但这里实际上没有好的答案。TSQL LIKE 表达式可以包含方括号转义部分,这些部分已经是几乎有效的正则表达式并允许匹配%_。例如:

'75%' LIKE '75[%]'
'[foo]' LIKE '[[]foo]' -- ugh

这是我将 LIKE 表达式转换为 RegExp 的函数。输入分为方括号和非方括号部分。方括号部分只需要反斜杠转义,非方括号部分完全转义,而%and_指令转换为正则表达式。

const likeRegExp = (expression, caseSensitive = false) =>
    new RegExp(`^${
        expression.split(/(\[.+?\])/g)
        .map((s, i) => i % 2 ?
            s.replace(/\\/g, '\\\\') :
            s.replace(/[-\/\\^$*+?.()|[\]{}%_]/g, m => {
                switch(m) {
                    case '%': return '.*';
                    case '_': return '.';
                    default: return `\\${m}`;
                }
            })
        ).join('')
    }$`, caseSensitive ? '' : 'i');
于 2018-05-23T10:03:08.137 回答
1

如果要使用正则表达式,可以将字符串的每个字符括在方括号中。那么你只有几个字符可以逃脱。

但更好的选择可能是截断目标字符串,以便长度与您的搜索字符串匹配并检查是否相等。

于 2009-08-21T20:32:59.140 回答
0

在 Chris Van Opstal 的回答中,您应该使用 replaceAll 而不是 replace 替换所有出现的“%”和“_”。参考如何做 replaceAll -这里

于 2014-02-04T05:25:41.713 回答
0

约翰尼最近来到这里,但这对我有用,我将它用于我的水疗页面,以避免某些页面在默认页面之后显示结果:

function like(haystack,needle){
    needle = needle.split(','); 
    var str = haystack.toLowerCase();
    var n = -1;
    for(var i=0;i<needle.length;i++){
        n = str.search(needle[i]);
        if(n > -1){
            return n;
        }
    }
return n;
}

用法是 - 在这里我不想在工具、联系人或主页上显示任何结果 - results() 是一个我不在这里显示的函数:

var n = like($data,'tools,contact,home');
//~ alert(n);
if(n < 0){// does not match anything in the above string
  results($data);
}
于 2016-05-14T06:30:47.123 回答
0

我想要一些也可以处理转义通配符%_使用\%and的东西\_

这是我使用负面后视的解决方案:

// escapes RegExp special characters
const escapePattern = s => s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');

// converts ILIKE pattern to a RegExp object
const ilikeToRegExp = pattern =>
  new RegExp(
    `^${escapePattern(pattern)}$`
      // convert ILIKE wildcards, don't match escaped
      .replace(/(?<![\\])%/g, '.*')
      .replace(/(?<![\\])_/g, '.')
      // replace ILIKE escapes
      .replace(/\\%/g, '%')
      .replace(/\\_/g, '_'),
    'i'
  );

用法:

ilikeToRegExp('%eLlo WoR%').test('hello world')  
// true

ilikeToRegExp('ello wor').test('hello world')  
// false

ilikeToRegExp('%90\%%').test('...90%...') 
// true
于 2019-05-31T09:13:31.057 回答
0

我需要这个,在 Safari 中转义和工作(没有负面的后视)。这是我想出的:

/**
 * Quotes a string following the same rules as https://www.php.net/manual/function.preg-quote.php
 *
 * Sourced from https://locutus.io/php/preg_quote/
 *
 * @param {string} str String to quote.
 * @param {?string} [delimiter] Delimiter to also quote.
 * @returns {string} The quoted string.
 */
function regexQuote(str, delimiter) {
    return (str + '').replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\' + (delimiter || '') + '-]', 'g'), '\\$&');
}

/**
 * Removes the diacritical marks from a string.
 *
 * Diacritical marks: {@link https://unicode-table.com/blocks/combining-diacritical-marks/}
 *
 * @param {string} str The string from which to strip the diacritical marks.
 * @returns {string} Stripped string.
 */
function stripDiacriticalMarks(str) {
    return unorm.nfkd(str).replaceAll(/[\u0300-\u036f]+/g, '');
}

/**
 * Checks if the string `haystack` is like `needle`, `needle` can contain '%' and '_'
 * characters which will behave as if used in a SQL LIKE condition. Character escaping
 * is supported with '\'.
 *
 * @param {string} haystack The string to check if it is like `needle`.
 * @param {string} needle The string used to check if `haystack` is like it.
 * @param {boolean} [ai] Whether to check likeness in an accent-insensitive manner.
 * @param {boolean} [ci] Whether to check likeness in a case-insensitive manner.
 * @returns {boolean} True if `haystack` is like `needle`, otherwise, false.
 */
function strLike(haystack, needle, ai = true, ci = true) {
    if (ai) {
        haystack = stripDiacriticalMarks(haystack);
        needle = stripDiacriticalMarks(needle);
    }

    needle = regexQuote(needle, '/');

    let tokens = [];

    for (let i = 0; i < needle.length; ) {
        if (needle[i] === '\\') {
            i += 2;
            if (i < needle.length) {
                if (needle[i] === '\\') {
                    tokens.push('\\\\');
                    i += 2;
                } else {
                    tokens.push(needle[i]);
                    ++i;
                }
            } else {
                tokens.push('\\\\');
            }
        } else {
            switch (needle[i]) {
                case '_':
                    tokens.push('.')
                    break;
                case '%':
                    tokens.push('.*')
                    break;
                default:
                    tokens.push(needle[i]);
                    break;
            }
            ++i;
        }
    }

    return new RegExp(`^${tokens.join('')}$`, `u${ci ? 'i' : ''}`).test(haystack);
}

/**
 * Escapes a string in a way that `strLike` will match it as-is, thus '%' and '_'
 * would match a literal '%' and '_' respectively (and not behave as in a SQL LIKE
 * condition).
 *
 * @param {string} str The string to escape.
 * @returns {string} The escaped string.
 */
function escapeStrLike(str) {
    let tokens = [];

    for (let i = 0; i < str.length; i++) {
        switch (str[i]) {
            case '\\':
                tokens.push('\\\\');
                break;
            case '%':
                tokens.push('\\%')
                break;
            case '_':
                tokens.push('\\_')
                break;
            default:
                tokens.push(str[i]);
        }
    }

    return tokens.join('');
}

上面的代码依赖于unorm,并且可以识别 unicode 以捕获以下情况:

strLike('Hello ', 'Hello _'); // true
strLike('Hello ', '_e%o__');  // true
strLike('asdfas \\H\\\\%É\\l\\_\\l\\o asdfasf', '%' . escapeStrLike('\\h\\\\%e\\l\\_\\l\\o') . '%'); // true
于 2020-10-10T15:51:28.277 回答
0

我最终根据这里的一些答案编写了一个对我来说效果很好的函数。我需要一些可以保留“startswith%”和“%endswith”语法的东西,并且不返回空搜索字符串的匹配项。

function sqlLIKE(target, likeExp) {
  let regex = likeExp
    .replaceAll(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1')
    .replaceAll("%", ".*")
    .replaceAll("_", ".");

  if (likeExp.charAt(0) !== '%' || !likeExp.includes('%')) regex = `^${regex}`;

  if (likeExp.charAt(likeExp.length - 1) !== '%' || !likeExp.includes('%')) regex = `${regex}$`;

  return new RegExp(regex).exec(target) !== null;
}
于 2021-05-25T16:14:18.580 回答