Lendo arquivos OFX no R

Há um tempo atrás, a Nubank (se você não sabe o que é Nubank, deveria) postou em sua página que era possível exportar as faturas fechadas, para serem levadas a programas de organização financeira. Apesar de não usar nenhum programa do tipo, resolvi dar uma olhada no arquivo disponibilizado. Admito que esperava encontrar um arquivo amigável e de fácil manipulação no R, ou até mesmo no Excel. Mas o que encontrei foi um arquivo no formato OFX. 

O formato OFX (Open Financial Exchange) foi criado em 1997, visando criar um padrão de arquivos para troca de informações financeira, seja entre instituições, seja entre instituição e cliente. Pesquisando um pouco sobre o formato, descobri que ele é baseado em XML - ótimo, o R lê arquivos XML através de diversos pacotes. Mas infelizmente eles não funcionaram - não pesquisei muito sobre as diferenças entre OFX e XML, mas acredito que seja devido ao OFX permitir que tags não sejam fechadas, enquanto todas tags XML devem ser fechadas, entre outras diferenças. Procurando na internet encontrei esse ODBC Driver, que possibilita a leitura de arquivos OFX com o pacote RODBC. Por não ser gratuito, nem tentei utilizar e fui para o plano B: fazer meu próprio código.

A função parse_OFX() está disponível no meu GitHub (junto tem também um arquivo OFX de exemplo), e seu uso é bastante simples: parse_OFX(readLines('C:/Exemplo.ofx')). Ela é toda baseada em expressões regulares (regex), que nos permite procurar por padrões de caracteres em strings. Segue a explicação dos principais padrões utilizados:

/^[^<]*|[^ >]*$/ - Para remover todos caracteres antes da primeira tag e depois da última.
  • 1a alternativa: ^[^<]*
    • ^ -  Indica que deve ser no início da string
    • [^<]* - casa qualquer caractere diferente (^) de <, em qualquer quantidade (*)
  • 2a alternativa: [^ >]*$
    • [^ >]* - casa qualquer caractere diferente (^) de >, em qualquer quantidade (*)
    • $ - Indica que deve ser no final da string
<[^/>]*>: - Para localizar todas as tags
  • < - casa com o caractere <
  • [^/>]*- casa qualquer caractere diferente (^) de > e /, em qualquer quantidade
  • > - casa com o caractere >

<%s>.*?</%1$s> - Para localizar início e fim de tags (o %s e %1$s é substituído por uma tag através do sprintf(), no exemplo: <ofx>.*?<ofx>)

  • <ofx> - casa com a string <ofx>
  • .*? - casa com qualquer caractere, em qualquer quantidade (*), mas a menor possível (?)
  • </ofx>- casa com a string </ofx>
A explicação desses 3 padrões é suficiente para entender os demais padrões utilizados no comando. Outra explicação importante sobre exressão regular é o comando utilizado, que possuem diferentes funções:
  • gsub(): Permite realizar substituições de strings que se encaixam no padrão informado
  • grepl(): Indica se o padrão informado está presente na string
  • gregexpr(): Informa a posição que o padrão foi encontrado dentro da string, além do comprimento dele.
Após ler o arquivo OFX, ainda é possível transformar a fatura em um data.frame, para facilitar as análises e gráficos, através do comando:

do.call(rbind, mapply(as.data.table, tail(dados$OFX$CREDITCARDMSGSRSV1$CCSTMTTRNRS$CCSTMTRS$BANKTRANLIST, -2), SIMPLIFY = FALSE))


 Único detalhe é que tudo é lido como string e, ao transformar em data.frame, eles viram fatores - incluindo os valores das operações!

A ideia desta postagem era mostrar um pouco mais sobre o quão poderosas são as funções do R de expressões regulares, e incentivar quem não utiliza a dar uma olhada nelas. Dúvida? Sugestões? Deixe seu comentário!

2 comentários:

  1. olá, tentei usar sua função mas preciso do pacote parseOFX.
    > extrato<-parse_OFX(readLines("C:/Users/praoi/Documents/bbapp/extrato.ofx"))
    Error: could not find function "parseOFX"
    In addition: Warning message:
    In readLines("C:/Users/praoi/Documents/bbapp/extrato.ofx") :
    incomplete final line found on 'C:/Users/praoi/Documents/bbapp/extrato.ofx'
    poderia dar um help? sou newbee em R.

    ResponderExcluir
    Respostas
    1. Pedro, renomeie a função para parseOFX na 1a linha

      Ao invés de:
      parse_OFX <- function(x) {
      Deve ser:
      parseOFX <- function(x) {

      E o comando passa a ser extrato<-parseOFX(readLines("C:/Users/praoi/Documents/bbapp/extrato.ofx"))

      Excluir