Ler e escrever arquivo csv é um mal necessário de muitos sistemas, ainda mais levando em conta que esta integração será feita via Excel, em algum Windows, com quilos de texto com acentos e dados a formatar. Dado este cenário, e que ele provavelmente se repetirá no futuro, deixo aqui um post auto-ajuda para mim mesmo e provavelmente para você que está lendo. :D
Na versão 1.9.3 e superior, o Ruby incluiu a classe CSV na sua standard lib, que facilita o trabalho de ler e/ou escrever arquivos csv. Exemplos em código abaixo.
Conhecendo o CSV
O modo mais simples e direto para ler um arquivo csv, é usar o CSV.read
que retorna um Array de Arrays:
1 2 3 4 5 |
|
Dentro da classe CSV, existem mais duas classes que facilitam ainda mais o manuseio dos dados.
Caso necessite de mais requinte e sofisticação, o método CSV.table
retorna uma instância de CSV::Table. Com o table, você tem acesso ao cabeçalho através do headers
e acesso a cada linha do arquivo com o each
, que retorna uma instância de CSV::Row.
1 2 3 4 5 6 7 |
|
Tanto o read
quanto o table
, aceitam um hash de options como segundo argumento. Tem uma descrição detalhada na documentação do método new. Exemplo usando options:
1 2 3 |
|
CSV converters
CSV::HeaderConverters contém um hash de symbol e block que são usados para converter os valores do cabeçalho. Para usá-los, você deve informar qual converter deseja aplicar na opção header_converters
. Acredito que o código abaixo explica melhor.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
No exemplo acima, criei o HeaderConverter “remap” que traduz o cabeçalho country para pais e birthday para dt_nascimento. Por padrão, o CSV
disponibiliza os converters downcase e symbol, que por sinal são usados quando usamos o método table
para ler csv.
CSV::Converters segue o mesmo padrão de symbol e block, a única diferença que este é usado para converter os valores da linha. Vamos ao código.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
No exemplo acima criei dois converters. Um para trocar nil por “” e o outro que converte para Date caso o valor esteja no formato 99/99/9999.
Encoding hell com Excel
Normalmente o csv é usado como meio de integração Excel <=> Sistema. Acontece que o Excel não se dá muito bem com acentos especiais como ãõáé etc. Isto porque estamos em 2014. Acontece que quando há caracteres especiais, a única abordagem que funcionou foi exportar para Unicode text. Neste formato, o encoding do arquivo é UTF-16LE e separado por tab (\t). Este post de 2009 da Plataformatec explica com mais detalhes este jeitinho do Excel de ser com os dados. A única diferença de 2009 pra hoje, é que podemos passar o encoding como parâmetro ao ler o arquivo, e por sorte evitar o uso do iconv. Vamos ao código:
1 2 3 4 5 6 7 8 |
|
Evitando o abuso de memória
Ao ler arquivos com read
ou table
, o arquivo é colocado em memória, ou seja, ao processar uma planilha de 100mb, o seu processo ruby vai pra um 100mb e pouco. Agora imagina 20 workers e cada um processando uma planilha de 100mb ou mais, facilmente o seu servidor terá um pico de consumo de memória, o no pior cenário vai dar crash no processo. Para evitar este consumo devemos usar o foreach
do CSV
.
1 2 3 4 5 6 7 8 9 |
|
Desta maneira a leitura é mais otimizada, pois apenas uma linha por vez é lida. O único problema é que perdemos algumas facilidades do table
, como os headers
e a instância do CSV::Row
por linha. Tentando chegar no modelo ideal, montei uma classe que usa o foreach e mesmo assim tem os headers
e os rows
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
|
Por último, uma observação importante: todo este código acima foi rodado no ruby 2.1.0. Espero que este mini guia de como ler arquivo csv com Ruby te ajude. Segue alguns links com mais informações:
- http://www.sitepoint.com/guide-ruby-csv-library-part/
- http://www.sitepoint.com/guide-ruby-csv-library-part-2/
- http://technicalpickles.com/posts/parsing-csv-with-ruby/
- http://blog.plataformatec.com.br/2009/09/exportando-dados-para-excel-usando-csv-em-um-aplicativo-rails/
Dúvidas, sugestões ou qualquer outra coisa. Deixe um comentário ou se preferir, mande um tweety! :D