Ricerca di UUID nel testo con regex


224

Sto cercando UUID in blocchi di testo usando una regex. Attualmente sto facendo affidamento sul presupposto che tutti gli UUID seguiranno una serie di 8-4-4-4-12 cifre esadecimali.

Qualcuno può pensare a un caso d'uso in cui questo presupposto sarebbe invalido e mi farebbe perdere alcuni UUID?


Questa domanda di 6 anni fa era di aiutarmi con un progetto per trovare carte di credito in un blocco di testo. Successivamente ho aperto il codice che è collegato dal mio post sul blog che spiega la sfumatura che gli UUID stavano causando durante la ricerca di carte di credito guyellisrocks.com/2013/11/…
Guy

4
Una ricerca della corrispondenza del modello di espressione regolare UUID mi ha portato a questo post di overflow dello stack ma la risposta accettata in realtà non è una risposta. Inoltre, il link che hai fornito nel commento sotto la tua domanda non ha lo schema (a meno che non mi manchi qualcosa). Una di queste risposte è qualcosa che hai finito per usare?
Tass

Se segui il labirinto di coniglio dei collegamenti che iniziano con quello che ho pubblicato, potresti trovare questa riga in GitHub che ha la regex che finalmente ho usato. (Comprendibile che è difficile da trovare.) Quel codice e quel file potrebbero aiutarti: github.com/guyellis/CreditCard/blob/master/Company.CreditCard/…
Guy

1
Nessuna di queste risposte sembra dare un unico regex per tutte le varianti di soli UUID RFC 4122 validi. Ma sembra che una risposta del genere sia stata data qui: stackoverflow.com/a/13653180/421049
Garret Wilson

Risposte:


41

Accetto che per definizione il tuo regex non manchi alcun UUID. Tuttavia, può essere utile notare che se stai cercando soprattutto gli identificatori univoci globali (GUID) di Microsoft, ci sono cinque rappresentazioni di stringa equivalenti per un 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
In quali situazioni si trova il primo modello? es. Esiste una funzione .Net che rimuove i trattini o restituisce il GUID senza trattini?
Guy,

1
Puoi ottenerlo con myGuid.ToString ("N").
Panos,

462

La regex per 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

19
fallo [a-f0-9]! Come è esadecimale! La tua regex (così com'è) potrebbe restituire falsi positivi.
exhuma,

13
In alcuni casi potresti persino voler creare [a-fA-F0-9] o [A-F0-9].
Hans-Peter Störr,

22
@ cyber-monk: [0-9a-f] è identico a [a-f0-9] e [0123456789abcdef] nel significato e nella velocità, poiché la regex viene comunque trasformata in una macchina a stati, con ogni cifra esadecimale trasformata in un voce in una tabella di stato. Per un punto di accesso a come funziona, vedere en.wikipedia.org/wiki/Nondeterministic_finite_automaton
JesperSM

10
Questa soluzione non è del tutto corretta. Corrisponde agli ID con versione non valida e caratteri varianti per RFC4122. La soluzione di @Gajus è più corretta al riguardo. Inoltre, RFC consente l'immissione di caratteri maiuscoli in ingresso, quindi l'aggiunta di [AF] sarebbe appropriata.
Broofa,

4
@broofa, vedo che sei davvero impostato su tutti che corrispondono solo agli UUID che sono coerenti con la RFC. Tuttavia, penso che il fatto che tu abbia dovuto sottolinearlo così tante volte è un solido indicatore del fatto che non tutti gli UUID useranno gli indicatori di versione e variante RFC. La definizione UUID en.wikipedia.org/wiki/Uuid#Definition afferma un semplice schema 8-4-4-4-12 e 2 ^ 128 possibilità. La RFC rappresenta solo un sottoinsieme di ciò. Quindi cosa vuoi abbinare? Il sottoinsieme o tutti?
Bruno Bronosky,

120

@ivelin: UUID può avere maiuscole. Quindi dovrai oLowerCase () la stringa o usare:

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

Avrei solo commentato questo, ma non abbastanza rappresentante :)


22
Di solito puoi gestirlo definendo il modello come maiuscolo / minuscolo con una i dopo il modello, questo rende un modello più pulito: / [0-9a-f] {8} - [0-9a-f] {4} - [0 -9a-f] {4} - [0-9a-f] {4} - [0-9a-f] {12} / i
Thomas Bindzus,

@ThomasBindzus Quell'opzione non è disponibile in tutte le lingue. Lo schema originale in questa risposta ha funzionato per me in Go. La /.../iversione no.
Chris Redford,

110

Gli UUID versione 4 hanno il formato xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx dove x è una cifra esadecimale e y è uno di 8, 9, A o B. es. F47ac10b-58cc-4372-a567-0e02b2c3d479.

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

Pertanto, questo è tecnicamente più corretto:

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

Non penso che tu intenda az.
Bruno Bronosky,

8
È necessario accettare anche [AF]. Per la sezione 3 di RFC4122: "I valori esadecimali da" a "a" f "vengono emessi come caratteri minuscoli e non fanno distinzione tra maiuscole e minuscole sull'input ". Inoltre (:?8|9|A|B)è probabilmente leggermente più leggibile come[89aAbB]
broofa

1
È necessario copiare la modifica di @ broofa; come la tua esclude la minuscola A o B.
ELLIOTTCABLE

6
@elliottcable A seconda del proprio ambiente, basta usare il iflag (senza distinzione tra maiuscole e minuscole).
Gajus,

20
Stai rifiutando la versione da 1 a 3 e 5. Perché?
iGEL

90

Se si desidera verificare o convalidare una versione UUID specifica , ecco le regex corrispondenti.

Nota che l'unica differenza è il numero di versione , che è spiegato nel 4.1.3. Versioncapitolo di UUID 4122 RFC .

Il numero di versione è il primo carattere del terzo gruppo [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

I motivi non includono lettere minuscole. Dovrebbe anche contenere a-faccanto a ciascun A-Fambito.
Paweł Psztyć,

27
Alla ifine del regex lo segna come maiuscole e minuscole.
johnhaley81,

Non è sempre possibile utilizzare un modificatore di motivi. Ad esempio, in una definizione openapi, lo schema fa distinzione tra maiuscole
Stephane Janicaud il

1
@StephaneJanicaud In OpenAPI, dovresti piuttosto usare il formatmodificatore impostandolo su "uuid" invece di usare un regex per testare gli UUID: swagger.io/docs/specification/data-models/data-types/#format
Ivan Gabriele

Grazie @IvanGabriele per il suggerimento, era solo un esempio, è lo stesso problema quando non si desidera controllare alcun modello insensibile al caso.
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

Il regexp di Gajus rifiuta UUID V1-3 e 5, anche se sono validi.


1
Ma consente versioni non valide (come 8 o A) e varianti non valide.
Brice,

Si noti che AB in [89AB] [0-9a-f] è in maiuscolo e il resto dei caratteri consentiti è in minuscolo. Mi ha catturato in Python
Tony Sepia il

17

[\w]{8}(-[\w]{4}){3}-[\w]{12} ha funzionato per me nella maggior parte dei casi.

O se vuoi essere davvero specifico [\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12}.


3
Vale la pena notare che \ w, almeno in Java, corrisponde a _ e alle cifre esadecimali. Sostituire \ w con \ p {XDigit} potrebbe essere più appropriato in quanto questa è la classe POSIX definita per la corrispondenza delle cifre esadecimali. Ciò potrebbe interrompersi quando si utilizzano altri set di caratteri Unicode.
oconnor0

1
@oconnor di \wsolito significa "caratteri parola" Corrisponderà molto più delle cifre esadecimali. La tua soluzione è molto meglio. Oppure, per compatibilità / leggibilità che potresti usare[a-f0-9]
exhuma,

1
Ecco una stringa che sembra una regex e corrisponde a quei pattern, ma è una regex non valida: 2wtu37k5-q174-4418-2cu2-276e4j82sv19
Travis Stevens

@OleTraveler non è vero, funziona come un fascino. 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 Quella stringa (2wt ...) è un UUID non valido, ma il modello indicato in questa risposta corrisponde a quella stringa indicando erroneamente che è un UUID valido. È un peccato che non ricordi perché l'UUID non sia valido.
Travis Stevens,

10

In python re, puoi passare dall'alfabeto numerico a quello maiuscolo. Così..

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

Ciò rende il regex UUID Python più semplice:

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

Lascio che sia un esercizio per il lettore usare timeit per confrontare le prestazioni di questi.

Godere. Keep it Pythonic ™!

NOTA: anche questi intervalli coincideranno :;<=>?@', quindi, se sospetti che possa darti falsi positivi, non prendere la scorciatoia. (Grazie Oliver Aubert per averlo sottolineato nei commenti.)


2
[0-F] corrisponderà effettivamente a 0-9 e AF, ma anche a qualsiasi carattere il cui codice ASCII è compreso tra 57 (per 9) e 65 (per A), vale a dire uno dei seguenti;; <=>? @ '.
Olivier Aubert,

7
Quindi non utilizzare il codice sopra menzionato tranne se si desidera considerare: =>;? <;: - <@ =: - @ =; = - @; @: -> == @?> =:? = @; come UUID valido :-)
Olivier Aubert,

9

Per definizione, un UUID è composto da 32 cifre esadecimali, separate in 5 gruppi da trattini, proprio come hai descritto. Non dovresti perdere nessuno con la tua espressione regolare.

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


2
Non corretto. RFC4122 consente solo [1-5] per la cifra della versione e [89aAbB] per la cifra della variante.
Broofa,

6

Quindi, penso che Richard Bronosky abbia effettivamente la migliore risposta fino ad oggi, ma penso che tu possa fare un po 'per renderlo un po' più semplice (o almeno più terser):

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

1
Ancora più difficile:re_uuid = re.compile(r'[0-9a-f]{8}(?:-[0-9a-f]{4}){4}[0-9a-f]{8}', re.I)
Pedro Gimeno

5

Variante per 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

Per UUID generato su OS X con uuidgen, il modello regex è

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

Verificare con

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;

A proposito, consentire solo 4 su una delle posizioni è valido solo per UUIDv4. Ma v4 non è l'unica versione UUID esistente. Ho incontrato anche v1 nella mia pratica.


1

Se si utilizza Posix regex ( grep -E, MySQL, ecc.), Potrebbe essere più semplice leggere e ricordare:

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

0

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

Per esempio:

$> 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.