domingo, 7 de outubro de 2007

Manipulando arquivos grandes em Python

Marcelo Toledo escreveu um artigo comparando a sua implmentação em C de um corretor ortográfico poposto por Peter Norgiv com a versão original em Python.

Porém Marcelo Toledo ao realizar essa comparação não levou em consideração que o exemplo desenvolvido por Peter Norvig era apensa um protótipo. Sendo assim, ele resolveu comparar ambos os programas, em C e Python, utilizando arquivos cada vez maiores e ilustrando a diferença de performance entre eles.

No código de Peter Norvig ele lê o arquivo de uma vez. Dá para imaginar o que acontenceu, baixa performance e "crash" com arquivos maiores de 100M devido a falta de RAM. :-(
Essa é a linha na qual o programa de Peter Norvig lê o arquivo e processa ele:
NWORDS = train(words(file('big.txt').read()))
Infelizmente Marcelo Toledo não procurou saber qual era o "bug" do código, deixando no ar uma idéia de que C é robusto é Python é uma linguagem não confiável.

Como eu fui questionado por um colega (Robson Peixoto) sobre o "porque" do problema com o código do corretor ortográfico. Resolvi então escrever um pequeno script que copia arquivos binários da forma correta. E assim fica claro que Python não tem problemas em manipular arquivos maiores que 100MB. ;-)

Segue um teste de uso do script copyfile.py

ruda@zion /tmp $ du -sh livecd-i686-installer-2007.0.iso
416M livecd-i686-installer-2007.0.iso

ruda@zion /tmp $ time ./copyfile.py livecd-i686-installer-2007.0
.iso teste.iso

real 0m39.708s
user 0m4.517s
sys 0m3.747s

Ou seja, sem usar muita CPU e nem mesmo RAM, esse script demorou 40
segundos para copiar um arquivo de 416MB, ou seja, 10MB/s aproximadamente.

Abaixo o código do script copyfile.py

#!/bin/env python
# -*- coding: utf-8 -*-
import sys

try:
origem = sys.argv[1]
destino = sys.argv[2]
except IndexError:
print "Modo de usar: copyfile.py origem destino"
sys.exit(1)

#Exemplo de leitura e gravação de arquivos grandes - usando modo binário
input = file(origem, 'rb')
output = file(destino, "wb")
for line in input:
output.write(line)

#Fechando os arquivos
input.close()
output.close()