【问题标题】:Searching for UUIDs in text with regex使用正则表达式在文本中搜索 UUID
【发布时间】:2010-09-13 06:49:43
【问题描述】:

我正在使用正则表达式在文本块中搜索 UUID。目前我依赖的假设是所有 UUID 都将遵循 8-4-4-4-12 十六进制数字的模式。

谁能想到这个假设无效并导致我错过一些 UUID 的用例?

【问题讨论】:

  • 这个 6 年前的问题是为了帮助我完成一个在文本块中查找信用卡的项目。我随后开源了链接自我的博客文章的代码,该代码解释了 UUID 在搜索信用卡时引起的细微差别guyellisrocks.com/2013/11/…
  • 对 UUID 正则表达式模式匹配的搜索将我带到了这个堆栈溢出帖子,但接受的答案实际上不是答案。此外,您在问题下方的评论中提供的链接也没有该模式(除非我遗漏了某些内容)。这些答案之一是您最终使用的吗?
  • 如果您关注从我发布的链接开始的链接的兔子沃伦,您可能会在 GitHub 中遇到这一行,其中包含我最终使用的正则表达式。 (可以理解的是很难找到。)那个代码和那个文件可能对你有帮助:github.com/guyellis/CreditCard/blob/master/Company.CreditCard/…
  • 这些答案似乎都没有为仅有效 RFC 4122 UUID 的所有变体提供单个正则表达式。但看起来这里给出了这样的答案:stackoverflow.com/a/13653180/421049

标签: regex


【解决方案1】:

想贡献我的一份力量,因为我的正则表达式涵盖了来自 OP 的所有案例,并在 group 方法上正确分组了所有相关数据(您不需要对字符串进行后处理来获取 uuid 的每个部分,这个正则表达式已经得到给你)

([\d\w]{8})-?([\d\w]{4})-?([\d\w]{4})-?([\d\w]{4})-?([\d\w]{12})|[{0x]*([\d\w]{8})[0x, ]{4}([\d\w]{4})[0x, ]{4}([\d\w]{4})[0x, {]{5}([\d\w]{2})[0x, ]{4}([\d\w]{2})[0x, ]{4}([\d\w]{2})[0x, ]{4}([\d\w]{2})[0x, ]{4}([\d\w]{2})[0x, ]{4}([\d\w]{2})[0x, ]{4}([\d\w]{2})[0x, ]{4}([\d\w]{2})

【讨论】:

    【解决方案2】:

    这是有效的正则表达式:https://www.regextester.com/99148

    const regex = [0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}
    

    【讨论】:

      【解决方案3】:

      如果使用 Posix 正则表达式(grep -E、MySQL 等),这可能更容易阅读和记住:

      [[:xdigit:]]{8}(-[[:xdigit:]]{4}){3}-[[:xdigit:]]{12}
      

      编辑: Perl 和 PCRE 风格也支持 Posix 字符类,因此这将适用于它们。对于那些,将(…) 更改为非捕获子组(?:…)

      【讨论】:

        【解决方案4】:

        对于 bash:

        grep -E "[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}"
        

        例如:

        $> echo "f2575e6a-9bce-49e7-ae7c-bff6b555bda4" | grep -E "[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}"
        f2575e6a-9bce-49e7-ae7c-bff6b555bda4
        

        【讨论】:

        • 您需要包含 grep 的 -i 选项以进行不区分大小写的匹配。
        【解决方案5】:

        uuid 的正则表达式是:

        \b[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f]{12}\b
        

        【讨论】:

        • [a-f0-9]!因为它是十六进制的!您的正则表达式(原样)可能会返回误报。
        • 在某些情况下,您甚至可能想要制作 [a-fA-F0-9] 或 [A-F0-9]。
        • @cyber-monk: [0-9a-f] 在含义和速度上与 [a-f0-9] 和 [0123456789abcdef] 相同,因为正则表达式无论如何都会变成状态机,每个十六进制数字都变成状态表中的一个条目。有关其工作原理的切入点,请参阅en.wikipedia.org/wiki/Nondeterministic_finite_automaton
        • 这个解决方案不太正确。根据 RFC4122,它匹配具有无效版本和变体字符的 ID。 @Gajus 的解决方案在这方面更正确。此外,RFC 允许输入大写字符,因此添加 [A-F] 是合适的。
        • @broofa,我看到你真的对每个人都只匹配与 RFC 一致的 UUID。但是,我认为您必须多次指出这一点这一事实是一个可靠的指标,即并非所有 UUID 都会使用 RFC 版本和变体指标。 UUID 定义en.wikipedia.org/wiki/Uuid#Definition 声明了一个简单的 8-4-4-4-12 模式和 2^128 种可能性。 RFC 仅代表其中的一个子集。那么你想匹配什么?子集,还是全部?
        【解决方案6】:
        $UUID_RE = join '-', map { "[0-9a-f]{$_}" } 8, 4, 4, 4, 12;
        

        顺便说一句,其中一个位置只允许 4 个仅对 UUIDv4 有效。 但是 v4 并不是唯一存在的 UUID 版本。 我在实践中也遇到过 v1。

        【讨论】:

          【解决方案7】:
          /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89AB][0-9a-f]{3}-[0-9a-f]{12}$/i
          

          Gajus 的正则表达式拒绝 UUID V1-3 和 5,即使它们是有效的。

          【讨论】:

          • 但它允许无效版本(如 8 或 A)和无效变体。
          • 请注意 [89AB][0-9a-f] 中的 AB 为大写,其余允许的字符为小写。它让我在 Python 中脱颖而出
          【解决方案8】:

          在 python re 中,您可以从数字跨越到大写字母。所以..

          import re
          test = "01234ABCDEFGHIJKabcdefghijk01234abcdefghijkABCDEFGHIJK"
          re.compile(r'[0-f]+').findall(test) # Bad: matches all uppercase alpha chars
          ## ['01234ABCDEFGHIJKabcdef', '01234abcdef', 'ABCDEFGHIJK']
          re.compile(r'[0-F]+').findall(test) # Partial: does not match lowercase hex chars
          ## ['01234ABCDEF', '01234', 'ABCDEF']
          re.compile(r'[0-F]+', re.I).findall(test) # Good
          ## ['01234ABCDEF', 'abcdef', '01234abcdef', 'ABCDEF']
          re.compile(r'[0-f]+', re.I).findall(test) # Good
          ## ['01234ABCDEF', 'abcdef', '01234abcdef', 'ABCDEF']
          re.compile(r'[0-Fa-f]+').findall(test) # Good (with uppercase-only magic)
          ## ['01234ABCDEF', 'abcdef', '01234abcdef', 'ABCDEF']
          re.compile(r'[0-9a-fA-F]+').findall(test) # Good (with no magic)
          ## ['01234ABCDEF', 'abcdef', '01234abcdef', 'ABCDEF']
          

          这是最简单的 Python UUID 正则表达式:

          re_uuid = re.compile("[0-F]{8}-([0-F]{4}-){3}[0-F]{12}", re.I)
          

          我将把它作为练习留给读者使用 timeit 来比较它们的性能。

          享受。 保持 Pythonic™!

          注意:这些跨度也将匹配:;<=>?@',因此,如果您怀疑这可能会给您带来误报,请不要走捷径。 (感谢 Oliver Aubert 在 cmets 中指出这一点。)

          【讨论】:

          • [0-F] 确实会匹配 0-9 和 AF,但也可以匹配 ASCII 码介于 57(对于 9)和 65(对于 A)之间的任何字符,也就是说: ;?@'.
          • 所以不要使用上面提到的代码,除非你想考虑:=>;?==@? >=:?=@;作为有效的 UUID :-)
          【解决方案9】:

          如果您想检查或验证特定的 UUID 版本,这里是相应的正则表达式。

          请注意,唯一的区别是版本号,在UUID 4122 RFC4.1.3. Version 章节中有说明。

          版本号是第三组的第一个字符:[VERSION_NUMBER][0-9A-F]{3}

          • UUID v1:

            /^[0-9A-F]{8}-[0-9A-F]{4}-[1][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i
            
          • UUID v2:

            /^[0-9A-F]{8}-[0-9A-F]{4}-[2][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i
            
          • UUID v3:

            /^[0-9A-F]{8}-[0-9A-F]{4}-[3][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i
            
          • UUID v4:

            /^[0-9A-F]{8}-[0-9A-F]{4}-[4][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i
            
          • UUID v5:

            /^[0-9A-F]{8}-[0-9A-F]{4}-[5][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i
            

          【讨论】:

          • 模式不包含小写字母。它还应该在每个 A-F 范围旁边包含 a-f
          • 正则表达式末尾的i 将其标记为不区分大小写。
          • 不能总是使用模式修饰符。例如,在 openapi 定义中,模式区分大小写
          • @StephaneJanicaud 在 OpenAPI 中,您应该使用 format 修饰符,将其设置为“uuid”,而不是使用正则表达式来测试 UUID:swagger.io/docs/specification/data-models/data-types/#format
          • 谢谢@IvanGabriele 的提示,这只是一个例子,当你不想检查任何不区分大小写的模式时,同样的问题。
          【解决方案10】:

          对于在 OS X 上使用uuidgen 生成的 UUID,正则表达式模式是

          [A-F0-9]{8}-[A-F0-9]{4}-4[A-F0-9]{3}-[89AB][A-F0-9]{3}-[A-F0-9]{12}
          

          验证

          uuidgen | grep -E "[A-F0-9]{8}-[A-F0-9]{4}-4[A-F0-9]{3}-[89AB][A-F0-9]{3}-[A-F0-9]{12}"
          

          【讨论】:

            【解决方案11】:

            [\w]{8}(-[\w]{4}){3}-[\w]{12} 在大多数情况下都为我工作。

            或者,如果您想更具体地[\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12}

            【讨论】:

            • 值得注意的是,至少在 Java 中,\w 匹配 _ 以及十六进制数字。用 \p{XDigit} 替换 \w 可能更合适,因为这是为匹配十六进制数字而定义的 POSIX 类。这可能会在使用其他 Unicode 字符集时中断。
            • @oconnor \w 通常表示“单词字符”,它将匹配比十六进制数字更多的字符。您的解决方案要好得多。或者,为了兼容性/可读性,您可以使用 [a-f0-9]
            • 这是一个看起来像正则表达式并匹配这些模式的字符串,但它是一个无效的正则表达式:2wtu37k5-q174-4418-2cu2-276e4j82sv19
            • @OleTraveler 不是真的,就像一个魅力。 import re def valid_uuid(uuid): regex = re.compile('[\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12}', re.I) match = regex.match(uuid) return bool(match) valid_uuid('2wtu37k5-q174-4418-2cu2-276e4j82sv19')
            • @tom 该字符串 (2wt...) 是无效的 UUID,但此答案中给出的模式与该字符串匹配,错误地表明它是有效的 UUID。太糟糕了,我不记得为什么那个 UUID 无效了。
            【解决方案12】:

            C++ 的变体:

            #include <regex>  // Required include
            
            ...
            
            // Source string    
            std::wstring srcStr = L"String with GIUD: {4d36e96e-e325-11ce-bfc1-08002be10318} any text";
            
            // Regex and match
            std::wsmatch match;
            std::wregex rx(L"(\\{[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}\\})", std::regex_constants::icase);
            
            // Search
            std::regex_search(srcStr, match, rx);
            
            // Result
            std::wstring strGUID       = match[1];
            

            【讨论】:

              【解决方案13】:

              版本 4 UUID 的格式为 xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx,其中 x 是任何十六进制数字,y 是 8、9、A 或 B 之一。例如f47ac10b-58cc-4372-a567-0e02b2c3d479。

              来源:http://en.wikipedia.org/wiki/Uuid#Definition

              因此,这在技术上更正确:

              /[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}/
              

              【讨论】:

              • 我不认为你的意思是 a-z。
              • 也需要接受 [A-F]。根据 RFC4122 的第 3 节:“十六进制值“a”到“f”作为小写字符输出,输入时不区分大小写'。另外(:?8|9|A|B) 可能比[89aAbB]更具可读性
              • 需要复制@broofa的修改;因为你的不包括小写 A 或 B。
              • @elliottcable 根据您的环境,只需使用i(不区分大小写)标志。
              • 您拒绝版本 1 到 3 和 5。为什么?
              【解决方案14】:

              所以,我认为 Richard Bronosky 实际上有迄今为止最好的答案,但我认为你可以做一些事情让它更简单(或至少更简洁):

              re_uuid = re.compile(r'[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}', re.I)
              

              【讨论】:

              • 更简洁:re_uuid = re.compile(r'[0-9a-f]{8}(?:-[0-9a-f]{4}){4}[0-9a-f]{8}', re.I)
              • 如果您希望使用捕获组从字符串中实际捕获数据,使用这不是一个好主意。它看起来有点简单,但在一些用法上却复杂了。
              【解决方案15】:

              @ivelin:UUID 可以有大写字母。所以你要么需要 toLowerCase() 字符串或使用:

              [a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}

              本来只是对此发表评论但没有足够的代表:)

              【讨论】:

              • 通常您可以通过将模式定义为不区分大小写并在模式后加上 i 来处理此问题,这样会产生更清晰的模式:/[0-9a-f]{8}-[0-9a- f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i
              • @ThomasBindzus 该选项并非在所有语言中都可用。这个答案中的原始模式在 Go 中对我有用。 /.../i 版本没有。
              • 对于未来的读者:/i 不是唯一的方法。 Go(而且不仅)支持模式开头的“(?i)”,例如 (?i)[a-f0-9].... ,这也会使整个模式不区分大小写。 (?i) 使右侧的所有内容不区分大小写。对口(?-i).
              【解决方案16】:

              我同意,根据定义,您的正则表达式不会遗漏任何 UUID。但是,请注意,如果您特别搜索 Microsoft 的全球唯一标识符 (GUID),则 GUID 有五种等效的字符串表示形式:

              "ca761232ed4211cebacd00aa0057b223" 
              
              "CA761232-ED42-11CE-BACD-00AA0057B223" 
              
              "{CA761232-ED42-11CE-BACD-00AA0057B223}" 
              
              "(CA761232-ED42-11CE-BACD-00AA0057B223)" 
              
              "{0xCA761232, 0xED42, 0x11CE, {0xBA, 0xCD, 0x00, 0xAA, 0x00, 0x57, 0xB2, 0x23}}" 
              

              【讨论】:

              • 在什么情况下会找到第一个模式?即是否有一个 .Net 函数可以去除连字符或返回不带连字符的 GUID?
              • 您可以使用 myGuid.ToString("N") 获取它。
              【解决方案17】:

              根据定义,UUID 是 32 个十六进制数字,由连字符分成 5 组,正如您所描述的。你不应该错过任何你的正则表达式。

              http://en.wikipedia.org/wiki/Uuid#Definition

              【讨论】:

              • 不正确。 RFC4122 仅允许 [1-5] 用于版本数字,而 [89aAbB] 用于变体数字。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多