Formatando moeda por meio de expressão regular

Encontrei hoje um requisito interessante para formatar um número flutuante como moeda. Nos Estados Unidos e em muitos outros países, a moeda maior ou igual a 1000 unidades ($ 1.000,00) é normalmente dividida em tríades por meio de um delimitador de vírgula. Minha reação inicial foi usar uma função recursiva, mas dei uma olhada na gem ActiveSupport do Rails para ver como o number_with_delimitermétodo deles executava essa mesma tarefa. Fiquei surpreso ao saber que isso foi tratado por meio de uma expressão regular inteligente:

/(d)(?=(d{3})+(?!d))/

Isso usa alguns dos que eu consideraria os conceitos mais avançados de expressões regulares: antevisão positiva e antevisão negativa .

Visão Positiva à Frente

Na página Rdoc para a classe de expressão regular de Ruby:

garante que os seguintes caracteres correspondam a pat, mas não inclui esses caracteres no texto correspondido

Podemos ver em nossa expressão que estamos procurando um dígito, então usamos a ?=sintaxe de lookahead positivo para procurar uma ou mais instâncias de uma tríade de dígitos. Isso corresponderia 1ao padrão 1234e 123ao padrão 123456.

Antecipação Negativa

Na página Rdoc:

garante que os seguintes caracteres não correspondam a pat, mas não inclui esses caracteres no texto correspondente

Este é o inverso do lookahead positivo e é invocado por ?!. Além disso, definimos nossa expressão capturando apenas um dígito que segue outro dígito. Acrescentando isso à nossa regra de lookahead positivo, selecionamos apenas dígitos cuja posição é um múltiplo de 3 quando esse dígito é precedido por outro dígito. Isso é um bocado, mas significa que estamos excluindo uma vírgula onde já existe um presente.

Inserindo a Vírgula

Agora que declaramos nossa expressão, podemos usar isso em uma chamada de substituição de string para interpolar nossas vírgulas em nossas posições identificadas usando uma referência anterior, seguida por nossa vírgula (ou qualquer outro delimitador):

Em Ruby:

"10000".gsub(/(d)(?=(d{3})+(?!d))/, "\1,") # => "10,000"

Em Javascript:

"10000".replace(/(d)(?=(d{3})+(?!d))/g, "$1,") # => "10,000"

O segundo argumento para os métodos de substituição de string é o valor pelo qual substituir nosso padrão. Aqui, vemos que nossa referência anterior tem uma vírgula anexada a ela.

Conclusão

As expressões regulares são uma ferramenta poderosa. O que me impressiona sobre esse padrão é que ele é tão conciso, mas elimina a necessidade de chamadas de funções recursivas mais complexas ou looping. O padrão aqui é definir pontos de inserção para o nosso delimitador, ao mesmo tempo que respeita todas as regras de formatação de moeda. As coisas que você aprende olhando sob o capô!