Usando Expressões Regulares na Linguagem C
Thobias Salazar Trevisan
%%date(%d/%m/%Y)

%! Cmdline : --style er_c.css -t html --toc
%!postproc(html): @@AB@@ <font color="blue">AB</font>

=Introdução=

[Expressão Regular http://www.opengroup.org/onlinepubs/007908799/xbd/re.html]
é um método que utiliza alguns caracteres com significado especial para
especificar um padrão de texto. O suporte a expressões regulares
(também conhecida como regexp, regex e ER) pode ser
encontrado em programas como sed, awk e grep, em editores de texto como vi e
emacs, e em linguagens de programação como C, perl, java, python, php etc.
Como se pode ver, um dia as expressões regulares vão dominar o mundo! 8=)

Se você não entendeu ou não sabe o que são, pra que servem ou como usar?
acesse http://aurelio.net/er e divirta-se!!

A utilização de ER pode facilitar muito a programação de
//parsers//, validação de dados, busca de textos... Mas
não vou ficar falando dos benefícios de usá-las ou como elas podem aumentar
sua  produtividade, economizar muito o seu tempo, transformar tarefas
chatas em emocionantes etc. O objetivo deste texto é mostrar como
utilizar ER na linguagem C.

Expressões regulares têm três interfaces para C:
[a projetada pelo GNU http://www.delorie.com/gnu/docs/regex/regex_44.html],
[a compatível com BSD http://docs.freebsd.org/info/regex/regex.info.BSD_Regex_Functions.html]
e
[a compatível com POSIX http://www.opengroup.org/onlinepubs/009695399/basedefs/regex.h.html].
A última será a abordada neste texto.

**Pré-requisito**: Conhecimentos básicos de expressões regulares e da
linguagem de programação C.

Como de costume, este texto é extremamente prático. Aperte o sinto,
segure-se na poltrona e vamos começar. =8)

=Início=

Existem quatro funções para ER POSIX em C:
[regcomp http://www.opengroup.org/onlinepubs/009695399/functions/regcomp.html],
[regexec http://www.opengroup.org/onlinepubs/009695399/functions/regexec.html],
[regerror http://www.opengroup.org/onlinepubs/009695399/functions/regerror.html]
e [regfree http://www.opengroup.org/onlinepubs/009695399/functions/regfree.html].
O cabeçalho destas funções está no arquivo
[regex.h http://www.opengroup.org/onlinepubs/009695399/basedefs/regex.h.html].
Este arquivo também tem os //defines// para duas estruturas:
//regex_t// e //regmatch_t//.

A utilização de ER em C se dá através de dois passos:
Primeiro deve-se compilar/converter a ER (string) em um
% não ficou clara a linha anterior
% ER string, achar uma expressão melhor
//pattern buffer//, que em POSIX, é do tipo //regex_t//.
Após este passo, pode-se casar a ER compilada com o input.

Para compilar uma dada ER em um //pattern buffer// utiliza-se a função
**regcomp**:

--- int regcomp(regex_t *preg, const char *regex, int cflags);

'preg' é um ponteiro para um //pattern buffer// (regex_t). 'regex' é um
ponteiro para uma string que contém a expressão regular. 'cflags' é
utilizada para determinar o tipo de compilação. As 'cflags' são:

 | **REG_EXTENDED** | para usar a sintaxe de POSIX [Extended Regular Expression http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap09.html], caso contrário é utilizado POSIX [Basic Regular Expression http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap09.html]. |
 | **REG_ICASE** | para ignorar maiúsculas e minúsculas (//ignore case//). |
 | **REG_NOSUB** | os parâmetros //nmatch// e //pmatch// da função //regexec// são ignorados. Utilizado somente para saber se a ER casa ou não. |
 | **REG_NEWLINE** | mesmo que o input tenha várias linhas, serão tratadas como se fossem independentes. ex: '^1' e '^1$' com uma entrada '1\n1' casariam duas vezes. '1\n1' casaria uma. |

Após compilar a ER pode-se tentar casá-la com uma dada entrada (//input//) através da função
**regexec**:

---
int regexec(const  regex_t  *preg,  const  char *string, size_t nmatch,
                   regmatch_t pmatch[], int eflags);
---

'preg' é um ponteiro para o //pattern buffer// (regex_t). 'nmatch' e 'pmatch'
têm informações sobre a localização dos //matches// na entrada (//input//). 'eflags' é
usado para alterar o comportamento do //match//:

 | **REG_NOTBOL** | o metacaractere '^' quando usado para marcar início de linha não tem efeito, ou seja, início da string passada (char *string) não deve ser considerado início de linha. |
 | **REG_NOTEOL** | o mesmo que REG_NOTBOL, mas para o caractere '$' que marca final de linha. |

Você entenderá o motivo destas flags no decorrer do tutorial. Não se
preocupe com elas agora.


==Casa ou Não Casa==

Esta pergunta depende muito do contexto. :)

Chega de teoria, hora da prática para clarear as idéias. O exemplo mais simples é
saber se uma expressão regular casa ou não com uma determinada entrada.

%!include(html): 'match.html'

Executando:

---
prompt> ./match '12' '1234567890'
Casou

prompt> ./match '^12' '1234567890'
Casou

prompt> ./match '^ 12' '1234567890'
Não Casou

prompt> ./match '[a-z]' '1234567890'
Não Casou
---

==String de Erro==

Executando o programa anterior com uma ER inválida tem-se a seguinte
saída:

---
prompt> ./match '[a-z' '1234567890'
erro regcomp
---

Pode-se utilizar a função **regerror** para transformar o código de erro
retornado por //regcomp// e //regexec// em uma mensagem de erro e, assim,
dando uma dica do problema na ER.

---
 size_t regerror(int errcode, const regex_t *preg, char *errbuf,  size_t
                       errbuf_size);
---

'errcode' é o erro retornado por //regcomp// ou //regexec//. 'preg' é um
ponteiro para o //pattern buffer// . 'errbuf' um buffer que conterá a
mensagem de erro. 'errbuf_size' é o tamanho da string de erro.

Se a função //regerror// for chamada com //errbuf// como NULL e //errbuf_size// como
zero, ela retorna o tamanho da mensagem de erro.

%!include(html): 'er_error.html'

Analisando algumas mensgens de erro:

---
prompt> ./er_error '[a-z' '1234567890'
Invalid regular expression

prompt> ./er_error '(12\1' '1234567890'
Invalid back reference

prompt> ./er_error '[a-#]' '1234567890'
Invalid range end
---


==my_grep==

Para finalizar esta seção, um programa que faz um //grep// em um
arquivo, ou seja, mostra somente as linhas que casam com a expressão
regular passada na linha de comando.

%!include(html): 'my_grep.html'

=Onde Casa=

Até agora não foi utilizada a estrutura //regmatch_t// porque queria-se saber
somente se a ER casava ou não com a entrada. Para saber qual parte da string de
entrada a ER casou, deve-se utilizar a estrutura //regmatch_t// que contém pelo menos
os seguintes campos:

 | regoff_t | rm_so | o deslcocamento (número de bytes) do início da string até o início do //match//, ou seja, primeiro caractere que a ER casou. |
 | regoff_t | rm_eo | o deslocamento (número de bytes) do início da string até o caractere depois do //match//, ou seja, o caractere após o último que a ER casou. |

Estas informações dizem respeito a **um** //match//. A função
//regexec// não realiza todos os //matches// possíveis da linha. Ela vai lendo a
entrada da esquerda para direita e após o primeiro match a função retorna.

Exemplo: imagine a seguinte ER: '12'. Com a seguinte entrada: '12012'. Dada a
premissa acima, a função //regexec// retornará com sucesso e terá na
estrutura //regmatch_t// o deslocamento do primeiro //match//, ou seja,
'**12**012'. Se chamarmos novamente //regexec// com a mesma entrada, ele
retornará as mesmas informações. Para tentar casar novamente a ER com a
linha, deve-se passar como entrada '012', assim eliminando do início da
string até o último caractere casado (//rm_eo//).

Outro detalhe:
Com a seguinte ER: '^12'. Com uma entrada: '1212'. Teria-se 2 //matches//.
Como ?! Após o primeiro //match// teria-se que chamar a //regexec//
com uma parte da string original, ou seja, os últimos dois caracteres '12',
que casaria novamente. Para resolver este problema, tem-se a
flag //REG_NOTBOL// que 'avisa' a //regexec// que o operador '^', que
marca início de linha, sempre falhará.

Com a ajuda do exemplo abaixo espera-se sedimentar estas regras:

%!include(html): 'match2.html'

Executando, tem-se a seguinte saída:

---
prompt> ./match2 '12' '12012'
********** string original **********
12012

início da string de pesquisa atual no caractere 0
string de pesquisa = "12012"
casou do caractere = 0 ao 2

início da string de pesquisa atual no caractere 2
string de pesquisa = "012"
casou do caractere = 1 ao 3

Número total de casamentos = 2


prompt> 
prompt> 
prompt> ./match2 '2|6' '1234567890'
********** string original **********
1234567890

início da string de pesquisa atual no caractere 0
string de pesquisa = "1234567890"
casou do caractere = 1 ao 2

início da string de pesquisa atual no caractere 2
string de pesquisa = "34567890"
casou do caractere = 3 ao 4

Número total de casamentos = 2
---

E como último exemplo do tutorial, um grep colorido, onde o programa mostra
somente as linhas que casarem com a ER e coloca as partes que casarem com a
ER em uma cor diferente.

%!include(html): 'grep_colorido.html'

---
prompt> echo -e "ABCD\nEFG\nHIABZ"
ABCD
EFG
HIABZ
prompt> 
prompt> ./grep_colorido 'AB' <(echo -e "ABCD\nEFG\nHIABZ")
@@AB@@CD
HI@@AB@@Z
---

**PS**: note que o 'grep_colorido' espera como //argv[2]// um arquivo (file
descriptor). O comando utilizado no exemplo só funciona porque a estrutura
do tipo '<()' é expandida para um //fd// (file descriptor) pelo shell.

=Considerações Finais=

Espero que este texto tenha sido útil para você e que consiga tirar
proveito de ER em seus programas C.

Baixe todos os programas do tutorial: [prog_er.tgz prog_er.tgz]

Ah, para colocar o código em C coloridinho, bonitinho, bem fru-fru,
fiz um pequeno script em sed. Se quiser baixar
[colorize_c.sed ../bin/colorize_c.sed]

NOTA: Conversando com o Aurélio sobre o colorize_c.sed, ele me falou que o
próprio VIM faz isso, ie, faz um 2html com a 'syntax highlighting' atual
para as linguagens que ele suporta
(//ls /usr/share/vim/vim*/syntax/2html.vim//).
De qualquer maneira foi divertido fazer o sed. 8=)

=====================================================================

This HTML page is [[pwrbytxt2tags-2.png] http://txt2tags.sf.net]
(see [source er_c.t2t])
