2010-11-11 18 views
1

Mon code Python prend en charge la lecture et l'écriture des données dans un format de fichier créé par d'autres, appelé BLT format. Le format BLT est un espace blanc et un retour à la ligne indépendante, car une nouvelle ligne est traitée comme n'importe quel autre espace blanc. L'entrée principale dans ce format est un « bulletin » qui se termine par un « 0 », par exemple,lecture de fichiers très volumineux dont le format est indépendant de la nouvelle ligne

1 2 3 0 

Le format est newline indépendant, il pourrait aussi être écrit comme

1 2 
3 0 

Ou vous pourrait avoir plusieurs bulletins de vote sur une ligne:

1 2 3 0 4 5 6 0 

Ces fichiers peuvent être très volumineux, donc je ne veux pas lire un fichier en mémoire. La lecture par ligne est compliquée car les données ne sont pas basées sur des lignes. Quel est un bon moyen de traiter ces fichiers de manière efficace sur le plan de la mémoire?

+0

Êtes-vous intéressé par le format BLT simple ou le format étendu (Open STV)? Le lien que vous avez donné est la seule spécification pour chaque format? –

+0

La divulgation complète, j'ai écrit la page wiki liée et conçu le format étendu. Je m'intéresse au format original où il est indépendant de Newline (ce qui n'est pas expliqué dans la page wiki parce que j'encourage les gens à utiliser les nouvelles lignes). –

Répondre

3

Pour moi, la façon la plus simple de résoudre ce problème est de générer des générateurs.

def tokens(filename): 
    with open(filename) as infile: 
     for line in infile: 
      for item in line.split(): 
       yield int(item) 

def ballots(tokens): 
    ballot = [] 
    for t in tokens: 
     if t: 
      ballot.append(t) 
     else: 
      yield ballot 
      ballot = [] 

t = tokens("datafile.txt") 

for b in ballots(t): 
    print b 

Je vois @katrielalex posté une solution utilisant un générateur alors que je posais le mien. La différence entre les nôtres est que j'utilise deux générateurs distincts, un pour les jetons individuels dans le fichier et un pour la structure de données spécifique que vous souhaitez analyser. Le premier est transmis à ce dernier en tant que paramètre, l'idée de base étant que vous pouvez écrire une fonction comme ballots() pour chacune des structures de données que vous souhaitez analyser. Vous pouvez itérer sur tout ce qui est généré par le générateur, ou appeler le next() sur l'un ou l'autre générateur pour obtenir le prochain jeton (préparez-vous à une exception StopIteration ou écrivez les générateurs pour générer une valeur sentinelle telle que None lorsque ils n'ont plus de données réelles et vérifient cela).

Il serait assez simple d'emballer le tout dans une classe. En fait ...

class Parser(object): 

    def __init__(self, filename): 

     def tokens(filename): 
      with open(filename) as infile: 
       for line in infile: 
        for item in line.split(): 
         yield int(item) 

     self.tokens = tokens(filename) 

    def ballots(self): 
     ballot = [] 
     for t in self.tokens: 
      if t: 
       ballot.append(t) 
      else: 
       yield ballot 
       ballot = [] 

p = Parser("datafile.txt") 

for b in p.ballots(): 
    print b 
+0

Cette solution (deux générateurs) est préférable à la réponse d'un seul générateur, mais ne va pas assez loin. Le format de fichier a d'autres structures en plus de la structure "vote". Vous avez donc besoin d'un générateur pour analyser le fichier en jetons (** pouvant inclure du texte entre guillemets **) et un module de reconnaissance (qui peut être un générateur) par structure différente. –

+0

Oui, je n'ai pas vraiment regardé le format de fichier complet. – kindall

+0

Merci pour cette réponse! L'absence d'une méthode prête à l'emploi pour saisir les n éléments suivants dans un fichier bloque les données en Python par rapport à la facilité de 'scan' de R ou même' read' de Fortran. Cette implémentation est très intelligente et efficace --- mais pas évidente pour les nouveaux arrivants. – Sharpie

1

Utilisez un generator:

>>> def ballots(f): 
...  ballots = [] 
...  for line in f: 
...    for token in line.split(): 
...      if token == '0': 
...        yield ballots 
...        ballots = [] 
...      else: 
...        ballots.append(token) 

Cela lire la ligne de fichiers en ligne, répartis sur tous les espaces, et ajoutez les jetons dans la ligne une par une à une liste. Chaque fois qu'un zéro est atteint, ce bulletin est yield ed et la liste est réinitialisée pour être vide.

+0

J'aime l'idée, mais il semble que ce serait lent à parcourir chaque caractère ... Je me demande si je peux obtenir l'efficacité et la vitesse de la mémoire. –

+0

Vous pourriez l'essayer avant de se plaindre de la lenteur. :-) Il lit une ligne à la fois, donc ce n'est pas comme si vous aviez un appel d'E/S pour chaque élément en cours de lecture. – kindall

+0

Il ** ne ** itère pas sur chaque caractère; 'char' est juste un nom très trompeur pour une donnée qui peut contenir' "-2" 'ou' "10" '(selon le lien donné) ou (probablement)' str (any_old_integer) ' –