O problema com os URLs

Os URLs são coisas simples. Ou pelo menos é o que o senhor pensa. Digamos que o senhor queira detectar um URL em um bloco de texto e convertê-lo em um hiperlink genuíno. Não há problema, certo?

Visite meu site em http://www.example.com, ele é incrível!

Para localizar o URL no texto acima, uma expressão regular simples deve ser suficiente: procuraremos uma string em um limite de palavra que comece com http:// , seguida por um ou mais caracteres sem espaço:

bhttp://[^s]+

É muito fácil. Isso parece funciona. Há muitos softwares de fóruns e discussões por aí que fazem links automáticos usando exatamente essa abordagem. Embora funcione na maioria das vezes, está longe de ser perfeito. E se o bloco de texto tivesse a seguinte aparência?

Meu site (http://www.example.com) é fantástico.

Esse URL será codificado incorretamente com o parêntese final. A propósito, esse é um extremamente comum que os usuários comuns do dia a dia incluam URLs em seus textos.

O que é realmente agravante é que o parênteses em URLs são perfeitamente legais. Eles são fazem parte das especificações e tudo mais:

somente alfanuméricos, os caracteres especiais “$-_.+!*'(),”e os caracteres reservados usados para seus fins reservados podem ser usados sem codificação em um URL.

Alguns sites, principalmente a Wikipedia e o MSDN, adoram gerar URLs com parênteses. Os sites são péssimos com essas coisas:

http://en.wikipedia.org/wiki/PC_Tools_(Central_Point_Software)
http://msdn.microsoft.com/en-us/library/aa752574(VS.85).aspx

Os URLs com parênteses reais significam que não podemos seguir o caminho mais fácil e ignorar o parêntese final. O senhor poderia forçar os usuários a escapar dos parênteses, mas isso é um pouco draconiano e não é razoável esperar que os usuários saibam como escapar de caracteres no URL.

http://en.wikipedia.org/wiki/PC_Tools_%28Central_Point_Software%29
http://msdn.microsoft.com/en-us/library/aa752574%28VS.85%29.aspx

Para detectar URLs corretamente em todos Na maioria dos casos, o senhor precisa criar algo mais sofisticado. É verdade que esse não é o problema mais difícil da ciência da computação, mas é um problema que muitos programadores erram. Até mesmo programadores com anos de experiência, como, por exemplo, Paul Graham.

Se formos mais inteligentes na construção da expressão regular, poderemos fazer um trabalho melhor.

(?bhttp://[-A-Za-z0-9+&@#/%?=~_()|!:,.;]*[-A-Za-z0-9+&@#/%=~_()|]
  1. A principal melhoria aqui é que estamos apenas aceitando uma lista de permissões de caracteres de URL reconhecidamente bons. Permitir caracteres aleatórios arbitrários em URLs é se preparar para explorações de XSS, e posso dizer ao senhor que por experiência própria. Não faça isso!
  2. Só permitimos que determinados caracteres “terminem” o URL. Terminar um URL com sinais de pontuação comuns, como ponto final, ponto de exclamação, ponto e vírgula etc., significa que esses caracteres serão considerados caracteres de fim de hiperlink e não serão incluídos no URL.
  3. Os parênteses, se presentes, são permitidos no URL, e absorvemos o parêntese inicial, se ele também estiver presente.

Não consegui encontrar uma maneira de o regex sozinho distinguir entre URLs que legitimamente terminam em parênteses (ala Wikipedia) e URLs que o usuário colocou em parênteses. Portanto, deve haver um punhado de código postfix para detectar e descartar os parênteses colocados pelo usuário nos URLs correspondentes:

if (s.StartsWith("(") && s.EndsWith(")"))
{
return s.Substring(1, s.Length - 2);
}

Isso é muito trabalho extra, apenas porque a especificação de URL permite parênteses. Não podemos consertar a Wikipedia ou o MSDN e certamente não podemos alterar a especificação de URL. Mas podemos garantir que o nosso evite tornar-se parte do problema. Evite usar parênteses (ou qualquer caractere incomum) nos URLs que o senhor criar. Eles são irritantes de usar e raramente são tratados corretamente pelo código de vinculação automática.