代码之家  ›  专栏  ›  技术社区  ›  erikkallen

在JavaScript中模拟类SQL

  •  8
  • erikkallen  · 技术社区  · 17 年前

    如何模拟SQL关键字 LIKE

    对于那些不知道什么的人 喜欢 它是一个非常简单的正则表达式,只支持通配符 % _ 正好匹配一个字符。

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

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

    7 回复  |  直到 8 年前
        1
  •  11
  •   Kip    17 年前

    只要您首先对模式中的正则表达式字符进行转义,您所拥有的将起作用。下面是一个例子 Simon Willison’s blog :

    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;
    
        2
  •  4
  •   Steven de Salas Alexander Bollaert    12 年前

    我一直在寻找同一个问题的答案,在阅读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评论改进性能。

        3
  •  2
  •   Kip    17 年前

    这是我使用的一个函数,基于 PHP's preg_quote function :

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

    var match = new RegEx(regex_quote(likeExpr).replace("%", ".*").replace("_", ".")).exec(str) != null;
    
        4
  •  2
  •   JohnLock    8 年前

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

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

    下面是我将LIKE表达式转换为RegExp的函数。输入分为方括号和非方括号部分。方括号部分只需要反斜杠转义,而非方括号部分在 % _

    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');
    
        5
  •  1
  •   Joel Coehoorn    17 年前

    如果要使用正则表达式,可以将字符串的每个字符都用方括号括起来。那么您只有几个字符可以转义。

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

        6
  •  0
  •   Community Mohan Dere    9 年前

    在Chris Van Opstal的回答中,您应该使用replaceAll而不是replace来替换“%”和“"的所有发生。 参考如何进行全部替换- here

        7
  •  0
  •   richardwhitney    10 年前

    Johnny最近来过这里,但这对我很有用。我将其用于spa页面,以避免某些页面在默认页面后显示结果:

    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);
    }
    
        8
  •  0
  •   Rafi    7 年前

    我想要的东西,也处理转义通配符 % _ 使用 \% \_ .

    以下是我使用反向查找的解决方案:

    // 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
    
        9
  •  0
  •   mishamosher    5 年前

    我需要这个,在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

    strLike('Hello 🙃', 'Hello _'); // true
    strLike('Hello 🙃', '_e%o__');  // true
    strLike('asdfas \\🙃H\\\\%🙃É\\l\\_🙃\\l\\o asdfasf', '%' . escapeStrLike('\\🙃h\\\\%🙃e\\l\\_🙃\\l\\o') . '%'); // true
    
        10
  •  0
  •   pigeontoe    5 年前

    最后,我根据这里的一些答案编写了一个函数,对我来说效果很好。我需要保留“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;
    }