A página de referência da F5 para o operador matches_regex traz uma observação curta: é mais eficiente usar um comando de string do que uma expressão regular, e a maioria dos casos pode ser resolvida com string match ou scan. O scan desmonta um valor usando um formato, como o sscanf do C, e guarda os pedaços em variáveis. Os iRules rodam o 8.4.6 (F5 K6091), então o scan descrito aqui é o dele.

Como ler um formato

Um formato é uma sequência de três tipos de diretiva:

  • espaço em branco pula qualquer quantidade de espaço em branco na entrada, inclusive nenhuma;
  • texto literal precisa casar com a entrada caractere por caractere;
  • conversões começam com % e leem um valor: %d um inteiro decimal, %x hexadecimal, %o octal, %i um inteiro cuja base vem do prefixo, %s caracteres até um espaço em branco, %c um caractere como seu código, %[...] caracteres de um conjunto (%[^&] é tudo até um "e comercial"), e %n quanto da entrada foi consumido até ali. Um * depois do % converte sem guardar, e um número define uma largura máxima.
% scan "10:30" "%d:%d" hours minutes
2
% scan "192.168.1.20" "%d.%d.%d.%d" a b c d
4
% scan "user=abc&x=1" {user=%[^&]} user
1
% set user
abc

São sessões do tclsh: cada comando vem depois do prompt % e o resultado fica na linha seguinte. O terceiro formato está entre chaves: entre aspas duplas, o Tcl executaria [^&] como comando antes de o scan vê-lo (o Tcl 8.4.6 para com invalid command name "^&"), então coloque entre chaves todo formato que tenha [.

O que o resultado significa

Com nomes de variáveis, scan devolve o número de conversões que guardaram um valor, o que não é o mesmo que "a entrada casou":

  • -1: a entrada acabou antes da primeira conversão (um valor vazio, por exemplo);
  • 0: uma conversão foi tentada e não casou (scan "abc" "%d" n);
  • um número menor que o esperado: a leitura parou no meio, e toda variável depois desse ponto não foi definida por este scan, então mantém o valor que tinha antes.

Sem nomes de variáveis, scan devolve os valores convertidos como uma lista.

Como a contagem só conta conversões, um literal que falha depois da última conversão não muda o resultado. A página de matches_regex da F5 mostra esta verificação como alternativa com scan a uma expressão regular para "www., depois um nome, depois .com":

scan [string tolower [HTTP::host]] {www.%[a-z0-9.:-].com} host_token

Ela devolve 1 para www.example.com, e também 1 para www.example.org: o conjunto inclui o ponto, então consome example.org e o literal .com nunca é alcançado. Mesmo tirando o ponto do conjunto, o .com que falha não diminuiria a contagem. Para verificar o que vem depois da última conversão, termine o formato com %n e exija que ele seja guardado:

if { [scan [string tolower [HTTP::host]] {www.%[a-z0-9-].com%n} name used] == 2
     && $used == [string length [HTTP::host]] } {
    pool www_pool
}

Detalhes do Tcl 8.4.6 que o manual não explicita

  • Os inteiros passam por um int em C. scan "3000000000" "%d" n guarda -1294967296, e o modificador de tamanho l não muda isso no 8.4.6. Para manter um número grande intacto, leia-o como texto com %[0-9] e deixe o expr interpretá-lo, já que o expr trabalha com inteiros de 64 bits.
  • %n conta bytes. O manual fala em caracteres, mas o Tcl 8.4.6 conta os bytes do seu interno, então cada caractere fora do conta 2 ou 3.
  • %i segue as regras de prefixo, então 08 lido com %i dá 0 e para no 8. Use %d para valores decimais com zero à esquerda, como horários.

Quando o scan é a ferramenta certa

O scan brilha quando a forma do valor é fixa: um endereço, um horário, uma string de versão, uma linha de requisição, um prefixo conhecido seguido de um valor. Quando o valor é livre, string first e string range, o getfield da F5 ou um switch -glob costumam ser mais claros. Para um valor com separador fixo, compare o scan com findstr, substr e getfield.

Experimente

O explicador de scan alinha cada diretiva com os caracteres que ela consumiu, mostra o valor de cada variável e diz exatamente por que a leitura parou, incluindo os comportamentos do Tcl 8.4.6 acima.

Fontes

A observação sobre eficiência e o exemplo www./.com vêm da referência de matches_regex da F5; as conversões e os códigos de resultado, do manual do Tcl 8.4; o armazenamento em int e a contagem em bytes foram confirmados rodando o próprio Tcl 8.4.6.