Dividir tokens na string usando Regex em c #

9

Eu tenho alguns modelos "tokenizados", por exemplo (eu chamo tokens a parte entre chaves duplas):

var template1 = "{{TOKEN1}} is a {{TOKEN2}} and it has some {{TOKEN3}}";

Eu quero extrair uma matriz desta frase, para ter algo como:

Array("{{TOKEN1}}",
      " is a ",
      "{{TOKEN2}}", 
      " and it has some ", 
      "{{TOKEN3}}");

Eu tentei conseguir isso com o seguinte código Regex:

Regex r = new Regex(@"({{[^\}]*}})");
var n = r.Split(template1);

E o resultado é:

Array("",
      "{{TOKEN1}}",
      " is a ",
      "{{TOKEN2}}", 
      " and it has some ", 
      "{{TOKEN3}}",
      "");

O primeiro problema foi que não consegui recuperar os tokens da frase. Eu resolvi isso apenas adicionando os parênteses na expressão Regex, embora eu não saiba por que isso resolve isso.

O problema que estou enfrentando atualmente é o termo extra vazio no início e / ou no final da matriz quando os primeiros e / ou últimos termos do modelo são "tokens" . Por que isso está acontecendo? Estou fazendo algo errado, ou eu deveria sempre verificar essas duas posições para o vazio?

No meu código, preciso saber qual termo veio de um token e qual era uma posição fixa no modelo. Nesta solução, terei que verificar a posição de cada array para uma string começando com "{{" e terminando com "}}", o que não acho que seja a melhor possibilidade. Então, se alguém encontrar uma solução melhor para quebrar essas coisas, ficarei feliz em saber!

Obrigado!

Editar: conforme solicitado, vou postar um exemplo simples de porque preciso dessa distinção em tokens e texto.

public abstract class TextParts { }
public class TextToken : TextParts { }
public class TextConstant : TextParts { }

var list = new List<TextParts>();
list.Add( new TextToken("{{TOKEN1}}") );
list.Add( new TextConstant(" is a ") );
list.Add( new TextToken("{{TOKEN2}}") );
/* and so on */

Dessa forma, eu terei uma lista das partes que compõem minha string e eu poderei gravar isso no meu banco de dados para permitir futuras manipulações e substituições. De fato, cada um desses TOKEN será substituído por uma string Regex.

O objetivo é que os usuários possam inserir mensagens como "{{SERVER}} não está escutando na porta {{PORT}}", e eu poderei substituir "{{SERVER}}" por [a-zA-Z0-9 ]+ e "{{PORT}}" para \d{1,5} . Faz sentido?

Espero que isso torne o post mais claro.

    
por tyron 13.10.2012 в 19:57
fonte

2 respostas

5

Se você dividir uma string ao longo de delimitadores e a string começar ou terminar com um delimitador, isso significa que há um elemento vazio antes / depois do primeiro / último delimitador:

Imagine a seguinte linha em um arquivo CSV:

,a,b,c,

Essa linha do CSV contém os elementos "" , "a" , "b" , "c" e "" .

O mesmo acontece com o seu {{TOKEN}} . Você poderia usar um método diferente:

MatchCollection allMatchResults = null;
Regex regexObj = new Regex(@"\{\{[^{}]*\}\}|[^{}]+");
allMatchResults = regexObj.Matches(subjectString);

Se chaves únicas puderem ocorrer dentro ou entre tokens, você também pode usar

Regex regexObj = new Regex(@"\{\{(?:(?!\}\}).)*\}\}|(?:(?!\{\{).)+");

que será um pouco menos eficiente, no entanto, por causa de todas as asserções antecipadas, então você deve usar isto somente se você precisar.

Editar: Acabei de perceber que havia outra pergunta em sua postagem: por que você precisou adicionar parênteses em torno do seu regex para "trabalhar"? Resposta: Geralmente, um comando split() retorna apenas o conteúdo entre os delimitadores. Se você colocar os delimitadores (ou partes deles) na captura de parênteses, então o que for correspondido dentro desses parênteses também será adicionado à lista resultante.

    
por Tim Pietzcker 13.10.2012 / 20:13
fonte
0

Experimente este padrão, ele irá obter seus tokens como correspondências.

\b*\{{2}\w+\}{2}\b*
    
por Rob 13.10.2012 / 20:18
fonte