Pergunta

Existe uma maneira para indicar que dois ou mais regex frases podem ocorrer em qualquer ordem? Por exemplo, atributos XML podem ser escritos em qualquer ordem. Dizer que tenho o seguinte XML:

<a href="home.php" class="link" title="Home">Home</a>
<a href="home.php" title="Home" class="link">Home</a>

Como eu poderia escrever uma partida que verifica a classe e título e obras para ambos os casos? Eu estou olhando principalmente para a sintaxe que me permite verificar em qualquer ordem, e não apenas combinando a classe e título como eu posso fazer isso. Existe alguma maneira, além de apenas incluindo tanto combinações e conectá-los com um '|'?

Editar :. Minha preferência seria fazê-lo em um único regex como eu estou construindo lo através de programação e também unidade de testá-lo

Foi útil?

Solução

Não, eu acredito que a melhor maneira de fazê-lo com um único RE é exatamente como você descreve. Infelizmente, ele vai ficar muito confuso quando seu XML pode ter 5 atributos diferentes, dando-lhe um grande número de diferentes REs para verificar.

Por outro lado, eu não estaria fazendo isso com um RE em tudo desde que eles não estão destinadas a ser linguagens de programação. O que há de errado com a velha abordagem moda de usar uma biblioteca de processamento de XML?

Se você é necessário para usar um RE, esta resposta provavelmente não ajuda muito, mas eu acredito em usar as ferramentas certas para o trabalho.

Outras dicas

Você considerou xpath? (Onde a ordem atributo não importa)

//a[@class and @title]

Será que seleccionar ambos os nós <a> como jogos válidos. A única ressalva sendo que a entrada deve ser XHTML (XML bem formadas).

Você pode criar uma visão antecipada para cada um dos atributos e ligá-los em um regex para toda a tag. Por exemplo, o regex para a marca poderia ser

<a\b[^<>]*>

Se você estiver usando isso em XML você provavelmente vai precisar de algo mais elaborado. Por si só, esta regex base vai coincidir com uma etiqueta com zero ou mais atributos. Em seguida, você adicionar um lookhead para cada um dos atributos que você deseja corresponder:

(?=[^<>]*\s+class="link")
(?=[^<>]*\s+title="Home")

O [^<>]* deixa-lo a varredura frente para o atributo, mas não vai deixá-lo olhar para além do suporte de ângulo de fechamento. Combinando o espaço em branco que leva aqui no lookahead serve a dois propósitos: é mais flexível do que combinando-o na regex base, e garantir que estamos combinando um nome de atributo todo. Combinando-os obtemos:

<a\b(?=[^<>]*\s+class="link")(?=[^<>]*\s+title="Home")[^<>]+>[^<>]+</a>

É claro, eu fiz algumas hipóteses simplificadoras para maior clareza. Eu não permitir espaços em branco ao redor do sinais de igual, por aspas simples ou sem aspas em torno dos valores de atributos, ou por colchetes nos valores de atributos (o que eu ouço é legal, mas eu nunca vi isso feito). Conectando esses vazamentos (se precisar) fará com que o mais feio regex, mas não vai exigir alterações na estrutura básica.

Você pode usar grupos para puxar os atributos fora do tag nomeado. Execute o regex e, em seguida, um loop sobre os grupos fazer o que quer que os testes que você precisa.

Algo parecido com isto (não testado, usando .net regex sintaxe com o \ w por caracteres de palavras e \ s para o espaço em branco):

<a ((?<key>\w+)\s?=\s?['"](?<value>\w+)['"])+ />

A maneira mais fácil seria escrever um regex que pega a parte <a .... >, e em seguida, escrever mais duas expressões regulares para retirar a classe eo título. Embora você provavelmente poderia fazê-lo com um único regex, seria muito complicado e, provavelmente, muito mais propenso a erros.

Com um único regex você precisaria de algo como

<a[^>]*((class="([^"]*)")|(title="([^"]*)"))?((title="([^"]*)")|(class="([^"]*)"))?[^>]*>

Que é apenas um primeiro palpite mão sem verificar para ver se é mesmo válido. Muito mais fácil simplesmente dividir e conquistar o problema.

solução Uma primeira ad hoc poderia ser a de fazer o seguinte.

((class|title)="[^"]*?" *)+

Isto está longe de ser perfeito, pois permite que todos os atributos de ocorrer mais de uma vez. Eu poderia imaginar que isso pode ser solucionável com afirmações. Mas se você só quer extrair o atribui isso pode já estar sufficent.

Se você quiser combinar uma permutação de um conjunto de elementos, você pode usar uma combinação de referências anteriores, e de largura de zero correspondência frente negativo.

Digamos que você queira combinar com qualquer uma dessas seis linhas:

123-abc-456-def-789-ghi-0AB
123-abc-456-ghi-789-def-0AB
123-def-456-abc-789-ghi-0AB
123-def-456-ghi-789-abc-0AB
123-ghi-456-abc-789-def-0AB
123-ghi-456-def-789-abc-0AB

Você pode fazer isso com o seguinte regex:

/123-(abc|def|ghi)-456-(?!\1)(abc|def|ghi)-789-(?!\1|\2)(abc|def|ghi)-0AB/

As referências anteriores (\1, \2), permitem que você se referir a seus jogos anteriores, e zero largura correspondente a frente ((?!...)) permite que você negar um jogo posicional, dizendo não correspondem se o partidas contidos nesta posição. Combinando as duas marcas se que o seu jogo é uma permutação legítimo dos elementos dados, com cada possibilidade única que ocorre uma vez.

Assim, por exemplo, em Ruby:

input = <<LINES
123-abc-456-abc-789-abc-0AB
123-abc-456-abc-789-def-0AB
123-abc-456-abc-789-ghi-0AB
123-abc-456-def-789-abc-0AB
123-abc-456-def-789-def-0AB
123-abc-456-def-789-ghi-0AB
123-abc-456-ghi-789-abc-0AB
123-abc-456-ghi-789-def-0AB
123-abc-456-ghi-789-ghi-0AB
123-def-456-abc-789-abc-0AB
123-def-456-abc-789-def-0AB
123-def-456-abc-789-ghi-0AB
123-def-456-def-789-abc-0AB
123-def-456-def-789-def-0AB
123-def-456-def-789-ghi-0AB
123-def-456-ghi-789-abc-0AB
123-def-456-ghi-789-def-0AB
123-def-456-ghi-789-ghi-0AB
123-ghi-456-abc-789-abc-0AB
123-ghi-456-abc-789-def-0AB
123-ghi-456-abc-789-ghi-0AB
123-ghi-456-def-789-abc-0AB
123-ghi-456-def-789-def-0AB
123-ghi-456-def-789-ghi-0AB
123-ghi-456-ghi-789-abc-0AB
123-ghi-456-ghi-789-def-0AB
123-ghi-456-ghi-789-ghi-0AB
LINES

# outputs only the permutations
puts input.grep(/123-(abc|def|ghi)-456-(?!\1)(abc|def|ghi)-789-(?!\1|\2)(abc|def|ghi)-0AB/)

Para uma permutação de cinco elementos, seria:

/1-(abc|def|ghi|jkl|mno)-
 2-(?!\1)(abc|def|ghi|jkl|mno)-
 3-(?!\1|\2)(abc|def|ghi|jkl|mno)-
 4-(?!\1|\2|\3)(abc|def|ghi|jkl|mno)-
 5-(?!\1|\2|\3|\4)(abc|def|ghi|jkl|mno)-6/x

Para o seu exemplo, a regex seria

/<a href="home.php" (class="link"|title="Home") (?!\1)(class="link"|title="Home")>Home<\/a>/
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top