【问题标题】:file name matching with wildcard与通配符匹配的文件名
【发布时间】:2011-03-19 01:02:25
【问题描述】:

我需要实现类似我自己的文件系统的东西。一个操作是 FindFirstFile。我需要检查调用者是否传递了 .、sample*.cpp 等内容。我的“文件系统”实现将“文件名”列表作为 char* 数组提供。

有没有实现这个文件名匹配的Windows函数或者源代码?

【问题讨论】:

    标签: c++ c windows filesystems


    【解决方案1】:

    对于使用 '*' 和 '?' 的通配符名称匹配试试这个(如果你想避免提升,使用 std::tr1::regex):

    #include <boost/regex.hpp>
    #include <boost/algorithm/string/replace.hpp>
    
    using std::string;
    
    bool MatchTextWithWildcards(const string &text, string wildcardPattern, bool caseSensitive /*= true*/)
    {
        // Escape all regex special chars
        EscapeRegex(wildcardPattern);
    
        // Convert chars '*?' back to their regex equivalents
        boost::replace_all(wildcardPattern, "\\?", ".");
        boost::replace_all(wildcardPattern, "\\*", ".*");
    
        boost::regex pattern(wildcardPattern, caseSensitive ? regex::normal : regex::icase);
    
        return regex_match(text, pattern);
    }
    
    void EscapeRegex(string &regex)
    {
        boost::replace_all(regex, "\\", "\\\\");
        boost::replace_all(regex, "^", "\\^");
        boost::replace_all(regex, ".", "\\.");
        boost::replace_all(regex, "$", "\\$");
        boost::replace_all(regex, "|", "\\|");
        boost::replace_all(regex, "(", "\\(");
        boost::replace_all(regex, ")", "\\)");
        boost::replace_all(regex, "{", "\\{");
        boost::replace_all(regex, "{", "\\}");
        boost::replace_all(regex, "[", "\\[");
        boost::replace_all(regex, "]", "\\]");
        boost::replace_all(regex, "*", "\\*");
        boost::replace_all(regex, "+", "\\+");
        boost::replace_all(regex, "?", "\\?");
        boost::replace_all(regex, "/", "\\/");
    }
    

    【讨论】:

    • 你几乎做对了。 regex_match() 不需要开始和结束符号。您还需要转义 { 和 }。我已提交修改,因此这些更改可能会整合到您的答案中。
    • @David:我需要开始/结束符号,因为我希望完整的文件名从头到尾匹配。如果只有部分文件名匹配,我不想匹配。这是从命令行习惯的行为。
    • regex_match 只会匹配整个输入序列,因此 ^ 和 $ 是多余的。也许您正在考虑 regex_search?
    • @David 你是对的。我删除了多余的代码。感谢您的澄清。
    • {和}提到的转义在哪里?我用std::regex 测试了代码,需要转义!
    【解决方案2】:

    周围有很多这样的功能。这里有一个directory的各种实现,分为递归和非递归等。

    如果您不喜欢那里的许可(或链接有问题等),这里有一个匹配算法的可能实现,它至少与 Windows 使用的算法非常接近:

    #include <string.h>
    #include <iostream>
    
    bool match(char const *needle, char const *haystack) {
        for (; *needle != '\0'; ++needle) {
            switch (*needle) {
            case '?': 
                if (*haystack == '\0')
                    return false;
                ++haystack;
                break;
            case '*': {
                if (needle[1] == '\0')
                    return true;
                size_t max = strlen(haystack);
                for (size_t i = 0; i < max; i++)
                    if (match(needle + 1, haystack + i))
                        return true;
                return false;
            }
            default:
                if (*haystack != *needle)
                    return false;
                ++haystack;
            }
        }
        return *haystack == '\0';
    }
    
    #ifdef TEST
    #define CATCH_CONFIG_MAIN
    
    #include "catch.hpp"
    
    TEST_CASE("Matching", "[match]") {
        REQUIRE(match("a", "a") == true);
        REQUIRE(match("a", "b") == false);
        REQUIRE(match("a*", "a") == true);
        REQUIRE(match("a?", "a") == false);
        REQUIRE(match("a?", "ab") == true);
        REQUIRE(match("a*b", "ab") == true);
        REQUIRE(match("a*b", "acb") == true);
        REQUIRE(match("a*b", "abc") == false);
        REQUIRE(match("*a*??????a?????????a???????????????", 
            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") == true);
    }
    
    #endif
    

    由于讨论了其他一些答案的复杂性,我会注意到我相信这具有 O(NM) 复杂性和 O(M) 存储使用(其中 N 是目标字符串的大小,并且M 是图案的大小)。

    使用@masterxilo 的测试对:

    "*a*??????*a*?????????a???????????????", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
    

    ...这会在我的机器上大约 3 微秒内找到匹配项。这比典型的模式慢很多——我的大多数其他测试在这台特定的机器上运行大约 300 纳秒左右。

    同时,@masterxilo 的代码在同一台机器上运行大约需要 11 微秒,所以这仍然快了大约 3 到 4 倍(更不用说更小更简单了)。

    【讨论】:

    • 不仅怪异,而且有点脑残。例如,如果我为 LibreOffice 工作,而 LibreOffice 被这样的军队使用,我可能不会使用代码......
    • @phresnel:这只适用于他的代码。相当多的东西不是他的(例如,一对夫妇根本没有限制)。
    • 虽然他也提到他修改了其中的一些。需要律师。
    • 可能只是对我来说它不起作用,但仍然无法访问
    • 我认为您的代码中有一个小错误:if (needle[1] == '\0' || max == 0) return true; || max == 0 部分应该恕我直言,因为那样干草堆“test.txt”将匹配针“test.txt*后缀”。
    【解决方案3】:

    查看 POSIX 函数 fnmatch、glob 和 wordexp。

    【讨论】:

      【解决方案4】:

      这是我的尝试。

      它是“C++”,但我故意让它几乎完全与 C 兼容。
      将其转换为 C 所需要做的就是删除 template 部分并将 Pattern 和 Text 更改为 char const * 之类的内容。

      // TEST THIS before use! I've only done limited testing.
      
      #include <stddef.h>
      #include <stdlib.h>
      #include <string.h>
      
      template<class Pattern, class Text>
      bool wildcard(
          Pattern const pat_begin, Pattern const pat_end,
          Text text_begin, Text const text_end)
      {
          ptrdiff_t const pat_size = pat_end - pat_begin;
          ptrdiff_t stackbuf[64];
          size_t c = sizeof(stackbuf) / sizeof(*stackbuf);
          ptrdiff_t *p = stackbuf;
          size_t n = 0;
          p[n++] = 0;
          while (n > 0 && text_begin != text_end)
          {
              for (size_t i = 0; i < n; i++)
              {
                  if (p[i] == pat_size)
                  {
                      p[i--] = p[--n];
                      continue;
                  }
                  switch (*(pat_begin + p[i]))
                  {
                  case '?': ++p[i]; break;
                  case '*':
                      ptrdiff_t off;
                      off = p[i];
                      while (off < pat_size &&
                          *(pat_begin + off) == '*')
                      { ++off; }
                      if (n == c)
                      {
                          ptrdiff_t const *const old = p;
                          c *= 2;
                          if (c == 0) { ++c; }
                          size_t const size = c * sizeof(*p);
                          p = (ptrdiff_t *)realloc(
                              old == stackbuf ? NULL : p,
                              size);
                          if (old == stackbuf)
                          { memcpy(p, old, n * sizeof(*old)); }
                      }
                      p[n++] = off;
                      break;
                  default:
                      if (*(pat_begin + p[i]) == *text_begin)
                      { ++p[i]; }
                      else { p[i--] = p[--n]; }
                      break;
                  }
              }
              ++text_begin;
          }
          bool success = false;
          if (text_begin == text_end)
          {
              while (!success && n > 0)
              {
                  --n;
                  while (p[n] != pat_size &&
                      *(pat_begin + p[n]) == '*')
                  { ++p[n]; }
                  if (p[n] == pat_size)
                  { success = true; }
              }
          }
          if (p != stackbuf) { free(p); }
          return success;
      }
      
      bool wildcard(char const *const pattern, char const *const text)
      {
          return wildcard(
              pattern,
              pattern + (pattern ? strlen(pattern) : 0),
              text,
              text + (text ? strlen(text) : 0));
      }
      
      bool wildcard(wchar_t const *const pattern, wchar_t const *const text)
      {
          return wildcard(
              pattern,
              pattern + (pattern ? wcslen(pattern) : 0),
              text,
              text + (text ? wcslen(text) : 0));
      }
      

      当然,您可以随意以任何您想要的方式使用代码。 :)

      【讨论】:

      • 这具有最坏情况下的指数运行时间。
      • @masterxilo:你能告诉我一个正确的例子吗?
      【解决方案5】:

      Mehrdad 提供的解决方案具有指数运行时间,因为它使用回溯。需要一秒钟才能确定"*a*??????*a*?????????a???????????????", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 是匹配项。也没有办法用 '*' 或 '?' 匹配字符串在里面。

      这是我提出的一个 O(nm) 实现,它的内存使用量为 O(n),其中 n 是表达式的长度,m 是字符串的长度。它还支持转义?、*和\。

      #include <stddef.h>
      #include <stdlib.h>
      #include <string.h>
      
      /** 
       * Wildcard matching.
       * See for example
       * http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/find_c_search_wildcard.mspx?mfr=true
       *
       *  * matches any amount of any characters, 
       *  ? matches any single character.
       *  c matches c.
       *  No escaping of * and ? implemented (these are not allowed in windows filenames anyways).
       *
       * Backtracking O(2^n) implementation.
       *
       * from http://stackoverflow.com/questions/3300419/file-name-matching-with-wildcard 
       * http://stackoverflow.com/a/12231681/524504
       */
      template<class Pattern, class Text>
      bool wildcard(
              Pattern const pat_begin,
              Pattern const pat_end,
              Text text_begin, 
              Text const text_end)
      {
          ptrdiff_t const pat_size = pat_end - pat_begin;
      
          /* initial pattern position stack (offsets into the pattern string) and its size c */
          ptrdiff_t stackbuf[64];
          size_t c                     = sizeof(stackbuf) / sizeof(*stackbuf);
          /* Stack base. Can be realloc'ed to some new memory location if we need more */
          ptrdiff_t *p                 = stackbuf;
      
          /* pointer to the location in the stack */
          size_t n                     = 0;
          p[n++]                       = 0; /* position 0 in the stack not used */
      
          /* text_begin updated to skip everything successfully consumed  */
          while (n > 0 && text_begin != text_end) 
          {
              for (size_t i = 0; i < n; i++) 
              {
                  /* if we're at the end of the pattern, but not at the end of the 
                   * string, we might have done something wrong.
                   */
                  if (p[i] == pat_size) 
                  {
                      p[i--] = p[--n];
                      continue;
                  }
                  /* current pattern character */
                  switch (*(pat_begin + p[i]))
                  {
                      case '?': ++p[i]; break; /* simply advance pattern pointer */
                      case '*':
                                ptrdiff_t off;
                                off = p[i];
                                while (off < pat_size &&
                                        *(pat_begin + off) == '*')
                                { ++off; }
                                /* if the stack is full, reallocate */
                                if (n == c)
                                {
                                    ptrdiff_t const *const old = p;
                                    c *= 2;
                                    /* assert positive size
                                     * stack size never reduced anyways?
                                     */
                                    if (c == 0) { ++c; }
                                    size_t const size = c * sizeof(*p);
                                    /* cannot use realloc to copy original stack 
                                     * (ptr in realloc must be 
                                     * "Pointer to a memory block previously 
                                     * allocated with malloc, calloc or realloc."
                                     * must do manually
                                     */
                                    p = (ptrdiff_t *)realloc(
                                             old == stackbuf ? NULL : p,
                                            size);
                                    if (old == stackbuf)
                                    { memcpy(p, old, n * sizeof(*old)); }
                                }
                                /* store offset */
                                p[n++] = off;
                                break;
                      default: /* a normal character in the pattern */
                                /* must be matched exactly */
                                if (*(pat_begin + p[i]) == *text_begin)
                                { ++p[i]; } /* advance pattern pointer */
                                else /* if not, backtrack */
                                { p[i--] = p[--n]; }
                                break;
                  }
              }
              ++text_begin;
          }
      
          bool success = false;
          if (text_begin == text_end)
          {
              while (!success && n > 0)
              {
                  --n;
                  while (p[n] != pat_size &&
                          *(pat_begin + p[n]) == '*')
                  { ++p[n]; }
                  if (p[n] == pat_size)
                  { success = true; }
              }
          }
      
          /* need to free stack if it was reallocated */
          if (p != stackbuf) { free(p); }
          return success;
      }
      
      bool wildcard(char const *const pattern, char const *const text)
      {
          return wildcard(
                  pattern,
                  pattern + (pattern ? strlen(pattern) : 0),
                  text,
                  text + (text ? strlen(text) : 0));
      }
      
      bool wildcard(wchar_t const *const pattern, wchar_t const *const text)
      {
          return wildcard(
                  pattern,
                  pattern + (pattern ? wcslen(pattern) : 0),
                  text,
                  text + (text ? wcslen(text) : 0));
      }
      
      /**
       * Virtual Machine Style Regular Expression parsing/NFA emulation
       * used for wildcard matching. O(nm) algorithm.
       *
       * See http://swtch.com/~rsc/regexp/ for more on efficient RegEx parsing.
       *
       * Copyright (c) March 29, 2013 Paul Frischknecht
       * Can be distributed under the MIT licence, see bottom of file.
       */
      
      //#define wildcard_fast_DEBUG /* define to make the alogorithm printf what its doing */
      
      /**
       * Instructions are:
       *
       * star
       *   This launches a new thread at the current position and continues with the 
       *   current thread at the next position accepting any character.
       * char c
       *   Accepts exactly the character c
       * anychar
       *   Accepts any character.
       * end 
       *   Accepts the end of the input string.
       */
      enum InstructionType {
          End     = 0,
          Star    = 1,
          Char    = 2,
          AnyChar = 3
      };
      
      struct Instruction {
          InstructionType i;
          int c;       /* the caracter this instruction matches - undefined for non Char */
          int threads; /* threads active in the given instruction */
          /*
           * storing this here allows us to find out wether there is a thread
           * active at a given instruction in O(1)
           */
      };
      
      /** 
       * Wildcard (file path) matching.
       * See for example 
       * http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/find_c_search_wildcard.mspx?mfr=true
       *
       *  * matches any amount of any characters, 
       *  ? matches any single character.
       *  c matches c.
       *
       * Escaping of '*', '?' and '\' via '\*', '\?' and '\\' implemented. 
       * All other bytes are recognized directly as is. 
       * The string may not end in a single (uneven amount of) '\'.
       *
       * If text_end is 0, the text is assumed to be 0 terminated.
       * Same for pattern_end. The end is exclusive.
       *
       * @return A pointer to the character after the last character matched.
       *
       * Virtual machine O(nm) implementation, see http://swtch.com/~rsc/regexp/regexp2.html
       *
       * TODO Factor out the precompilation step to speed up matching multiple strings
       * against the same expression.
       */
      template<class Pattern, class Text>
      Text wildcard_fast(
              Pattern const pat_begin,
              Pattern const pat_end,
              Text const text_begin, 
              Text const text_end)
      {
          /* 
           * give some reasonable default program size so we don't have to call
           * malloc in most cases 
           */
          #define DEFAULTonstack_program_SIZE 256
          Instruction onstack_program[DEFAULTonstack_program_SIZE];
          /* this stores the current run and next run thread list, alternating */
          /* there are as most as many threads as instructions */
          Instruction* onstack_threads[DEFAULTonstack_program_SIZE*2]; 
      
          Instruction** threads      = onstack_threads;
          Instruction*  program      = onstack_program;
          int           program_size = sizeof(onstack_program)/sizeof(*program);
      
          Instruction* program_last_instruction = program + program_size - 1;
      
          /* program and pattern pointers */
          Instruction* pp   = program;
          Pattern      patp = pat_begin;
      
          /* compile */
      
          while ((pat_end == 0 && *patp != 0) || (pat_end != 0 && patp != pat_end)) {
      
              /* need more space */
              if (pp == program_last_instruction) {
      
                  Instruction*  old_program = program;
                  Instruction** old_threads = threads;
                  int old_program_size      = program_size;
      
                  program_size *= 2;
      
                  program = (Instruction*) malloc(program_size*sizeof(*program));
                  threads = (Instruction**)malloc(program_size*sizeof(*threads)*2);
      
                  memcpy(program, old_program, old_program_size*sizeof(*program));
      
                  if (old_program != onstack_program) {
                      free(old_program); free(old_threads);
                  }
      
                  program_last_instruction = program + program_size - 1;
                  pp = pp - old_program + program;
              }
      
              /* parse pattern */
              switch (*patp) {
                  case '*': 
                      pp->i = Star; 
                      /* Optimize multiple stars away */
                      while ((pat_end == 0 || patp+1 != pat_end) && *(patp+1) == '*')  
                          patp++; 
                      break;
      
                  case '?':
                      pp->i = AnyChar; 
                      break;
      
                  case '\\': 
                      pp->i = Char; 
                      pp->c = *(++patp); /* assumes string does not end in \ */
                      break;
      
                  default: 
                      pp->i = Char; 
                      pp->c = *patp; 
                      break;
              }
      
              pp->threads = 0;
      
              pp++;
              patp++;
          }
      
          /* add the End instruction at the end */
          program_last_instruction = pp;
          pp->i                    = End;
          pp->threads              = 0;
      
          /* run */
          Text sp = text_begin; /* input string pointer */
          int n = 1, c = 0; /* next and current index */
          int threadcount[2];
      
          /* initialize */
          threadcount[c] = 1;
          threads[0] = program;
          threads[0]->threads++;
      
          /* run over text */
          while ((text_end == 0 && *sp != 0) || (text_end != 0 && sp != text_end)) {
              /* unless recreated, all threads will die */
              threadcount[n] = 0;
      
              /* run over threads */
              for (int i = 0; i < threadcount[c]; i++) {
      
                  Instruction* inst = threads[2*i+c];
                  switch (inst->i) {
                      case End: 
                          /* we may not reach end early */ 
                          /* kill this thread without recrating it */
                          inst->threads--; 
                          continue; /* with for loop */
      
                      case Char: 
                          if (*sp != inst->c) {
                              /* if the character is not matched, kill this thread */
                              inst->threads--;
                              continue;
                          }
                          break;
      
                      case Star: 
                          /* spawn off additional thread at current location */
      
                          if (inst->threads == 1) { 
                              /* only if there's noone active there yet */
                              threads[2*(threadcount[n]++)+n] = inst;
                              inst->threads++;
                          }
                          break;
                  }
                  /* 
                   * common actions: increase program counter for current thread,
                   * decrese amount of threads in last (current) instruction.
                   */
                  inst->threads--;
                  inst++;
                  inst->threads++;
      
                  /* respawn at new location in next iteration */
                  threads[2*(threadcount[n]++)+n] = inst;
      
                  if (inst->i == Star && (inst+1)->threads == 0) { 
                      /* 
                       * already follow no-match option to give us 
                       * more stuff to do 
                       */
                      threads[2*(threadcount[n]++)+n] = inst+1;
                      (inst+1)->threads++;
                  }
      
      #ifdef wildcard_fast_DEBUG
                    for (int i = 0 ; i < threadcount[n]; i++) {
                      printf("thread %d at %d.\n", i, threads[2*i+n]-program);
                  }
      #endif
      
              }
      
      #ifdef wildcard_fast_DEBUG
              const char *ns[] = {
                  "end",
                  "star",
                  "char",
                  "anychar",
              };
              for (Instruction* p = program; p->i; p++) {
                  printf("%d. %s %c (%d threads)\n", p-program, ns[p->i], p->i == Char ? p->c : ' ', p->threads);
              }
      #endif
      
              /* swap next and current and advance */
              n = c;
              c = !c;
              sp++;
          }
      
          /* 
           * if there is no thread active in the End instruction when the 
           * end of the input was reached, this was no match
           */
          if (program_last_instruction->threads == 0) sp = 0; 
      
          if (program != onstack_program) {
              /* only need to free if we used malloc */
              free(program); free(threads);
          }
      
          return sp;
      }
      
      char const* wildcard_fast(
              char const *const pattern, 
              char const *const text) 
      {
          return wildcard_fast(
                  pattern,
                  (const char*)0,
                  text,
                  (const char*)0);
      }
      
      wchar_t const* wildcard_fast(
              wchar_t const *const pattern,
              wchar_t const *const text) 
      {
          return wildcard_fast(
                  pattern,
                  (const wchar_t*)0,
                  text,
                  (const wchar_t*)0);
      }
      
      
      /* tests */
      #ifndef INCLUDE_IMPLEMENTATION
      /* 
       * I just include this file in my projects and define this.
       * That works well for simple algorithms like this
       */
      
      #include <stdio.h>
      #include <time.h>
      
      int main() {
          struct {
              char* p;
              char* t;
              bool expected_result;
          } test[] = {
              {
                  "",
                  "",
                  true
              },
              {
                  "a",
                  "",
                  false
              },
              {
                  "",
                  "a",
                  false
              },
              {
                  "a",
                  "a",
                  true
              },        
              {
                  "****.txt", 
                  "hello.txt",
                  true
              },
              {
                  "*.txt", 
                  "hello.tzt",
                  false
              },
              {
                  "*.t?t*", 
                  "hello.tzt",
                  true
              },
              {
                  "*.*", 
                  "hi.there",
                  true
              },
              /* the wildcard implementation will fail this as it doesn't understand escaping */
              {
                  "\\*who\\?\\?.*\\\\", 
                  "*who??.there\\",
                  true
              },        
              /* these take extraordinaryly long on the O(2^n) implementation */
              {
                  "**a*************************??????***a************************?????????a???????????????", 
                  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                  true
              },
              /* runs a bit better if we omit the extra *'s. The fast implementation already optimizes these away. */
              {
                  "*a*??????*a*?????????a???????????????", 
                  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                  true
              },
              {0,0} /* marks end of tests */
          };
      
          int t0 = clock();
          const char* result;
          const char* r[] = {"no match", "match"};
      
          for (int i = 0; test[i].p; i++) {
              printf("=== Test %d: Matching '%s' against '%s'...===\n", i, test[i].t, test[i].p);
      
              /* wildcard_fast */
              t0 = clock();
              result = wildcard_fast(test[i].p, test[i].t);
      
              printf("  wildcard_fast (took %d ms):\n", clock()-t0);
              if (!!result != test[i].expected_result)
                  printf("    Test failed (reported %s instead of expected %s).\n",
                          r[!!result], r[test[i].expected_result]);
              else if (result) {
                  printf("    %s\n", test[i].t);
                  printf("    %*.c\n", result-test[i].t+1, '^');
              }
              else
                  printf("    No match.\n");
      
              /* wildcard */
              t0 = clock();
              result = (const char*)
                  wildcard(test[i].p, test[i].t);
      
              printf("  wildcard (took %d ms):\n", clock()-t0);
              if (!!result != test[i].expected_result)
                  printf("    Test failed (reported %s instead of expected %s).\n",
                          r[!!result], r[test[i].expected_result]);
              else if (result) printf("    Match.\n");
              else printf("    No match.\n");
      
              printf("\n");
          }
      }
      #endif
      
      /*
       * Permission is hereby granted, free of charge, to any person
       * obtaining a copy of this software and associated
       * documentation files (the "Software"), to deal in the
       * Software without restriction, including without limitation
       * the rights to use, copy, modify, merge, publish, distribute,
       * sublicense, and/or sell copies of the Software, and to
       * permit persons to whom the Software is furnished to do so,
       * subject to the following conditions:
       * 
       * The above copyright notice and this permission notice shall
       * be included in all copies or substantial portions of the
       * Software.
       * 
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
       * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
       * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
       * PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS
       * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
       * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
       * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       */
      

      这使用了虚拟机方法。有关高效正则表达式解析的更多信息,请参阅http://swtch.com/~rsc/regexp/。

      【讨论】:

      • “Mehrdad 提供的解决方案具有指数运行时间”...不,它具有线性运行时间。当人们谈论正则表达式运行时间时,他们谈论的是输入字符串的大小,而不是模式的大小。我们的两个示例在输入字符串的大小上都是线性的;我认为你的在模式的大小上使用了潜在的指数内存,而我的在模式的大小上使用了潜在的指数时间。您可以将 NFA 转换为 DFA,这通常是一个很好的折衷方案,但如果您考虑到模式大小,则应该提及这一点。
      • 谈到空间复杂度,我很确定您的实现确实没有具有“O(n)”内存使用量。空间使用怎么可能与模式复杂性无关?!
      • 要明确一点,n 是模式长度,m 是输入字符串长度。内存使用仅取决于模式大小n,无论模式是什么。内存使用量随着模式的大小线性增长——它只需要编译模式,其中一个字符(1 个字节)恰好转换为一个指令(12 个字节)。我没有为输入字符串分配额外的内存。我认为回溯搜索是输入字符串大小乘以模式大小的最坏情况指数,因为它可能对每个输入字符的每个模式字符做出错误的猜测。
      • “内存使用量只取决于模式大小 n,不管模式是什么。”...嗯?您生成的线程数不取决于输入大小吗?不是每个线程都消耗内存吗?
      • 不,每条指令最多有一个线程。然后循环遍历输入中每个字符的所有线程,因此为 O nm。我链接到的文章更详细地描述了这种方法,值得一读。
      【解决方案6】:

      这是一个无依赖的可移植 C++ 版本:

      #include <string>
      
      #include <string.h>
      
      bool wild_match(const std::string& str, const std::string& pat) {
        std::string::const_iterator str_it = str.begin();
        for (std::string::const_iterator pat_it = pat.begin(); pat_it != pat.end();
             ++pat_it) {
          switch (*pat_it) {
            case '?':
              if (str_it == str.end()) {
                return false;
              }
      
              ++str_it;
              break;
            case '*': {
              if (pat_it + 1 == pat.end()) {
                return true;
              }
      
              const size_t max = strlen(&*str_it);
              for (size_t i = 0; i < max; ++i) {
                if (wild_match(&*(pat_it + 1), &*(str_it + i))) {
                  return true;
                }
              }
      
              return false;
            }
            default:
              if (*str_it != *pat_it) {
                return false;
              }
      
              ++str_it;
          }
        }
      
        return str_it == str.end();
      }
      

      【讨论】:

        【解决方案7】:

        PathMatchSpec。虽然它受到MAX_PATH 的限制(即不能超过 260 个字符)。您最好实现自己的匹配器;代码不多。

        【讨论】:

          【解决方案8】:

          这与@nabulke 的答案几乎相同,使用 C++11 而不是 Boost(因此,如果您喜欢这个答案,请务必也投赞成票):

          #include <regex>
          #include <string>
          
          std::regex wildcardToRegex(const std::string& wildcard, bool caseSensitive = true)
          {
              // Note It is possible to automate checking if filesystem is case sensitive or not (e.g. by performing a test first time this function is ran)
              std::string regexString{ wildcard };
              // Escape all regex special chars:
              regexString = std::regex_replace(regexString, std::regex("\\\\"), "\\\\");
              regexString = std::regex_replace(regexString, std::regex("\\^"), "\\^");
              regexString = std::regex_replace(regexString, std::regex("\\."), "\\.");
              regexString = std::regex_replace(regexString, std::regex("\\$"), "\\$");
              regexString = std::regex_replace(regexString, std::regex("\\|"), "\\|");
              regexString = std::regex_replace(regexString, std::regex("\\("), "\\(");
              regexString = std::regex_replace(regexString, std::regex("\\)"), "\\)");
              regexString = std::regex_replace(regexString, std::regex("\\{"), "\\{");
              regexString = std::regex_replace(regexString, std::regex("\\{"), "\\}");
              regexString = std::regex_replace(regexString, std::regex("\\["), "\\[");
              regexString = std::regex_replace(regexString, std::regex("\\]"), "\\]");
              regexString = std::regex_replace(regexString, std::regex("\\+"), "\\+");
              regexString = std::regex_replace(regexString, std::regex("\\/"), "\\/");
              // Convert wildcard specific chars '*?' to their regex equivalents:
              regexString = std::regex_replace(regexString, std::regex("\\?"), ".");
              regexString = std::regex_replace(regexString, std::regex("\\*"), ".*");
          
              return std::regex(regexString, caseSensitive ? std::regex_constants::ECMAScript : std::regex_constants::icase);
          }
          
          bool wildmatch(const std::string& input, const std::string& wildcard)
          {
              auto rgx = wildcardToRegex(wildcard);
              return std::regex_match(input, rgx);
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-05-27
            • 1970-01-01
            • 2015-02-18
            • 2010-10-13
            • 1970-01-01
            • 2011-09-09
            • 2011-06-25
            • 1970-01-01
            相关资源
            最近更新 更多