使用正则表达式在文本中搜索 UUID

Posted

技术标签:

【中文标题】使用正则表达式在文本中搜索 UUID【英文标题】:Searching for UUIDs in text with regex 【发布时间】: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 的所有变体提供单个正则表达式。但看起来这里给出了这样的答案:***.com/a/13653180/421049 【参考方案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 -Emysql 等),这可能更容易阅读和记住:

[[: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 RFC 的4.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 匹配 _ 以及十六进制数字。用 \pXDigit 替换 \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] 用于变体数字。

以上是关于使用正则表达式在文本中搜索 UUID的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript 正则表达式

JavaScript 正则表达式

正则表达式

优化在文本搜索中使用正则表达式的 psql 查询

用正则表达式regexp进行高级搜索数据

正则表达式