Tìm kiếm UUID trong văn bản với regex


224

Tôi đang tìm kiếm UUID trong các khối văn bản bằng biểu thức chính quy. Hiện tại tôi đang dựa vào giả định rằng tất cả các UUID sẽ tuân theo một chuỗi các chữ số thập lục phân 8-4-4-4-12.

Bất cứ ai cũng có thể nghĩ về một trường hợp sử dụng trong đó giả định này không hợp lệ và sẽ khiến tôi bỏ lỡ một số UUID?


Câu hỏi này từ 6 năm trước là để giúp tôi với một dự án tìm thẻ tín dụng trong một khối văn bản. Tôi đã sau đó mở nguồn mã được liên kết từ bài viết trên blog của tôi mà giải thích những sắc thái mà các UUIDs đã gây ra khi tìm kiếm thẻ tín dụng guyellisrocks.com/2013/11/...
Guy

4
Một tìm kiếm cho khớp mẫu biểu thức chính quy UUID đã đưa tôi đến bài viết tràn ngăn xếp này nhưng câu trả lời được chấp nhận thực sự không phải là một câu trả lời. Ngoài ra, liên kết bạn cung cấp trong nhận xét bên dưới câu hỏi của bạn cũng không có mẫu (trừ khi tôi thiếu một cái gì đó). Là một trong những câu trả lời mà bạn đã sử dụng?
Tass

Nếu bạn theo dõi các liên kết thỏ bắt đầu với liên kết mà tôi đã đăng, bạn có thể bắt gặp dòng này trong GitHub có regex mà cuối cùng tôi đã sử dụng. (Có thể hiểu rằng rất khó tìm.) Mã đó và tệp đó có thể giúp bạn: github.com/guyellis/CreditCard/blob/master/Company.CreditCard/iêu
Guy

1
Không có câu trả lời nào trong số này dường như đưa ra một biểu thức chính cho tất cả các biến thể của chỉ các UUID RFC 4122 hợp lệ. Nhưng có vẻ như một câu trả lời như vậy đã được đưa ra ở đây: stackoverflow.com/a/13653180/421049
Garret Wilson

Câu trả lời:


41

Tôi đồng ý rằng theo định nghĩa regex của bạn không bỏ lỡ bất kỳ UUID nào. Tuy nhiên, có thể hữu ích khi lưu ý rằng nếu bạn đang tìm kiếm đặc biệt là Số nhận dạng duy nhất toàn cầu (GUID) của Microsoft, có năm cách biểu thị chuỗi tương đương cho 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}}" 

3
Trong những tình huống nào mẫu đầu tiên sẽ được tìm thấy? tức là có một hàm .Net sẽ loại bỏ các dấu gạch nối hoặc trả về GUID mà không có dấu gạch nối?
Chàng trai

1
Bạn có thể lấy nó với myGuid.ToString ("N").
Panos

462

Regex cho uuid là:

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

19
làm cho điều đó [a-f0-9]! Như là hex! Regex của bạn (như nó là) có thể trả về dương tính giả.
shoutuma

13
Trong một số trường hợp, bạn thậm chí có thể muốn tạo [a-fA-F0-9] hoặc [A-F0-9].
Hans-Peter Störr

22
@ cyber-monk: [0-9a-f] giống hệt với [a-f0-9] và [0123456789abcdef] về ý nghĩa và tốc độ, vì dù sao regex được biến thành một máy trạng thái, với mỗi chữ số hex được chuyển thành một mục trong một bảng nhà nước. Để biết một mục nhập về cách thức hoạt động của nó, hãy xem en.wikipedia.org/wiki/Nondeterministic_finite_automaton
JesperSM

10
Giải pháp này không hoàn toàn chính xác. Nó khớp với các ID có phiên bản không hợp lệ và các ký tự biến thể trên RFC4122. Giải pháp của @Gajus đúng hơn về vấn đề đó. Ngoài ra, RFC cho phép các ký tự chữ hoa trên đầu vào, vì vậy việc thêm [AF] sẽ phù hợp.
broofa

4
@broofa, tôi thấy rằng bạn thực sự được thiết lập trên tất cả mọi người chỉ khớp với UUID phù hợp với RFC. Tuy nhiên, tôi nghĩ rằng việc bạn phải chỉ ra điều này rất nhiều lần là một chỉ số chắc chắn rằng không phải tất cả các UUID sẽ sử dụng phiên bản RFC và các chỉ số biến thể. Định nghĩa UUID en.wikipedia.org/wiki/Uuid#DefDef nêu một mẫu 8-4-4-4-12 đơn giản và 2 ^ 128 khả năng. RFC chỉ đại diện cho một tập hợp con của điều đó. Vì vậy, những gì bạn muốn phù hợp? Các tập hợp con, hoặc tất cả chúng?
Bruno Bronosky

120

@ Xoayin: UUID có thể có thủ đô. Vì vậy, bạn sẽ cần phải toLowerCase () chuỗi hoặc sử dụng:

[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}

Sẽ chỉ nhận xét này nhưng không đủ đại diện :)


22
Thông thường, bạn có thể xử lý việc này bằng cách xác định mẫu không phân biệt chữ hoa chữ thường với chữ i sau mẫu, điều này tạo ra mẫu sạch hơn: / [0-9a-f] {8} - [0-9a-f] {4} - [0 -9a-f] {4} - [0-9a-f] {4} - [0-9a-f] {12} / i
Thomas Bindzus

@ThomasBindzus Tùy chọn đó không có sẵn trong tất cả các ngôn ngữ. Mẫu ban đầu trong câu trả lời này đã làm việc cho tôi trong Go. Các /.../iphiên bản thì không.
Chris Redford

110

Các UUID phiên bản 4 có dạng xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx trong đó x là bất kỳ chữ số thập lục phân nào và y là một trong 8, 9, A hoặc B. ví dụ: f47ac10b-58cc-4372-a567-0e02b3.

nguồn: http://en.wikipedia.org/wiki/Uuid#DefDef

Do đó, điều này đúng hơn về mặt kỹ thuật:

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

Tôi không nghĩ bạn có nghĩa là az.
Bruno Bronosky

8
Cần chấp nhận [AF], quá. Trên mỗi phần 3 của RFC4122: 'Các giá trị thập lục phân "a" đến "f" là đầu ra dưới dạng ký tự chữ thường và không phân biệt chữ hoa chữ thường trên đầu vào '. Cũng (:?8|9|A|B)có thể dễ đọc hơn một chút vì[89aAbB]
broalid

1
Cần sao chép sửa đổi của @ broofa; như của bạn loại trừ chữ A hoặc B. chữ thường
ELLIOTTCABLE

6
@elliottcable Tùy thuộc vào môi trường của bạn, chỉ cần sử dụng icờ (không phân biệt chữ hoa chữ thường).
Gajus

20
Bạn đang từ chối Phiên bản 1 đến 3 và 5. Tại sao?
iGEL

90

Nếu bạn muốn kiểm tra hoặc xác nhận một phiên bản UUID cụ thể , đây là các biểu thức chính tương ứng.

Lưu ý rằng sự khác biệt duy nhất là số phiên bản , được giải thích trong 4.1.3. Versionchương của UUID 4122 RFC .

Số phiên bản là ký tự đầu tiên của nhóm thứ ba [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

Các mẫu không bao gồm chữ thường. Nó cũng nên chứa a-fbên cạnh mỗi A-Fphạm vi.
Paweł Psztyć

27
icuối của regex đánh dấu nó là trường hợp không nhạy cảm.
johnhaley81

Một sửa đổi mẫu không thể luôn luôn được sử dụng. Ví dụ: trong định nghĩa openapi, mẫu này phân biệt chữ hoa chữ thường
Stephane Janicaud

1
@StephaneJanicaud Trong OpenAPI, bạn nên sử dụng công cụ formatsửa đổi bằng cách đặt nó thành "uuid" thay vì sử dụng regex để kiểm tra UUID: swagger.io/docs/specifying/data-models/data-types/#format
Ivan

Cảm ơn bạn @IvanGabriele vì tiền boa, đó chỉ là một ví dụ, đó là vấn đề tương tự khi bạn không kiểm tra bất kỳ mẫu không nhạy cảm nào.
Stephane Janicaud

35
/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89AB][0-9a-f]{3}-[0-9a-f]{12}$/i

Regrec của Gajus từ chối UUID V1-3 và 5, mặc dù chúng hợp lệ.


1
Nhưng nó cho phép các phiên bản không hợp lệ (như 8 hoặc A) và các biến thể không hợp lệ.
Brice

Lưu ý rằng AB trong [89AB] [0-9a-f] là chữ hoa và phần còn lại của các ký tự được phép là chữ thường. Nó đã bắt được tôi trong Python
Tony Sepia

17

[\w]{8}(-[\w]{4}){3}-[\w]{12} đã làm việc cho tôi trong hầu hết các trường hợp.

Hoặc nếu bạn muốn thực sự cụ thể [\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12}.


3
Điều đáng chú ý là ít nhất, trong Java, khớp với _ cũng như các chữ số thập lục phân. Thay thế \ w bằng \ p {XDigit} có thể phù hợp hơn vì đó là lớp POSIX được xác định để khớp các chữ số thập lục phân. Điều này có thể bị hỏng khi sử dụng bộ ký tự Unicode khác tho.
oconnor0

1
@oconnor \wthường có nghĩa là "ký tự từ" Nó sẽ khớp nhiều hơn nhiều so với các chữ số hex. Giải pháp của bạn tốt hơn nhiều. Hoặc, để tương thích / dễ đọc, bạn có thể sử dụng[a-f0-9]
shoutuma

1
Đây là một chuỗi trông giống như một regex và khớp với các mẫu đó, nhưng là một regex không hợp lệ: 2wtu37k5-q174-4418-2cu2-276e4j82sv19
Travis Stevens

@OleTraveler không đúng, hoạt động như một lá bùa. 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')
Tomasz Wojcik

3
@tom Chuỗi đó (2wt ...) là một UUID không hợp lệ, nhưng mẫu được đưa ra trong câu trả lời này khớp với chuỗi đó chỉ ra rằng đó là một UUID hợp lệ. Thật tệ khi tôi không nhớ tại sao UUID đó không hợp lệ.
Travis Stevens

10

Trong python re, bạn có thể trải dài từ alpha số đến chữ hoa. Vì thế..

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']

Điều đó làm cho regex Python UUID đơn giản nhất:

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

Tôi sẽ để nó như một bài tập cho người đọc sử dụng thời gian để so sánh hiệu suất của những thứ này.

Thưởng thức. Giữ nó Pythonic ™!

LƯU Ý: Các nhịp đó cũng sẽ khớp với nhau :;<=>?@', vì vậy, nếu bạn nghi ngờ rằng có thể cung cấp cho bạn dương tính giả, đừng dùng phím tắt. (Cảm ơn Oliver Aubert đã chỉ ra điều đó trong các bình luận.)


2
[0-F] thực sự sẽ khớp 0-9 và AF, nhưng cũng có bất kỳ ký tự nào có mã ASCII nằm trong khoảng từ 57 (cho 9) và 65 (cho A), nghĩa là nói bất kỳ:: <=>? @ '.
Olivier Aubert

7
Vì vậy, không sử dụng mã đã nêu ở trên trừ khi bạn muốn xem xét: => ;? <;: - <@ =: - @ =; = - @; @: -> == @?> =:? = @; như một UUID hợp lệ :-)
Olivier Aubert

9

Theo định nghĩa, UUID là 32 chữ số thập lục phân, được phân tách thành 5 nhóm bằng dấu gạch nối, giống như bạn đã mô tả. Bạn không nên bỏ lỡ bất kỳ với biểu hiện thông thường của bạn.

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


2
Không chính xác. RFC4122 chỉ cho phép [1-5] cho chữ số phiên bản và [89aAbB] cho chữ số biến thể.
broofa

6

Vì vậy, tôi nghĩ Richard Bronosky thực sự có câu trả lời tốt nhất cho đến nay, nhưng tôi nghĩ bạn có thể làm một chút để làm cho nó đơn giản hơn một chút (hoặc ít nhất là khó hơn):

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

1
Ngay cả terser:re_uuid = re.compile(r'[0-9a-f]{8}(?:-[0-9a-f]{4}){4}[0-9a-f]{8}', re.I)
Pedro Gimeno

5

Biến thể cho 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];

5

Đối với UUID được tạo trên OS X với uuidgen, mẫu regex là

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

Xác minh với

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}"

2
$UUID_RE = join '-', map { "[0-9a-f]{$_}" } 8, 4, 4, 4, 12;

BTW, chỉ cho phép 4 trên một trong các vị trí chỉ hợp lệ cho UUIDv4. Nhưng v4 không phải là phiên bản UUID duy nhất tồn tại. Tôi đã gặp v1 trong thực tế của tôi là tốt.


1

Nếu sử dụng Posix regex ( grep -E, MySQL, v.v.), điều này có thể dễ đọc và dễ nhớ hơn:

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

0

Đối với 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}"

Ví dụ:

$> 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.