Procurando UUIDs em texto com regex

224

Estou procurando por UUIDs em blocos de texto usando um regex. Atualmente, estou confiando na suposição de que todos os UUIDs seguirão um padrão de 8-4-4-4-12 dígitos hexadecimais.

Alguém pode pensar em um caso de uso em que essa suposição seria inválida e me faria perder alguns UUIDs?

Cara
fonte
Esta pergunta de 6 anos atrás era para me ajudar com um projeto para encontrar cartões de crédito em um bloco de texto. Posteriormente, abri o código que está vinculado a partir do meu blog, explicando as nuances que os UUIDs estavam causando ao procurar por cartões de crédito guyellisrocks.com/2013/11/…
Guy
4
Uma pesquisa pela correspondência de padrões de expressão regular UUID me levou a esta postagem de estouro de pilha, mas a resposta aceita não é realmente uma resposta. Além disso, o link que você forneceu no comentário abaixo da sua pergunta também não possui o padrão (a menos que esteja faltando alguma coisa). Uma dessas respostas é algo que você acabou usando?
Tass
Se você seguir o conjunto de links que começou com o que eu postei, você poderá encontrar essa linha no GitHub, que possui o regex que eu finalmente usei. (É compreensível que seja difícil encontrá-lo.) Esse código e esse arquivo podem ajudá-lo: github.com/guyellis/CreditCard/blob/master/Company.CreditCard/…
Guy
1
Nenhuma dessas respostas parece fornecer um único regex para todas as variantes de apenas UUIDs RFC 4122 válidos. Mas parece que essa resposta foi dada aqui: stackoverflow.com/a/13653180/421049
Garret Wilson

Respostas:

41

Concordo que, por definição, seu regex não perde nenhum UUID. No entanto, pode ser útil observar que, se você estiver pesquisando especialmente os GUIDs (Globally Unique Identifiers) da Microsoft, há cinco representações de seqüência de caracteres equivalentes para um 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}}" 
Panos
fonte
3
Em que situações o primeiro padrão seria encontrado? Existe uma função .Net que retire os hífens ou retorne o GUID sem hífens?
Guy
1
Você pode obtê-lo com myGuid.ToString ("N").
Panos
462

O regex para 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
Ivelin
fonte
19
faça isso [a-f0-9]! Como é hexadecimal! Seu regex (como está) pode retornar falsos positivos.
exhuma 25/09
13
Em alguns casos, você pode querer fazer isso [a-fA-F0-9] ou [A-F0-9].
Hans-Peter Störr
22
@ cyber-monk: [0-9a-f] é idêntico a [a-f0-9] e [0123456789abcdef] em significado e velocidade, uma vez que o regex é transformado em uma máquina de estado de qualquer maneira, com cada dígito hexadecimal em um entrada em uma tabela de estado. Para um ponto de entrada sobre como isso funciona, consulte en.wikipedia.org/wiki/Nondeterministic_finite_automaton
JesperSM 3/12/12
10
Esta solução não está correta. Corresponde aos IDs que possuem versão e caracteres variantes inválidos por RFC4122. A solução da @Gajus é mais correta nesse sentido. Além disso, o RFC permite caracteres maiúsculos na entrada; portanto, adicionar [AF] seria apropriado.
broofa 6/02/2013
4
@broofa, vejo que você está realmente definido para todos que correspondem apenas a UUIDs que são consistentes com a RFC. No entanto, acho que o fato de você ter mencionado isso tantas vezes é um indicador sólido de que nem todos os UUIDs usarão a versão RFC e os indicadores de variantes. A definição de UUID en.wikipedia.org/wiki/Uuid#Definition indica um padrão 8-4-4-4-12 simples e 2 ^ 128 possibilidades. O RFC representa apenas um subconjunto disso. Então, o que você quer combinar? O subconjunto, ou todos eles?
Bruno Bronosky
120

@ivelin: UUID pode ter maiúsculas. Então você precisará toLowerCase () a string ou usará:

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

Teria acabado de comentar isso, mas não o suficiente rep :)

Matthew F. Robben
fonte
22
Geralmente, você pode lidar com isso definindo o padrão como não diferenciando maiúsculas de minúsculas de um i após o padrão, isso torna um padrão mais limpo: / [0-9a-f] {8} - [0-9a-f] {4} - [0 -9a-f] {4} - [0-9a-f] {4} - [0-9a-f] {12} / i
Thomas Bindzus 27/02/16
@ThomasBindzus Essa opção não está disponível em todos os idiomas. O padrão original nesta resposta funcionou para mim no Go. A /.../iversão não.
Chris Redford
110

Os UUIDs da versão 4 têm o formato xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx, em que x é qualquer dígito hexadecimal e y é um de 8, 9, A ou B. por exemplo, f47ac10b-58cc-4372-a567-0e02b2c3d479.

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

Portanto, isso é tecnicamente mais correto:

/[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}/
Gajus
fonte
Eu não acho que você quer dizer az.
de Bruno Bronosky
8
Também é necessário aceitar [AF]. Pela seção 3 da RFC4122: 'Os valores hexadecimais "a" a "f" são exibidos como caracteres minúsculos e não diferenciam maiúsculas de minúsculas na entrada ". Também (:?8|9|A|B)é provavelmente um pouco mais legível como[89aAbB]
broofa
1
Precisa copiar a modificação do @ broofa; como o seu exclui A ou B.
minúsculo
6
@elliottcable Dependendo do seu ambiente, basta usar o isinalizador (sem distinção entre maiúsculas e minúsculas).
Gajus 14/01
20
Você está rejeitando as versões 1 a 3 e 5. Por que?
iGEL 24/06
90

Se você deseja verificar ou validar uma versão UUID específica , aqui estão as regexes correspondentes.

Observe que a única diferença é o número da versão , explicado no 4.1.3. Versioncapítulo do RFC UUID 4122 .

O número da versão é o primeiro caractere do terceiro grupo [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
Ivan Gabriele
fonte
Os padrões não incluem letras minúsculas. Também deve conter a-fao lado de cada A-Fescopo.
Paweł Psztyć
27
O ino final do regex o marca como sem distinção entre maiúsculas e minúsculas.
johnhaley81
Um modificador de padrão nem sempre pode ser usado. Por exemplo, em uma definição de openapi, o padrão
diferencia
1
@StephaneJanicaud No OpenAPI, você deve usar o formatmodificador configurando-o para "uuid" em vez de usar um regex para testar UUIDs: swagger.io/docs/specification/data-models/data-types/#format
Ivan Gabriele
Obrigado @IvanGabriele pela dica, foi apenas um exemplo, é o mesmo problema quando você não deseja verificar nenhum padrão sem distinção entre maiúsculas e minúsculas.
Stephane Janicaud 27/03
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

O regexp de Gajus rejeita UUID V1-3 e 5, mesmo que eles sejam válidos.

iGEL
fonte
1
Mas permite versões inválidas (como 8 ou A) e variantes inválidas.
Brice
Observe que AB em [89AB] [0-9a-f] está em maiúscula e o restante dos caracteres permitidos está em minúscula. Ele chamou-me para fora em Python
Tony Sépia
17

[\w]{8}(-[\w]{4}){3}-[\w]{12} funcionou para mim na maioria dos casos.

Ou se você quiser ser realmente específico [\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12}.

JimP
fonte
3
Vale a pena notar que \ w, em Java, pelo menos, corresponde a _ e também a dígitos hexadecimais. Substituir \ w por \ p {XDigit} pode ser mais apropriado, pois é a classe POSIX definida para combinar dígitos hexadecimais. Isso pode ser interrompido ao usar outros caracteres Unicode tho.
precisa saber é o seguinte
1
@oconnor \wgeralmente significa "caracteres de palavras". Ele corresponderá a muito mais do que dígitos hexadecimais. Sua solução é muito melhor. Ou, para compatibilidade / legibilidade que você pode usar[a-f0-9]
exhuma
1
Aqui é uma string que se parece com um regex e combinar esses padrões, mas é uma expressão regular inválida: 2wtu37k5-q174-4418-2cu2-276e4j82sv19
Travis Stevens
@OleTraveler não é verdade, funciona como um encanto. 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 Essa string (2wt ...) é um UUID inválido, mas o padrão fornecido nesta resposta corresponde a essa string, indicando falsamente que é um UUID válido. Pena que não me lembro por que esse UUID é inválido.
Travis Stevens
10

No python re, você pode abranger de alfa numérico a maiúsculo. Assim..

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

Isso torna a regex UUID do Python mais simples:

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

Vou deixar como um exercício para o leitor usar o timeit para comparar o desempenho deles.

Aproveitar. Mantenha-o Pythonic ™!

OBSERVAÇÃO: esses intervalos também corresponderão :;<=>?@'; se você suspeitar que isso possa dar falsos positivos, não use o atalho. (Obrigado Oliver Aubert por apontar isso nos comentários.)

Bruno Bronosky
fonte
2
[0-F] realmente corresponderá a 0-9 e AF, mas também qualquer caractere cujo código ASCII esteja entre 57 (para 9) e 65 (para A), ou seja, qualquer um de:; <=>? @ '.
Olivier Aubert
7
Portanto, não use o código acima, exceto se você quiser considerar: =>;? <;: - <@ =: - @ =; = - @; @: -> == @?> =:? = @; como um UUID válido :-)
Olivier Aubert
9

Por definição, um UUID tem 32 dígitos hexadecimais, separados em 5 grupos por hífens, exatamente como você descreveu. Você não deve perder nada com sua expressão regular.

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

pix0r
fonte
2
Incorreto. RFC4122 permite apenas [1-5] para o dígito da versão e [89aAbB] para o dígito da variante.
broofa 06/02
6

Então, acho que Richard Bronosky realmente tem a melhor resposta até o momento, mas acho que você pode fazer um pouco para torná-lo um pouco mais simples (ou pelo menos terso):

re_uuid = re.compile(r'[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}', re.I)
Christopher Smith
fonte
1
Even terser:re_uuid = re.compile(r'[0-9a-f]{8}(?:-[0-9a-f]{4}){4}[0-9a-f]{8}', re.I)
Pedro Gimeno
5

Variante para 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];
Anton K
fonte
5

Para UUID gerado no OS X com uuidgen, o padrão regex é

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

Verifique com

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}"
Quanlong
fonte
2
$UUID_RE = join '-', map { "[0-9a-f]{$_}" } 8, 4, 4, 4, 12;

BTW, permitir apenas 4 em uma das posições é válido apenas para UUIDv4. Mas a v4 não é a única versão UUID que existe. Também conheci a v1 na minha prática.

abufct
fonte
1

Se você estiver usando o Regex Posix ( grep -EMySQL, etc.), pode ser mais fácil ler e lembrar:

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

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

Por exemplo:

$> 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
asherbar
fonte