【问题标题】:Wildcard string comparison in JavascriptJavascript中的通配符字符串比较
【发布时间】:2014-12-02 12:07:53
【问题描述】:

假设我有一个包含许多字符串的数组,称为 "birdBlue""birdRed" 和其他一些动物,如 "pig1""pig2")。

现在我运行一个遍历数组并返回所有鸟的 for 循环。什么比较在这里有意义?

Animals == "bird*" 是我的第一个想法,但不起作用。有没有办法使用运算符 * (或者有类似使用的东西?

【问题讨论】:

标签: javascript string comparison


【解决方案1】:

当你有一个rule 通配符字符串和一个text 字符串要匹配时:

function wcMatch(rule, text) {
  return (new RegExp('^' + rule.replaceAll(/([.+?^=!:${}()|\[\]\/\\])/g, "\\$1").replaceAll('*', '(.*)') + '$')).test(text)
}

第一个replaceAll 转义特殊字符,第二个用(.*) 替换*(表达式表示“任何零个或多个字符”)

例如,一个字符串*&utm_*会被转换成一个表达式/^(.*)\&utm_(.*)$/

【讨论】:

    【解决方案2】:

    我使用 @Spenhouetanswer 并添加了比“*”更多的“替换”可能性。例如 ”?”。只需将您的需求添加到 replaceHelper 中的字典即可。

    /**
     * @param {string} str
     * @param {string} rule
     * checks match a string to a rule
     * Rule allows * as zero to unlimited numbers and ? as zero to one character
     * @returns {boolean}
     */
    function matchRule(str, rule) {
      const escapeRegex = (str) => str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
      return new RegExp("^" + replaceHelper(rule, {"*": "\\d*", "?": ".?"}, escapeRegex) + "$").test(str);
    }
    
    function replaceHelper(input, replace_dict, last_map) {
      if (Object.keys(replace_dict).length === 0) {
        return last_map(input);
      }
      const split_by = Object.keys(replace_dict)[0];
      const replace_with = replace_dict[split_by];
      delete replace_dict[split_by];
      return input.split(split_by).map((next_input) => replaceHelper(next_input, replace_dict, last_map)).join(replace_with);
    }
    

    【讨论】:

      【解决方案3】:

      该函数将通配符转换为正则表达式并进行测试(支持.*通配符)

      function wildTest(wildcard, str) {
        let w = wildcard.replace(/[.+^${}()|[\]\\]/g, '\\$&'); // regexp escape 
        const re = new RegExp(`^${w.replace(/\*/g,'.*').replace(/\?/g,'.')}$`,'i');
        return re.test(str); // remove last 'i' above to have case sensitive
      }
      

      function wildTest(wildcard, str) {
        let w = wildcard.replace(/[.+^${}()|[\]\\]/g, '\\$&'); // regexp escape 
        const re = new RegExp(`^${w.replace(/\*/g,'.*').replace(/\?/g,'.')}$`,'i');
        return re.test(str); // remove last 'i' above to have case sensitive
      }
      
      
      // Example usage
      
      let arr = ["birdBlue", "birdRed", "pig1z", "pig2z", "elephantBlua" ];
      
      let resultA = arr.filter( x => wildTest('biRd*', x) );
      let resultB = arr.filter( x => wildTest('p?g?z', x) );
      let resultC = arr.filter( x => wildTest('*Blu?', x) );
      
      console.log('biRd*',resultA);
      console.log('p?g?z',resultB);
      console.log('*Blu?',resultC);

      【讨论】:

      • TypeError: wildcard.replace 不是函数
      • @DanPalmieri 通配符必须是一个字符串(所有字符串都有replace 函数)——可能你有数字。看看 sn-p 里面 - 有例子
      【解决方案4】:

      而不是 Animals == "bird*" Animals = "bird*" 应该可以工作。

      【讨论】:

        【解决方案5】:

        我认为您的意思是像“*”(星号)这样的通配符:

        • “a*b” => 以“a”开头并以“b”结尾的所有内容
        • “a*” => 以“a”开头的所有内容
        • "*b" => 以 "b" 结尾的所有内容
        • "*a*" => 包含“a”的所有内容
        • "*a*b*"=> 包含“a”的所有内容,后跟任何内容,后跟“b”,然后是任何内容

        或在您的示例中:“bird*” => 以bird 开头的所有内容

        我遇到了类似的问题,用 RegExp 写了一个函数:

        //Short code
        function matchRuleShort(str, rule) {
          var escapeRegex = (str) => str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
          return new RegExp("^" + rule.split("*").map(escapeRegex).join(".*") + "$").test(str);
        }
        
        //Explanation code
        function matchRuleExpl(str, rule) {
          // for this solution to work on any string, no matter what characters it has
          var escapeRegex = (str) => str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
        
          // "."  => Find a single character, except newline or line terminator
          // ".*" => Matches any string that contains zero or more characters
          rule = rule.split("*").map(escapeRegex).join(".*");
        
          // "^"  => Matches any string with the following at the beginning of it
          // "$"  => Matches any string with that in front at the end of it
          rule = "^" + rule + "$"
        
          //Create a regular expression object for matching string
          var regex = new RegExp(rule);
        
          //Returns true if it finds a match, otherwise it returns false
          return regex.test(str);
        }
        
        //Examples
        alert(
            "1. " + matchRuleShort("bird123", "bird*") + "\n" +
            "2. " + matchRuleShort("123bird", "*bird") + "\n" +
            "3. " + matchRuleShort("123bird123", "*bird*") + "\n" +
            "4. " + matchRuleShort("bird123bird", "bird*bird") + "\n" +
            "5. " + matchRuleShort("123bird123bird123", "*bird*bird*") + "\n" +
            "6. " + matchRuleShort("s[pe]c 3 re$ex 6 cha^rs", "s[pe]c*re$ex*cha^rs") + "\n" +
            "7. " + matchRuleShort("should not match", "should noo*oot match") + "\n"
        );

        如果您想了解更多关于使用的功能:

        【讨论】:

        • 此答案存在潜在问题。 Javascript 的 .replace() 在第一个参数上带有字符串只会替换找到的第一个字符,因此第 4 个示例 *a* 不起作用。您可以使用 .replace(/\*/g, '.*') 替换所有 *s
        • @ThadeuLuz:这不正确。 Javascript 的 .replace() 采用正则表达式并替换找到的所有与正则表达式匹配的字符 => w3schools.com/jsref/jsref_replace.asp
        • 第一个参数不带字符串,如行 自己看:
        • @ThadeuLuz:是的,你是对的。谢谢您的帮助!我已将 .replaced 替换为 split.join 解决方案(而不是 /\*/g 全局选项)。我还添加了一些涵盖 *a* 案例的示例。
        • @Spen,你的回答让我很了解,但如果你使用的是 url,则可能会出现误报。例如matchRuleShort("https://evil.com", "https://*.il.com) 评估为真!为了防止这种情况,我必须用转义的等价物转义所有非 * 字符。 return new RegExp("^" + rule.replace(/[.?+^$[\]\\(){}|-]/g, "\\$&");.split("*").join(".*") + "$").test(str); 借用自 stackoverflow.com/a/2593661/238638 的正则表达式
        【解决方案6】:

        您可以使用 Javascript 的 substring 方法。例如:

        var list = ["bird1", "bird2", "pig1"]
        
        for (var i = 0; i < list.length; i++) {
          if (list[i].substring(0,4) == "bird") {
           console.log(list[i]);
          }
        }
        

        哪些输出:

        bird1
        bird2
        

        基本上,您正在检查数组中的每个项目,以查看前四个字母是否为“鸟”。这确实假设 'bird' 将始终位于字符串的前面。


        假设您从 URL 获取路径名:

        假设您在 bird1?=letsfly - 您可以使用此代码检查 URL:

        var listOfUrls = [
                          "bird1?=letsfly",
                          "bird",
                          "pigs?=dontfly",
                         ]
        
        for (var i = 0; i < list.length; i++) {
          if (listOfUrls[i].substring(0,4) === 'bird') {
            // do something
          }
        }
        

        上面将第一个匹配到 URL,但不是第三个(不是猪)。您可以轻松地用正则表达式替换 url.substring(0,4),甚至可以使用 .contains() 等其他 javascript 方法


        使用.contains() 方法可能更安全一些。您不需要知道 URL 'bird' 的哪一部分。例如:

        var url = 'www.example.com/bird?=fly'
        
        if (url.contains('bird')) {
          // this is true
          // do something
        }
        

        【讨论】:

        • 感谢您的回复。在我的真实世界最终场景中,字符串将通过超链接进行,只有结尾会有所不同。而且我将使用 document.URL 来比较它们,所以我不能使用 RegEx,可以吗?为了使其适合示例:应该找到/返回bird1,以及bird1?param=letsfly 有什么建议吗?哪种方法最聪明?
        • 你绝对可以使用正则表达式,但你不需要。你可以使用一些东西,真的。我刚刚更新了我的答案,这更接近你想要的吗? URL 列表可以来自任何地方,包括 document.url
        • 我认为url.substring(0,4) 将为所有 3 个返回 http。也就是说,如果存在 url 变量 (var url = list[i])
        • 啊,不错。将其更新为假设数组填充的是路径名,而不是 URL。因为这不是问题的真正意义所在。谢谢
        • 我的问题是反过来。一方面,我在数组中有很多不同的 url(基本上都应该是像 bird1.com* 这样的正则表达式,需要与 document.url 进行比较。所以要么我应该把 /document.url*/ 作为正则表达式,我认为这不起作用,或者我应该将所有字符串作为正则表达式放入数组中,最后用 *我觉得也不是很好?:/
        【解决方案7】:
        var searchArray = function(arr, str){
            // If there are no items in the array, return an empty array
            if(typeof arr === 'undefined' || arr.length === 0) return [];
            // If the string is empty return all items in the array
            if(typeof str === 'undefined' || str.length === 0) return arr;
        
            // Create a new array to hold the results.
            var res = [];
        
            // Check where the start (*) is in the string
            var starIndex = str.indexOf('*');
        
            // If the star is the first character...
            if(starIndex === 0) {
        
                // Get the string without the star.
                str = str.substr(1);
                for(var i = 0; i < arr.length; i++) {
        
                    // Check if each item contains an indexOf function, if it doesn't it's not a (standard) string.
                    // It doesn't necessarily mean it IS a string either.
                    if(!arr[i].indexOf) continue;
        
                    // Check if the string is at the end of each item.
                    if(arr[i].indexOf(str) === arr[i].length - str.length) {                    
                        // If it is, add the item to the results.
                        res.push(arr[i]);
                    }
                }
            }
            // Otherwise, if the star is the last character
            else if(starIndex === str.length - 1) {
                // Get the string without the star.
                str = str.substr(0, str.length - 1);
                for(var i = 0; i < arr.length; i++){
                    // Check indexOf function                
                    if(!arr[i].indexOf) continue;
                    // Check if the string is at the beginning of each item
                    if(arr[i].indexOf(str) === 0) {
                        // If it is, add the item to the results.
                        res.push(arr[i]);
                    }
                }
            }
            // In any other case...
            else {            
                for(var i = 0; i < arr.length; i++){
                    // Check indexOf function
                    if(!arr[i].indexOf) continue;
                    // Check if the string is anywhere in each item
                    if(arr[i].indexOf(str) !== -1) {
                        // If it is, add the item to the results
                        res.push(arr[i]);
                    }
                }
            }
        
            // Return the results as a new array.
            return res;
        }
        
        var birds = ['bird1','somebird','bird5','bird-big','abird-song'];
        
        var res = searchArray(birds, 'bird*');
        // Results: bird1, bird5, bird-big
        var res = searchArray(birds, '*bird');
        // Results: somebird
        var res = searchArray(birds, 'bird');
        // Results: bird1, somebird, bird5, bird-big, abird-song
        

        这样的方法有一长串警告,还有一长串没有考虑到的“假设”,其中一些在其他答案中提到。但对于星型语法的简单使用,这可能是一个很好的起点。

        Fiddle

        【讨论】:

        • 感谢您的精彩回复。这不是内置在 JS 中并且可以“开箱即用”使用吗? ://
        • 不,不是真的,内置的 javascript 将使用正则表达式进行模式匹配。 @Davsket 答案显示了您如何做到这一点,并且您问题上的 cmets 提供了有关如何使用 Regex 的更多详细信息。就个人而言,对于这样的事情,我会采用正则表达式的方法,但要专门解决你的“鸟*”格式,这个答案就足够简单的使用了。
        • Regex 的问题在于,在现实世界场景中,数组的内容将是超链接,我会将它们与 (document.URL) 进行比较。假设数组中的字符串/url 是 bird1.com 如果 documentUrl 是 bird1.com 我需要返回它,但如果它是 bird1.com?param=letsfly 也需要返回。我如何以最有效的方式解决这个问题? ://
        • 好吧,我还是要说正则表达式。我对此很糟糕,所以我不是一个好人问但我向你保证,有人会知道如何在正则表达式中做到这一点,或者你可以尝试自己学习。我发现的一个有趣的库是here,它将全局模式example: bird* 转换为良好的正则表达式。由 node.js 包管理器使用,因此它可能非常健壮。
        【解决方案8】:
        if(mas[i].indexOf("bird") == 0)
            //there is bird
        

        您可以在此处阅读有关 indexOf 的信息:http://www.w3schools.com/jsref/jsref_indexof.asp

        【讨论】:

        【解决方案9】:

        你应该使用 RegExp(它们很棒)一个简单的解决方案是:

        if( /^bird/.test(animals[i]) ){
            // a bird :D
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-06-23
          • 2016-05-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多