91 votes

Comment lire deux lignes d'un fichier à la fois en utilisant python

Je suis à coder un script python qui analyse un fichier texte. Le format de ce fichier texte est telle que chaque élément dans le fichier utilise deux lignes et pour des raisons de commodité, je vais lire les deux lignes avant l'analyse. Cela peut être fait en python?

Je voudrais quelque chose comme:

f = open(filename, "r")
for line in f:
    line1 = line
    line2 = f.readline()

f.close

Mais cela casse en disant que

ValueError: Mixing iteration and read methods would lose data

Quelqu'un aurait-il des idées?

Reconnaissant pour toute aide,

/Daniel

Connexes:

58voto

robince Points 6523

Question similaire ici . Vous ne pouvez pas mélanger itération et readline, vous devez donc utiliser l’un ou l’autre.

 while True:
    line1 = f.readline()
    line2 = f.readline()
    if not line2: break  # EOF
    ...
 

58voto

unutbu Points 222216
import itertools
with open('a') as f:
    for line1,line2 in itertools.izip_longest(*[f]*2):
        print(line1,line2)

izip_longest retourne un itérateur, donc ça devrait bien fonctionner, même si le fichier est très volumineux.

Si il y a un nombre impair de lignes, line2 obtient la valeur None à la dernière itération.

izip_longest est dans itertools si vous avez python 2.6 ou version ultérieure. Si vous utilisez une version antérieure, vous pouvez ramasser un python de mise en œuvre de izip_longest ici:

Dans les commentaires, il a été demandé si cette solution se lit tout le fichier d'abord, puis parcourt le fichier une seconde fois. Je crois qu'il ne le fait pas. L' with open('a') as f ligne ouvre un descripteur de fichier, mais ne pas lire le fichier. f est un itérateur, de sorte que son contenu ne sont pas lus qu'à la demande. izip_longest prend les itérateurs comme arguments et retourne un itérateur.

izip_longest est en effet nourris de la même itérateur, f, deux fois. Mais il arrive que f.next() (ou next(f) en Python3) est appelée sur le premier argument, puis sur le deuxième argument. Depuis next() est appelée sur le même sous-jacent itérateur, les lignes successives sont donné. C'est très différent de la lecture dans l'ensemble du fichier. En effet, le but de l'utilisation des itérateurs est précisément pour éviter de lire dans le fichier en entier.

Je crois donc que la solution fonctionne comme souhaité -- le fichier n'est lu qu'une seule fois par la boucle for.

Pour corroborer cela, j'ai couru le izip_longest solution par rapport à une solution à l'aide d' f.readlines(). J'ai mis un raw_input() à la fin de la pause, les scripts, et a couru ps axuw sur chaque:

% ps axuw | grep izip_longest_method.py

unutbu 11119 2.2 0.2 4520 2712 pts/0 S+ 21:14 0:00 python /home/unutbu/pybin/izip_longest_method.py bigfile

% ps axuw | grep readlines_method.py

unutbu 11317 6.5 8.8 93908 91680 pts/0 S+ 21:16 0:00 python /home/unutbu/pybin/readlines_method.py bigfile

L' readlines clairement lit tout le fichier à la fois. Depuis l' izip_longest_method utilise beaucoup moins de mémoire, je pense qu'il est sûr pour conclure, c'est pas de la lecture de l'ensemble du dossier à la fois.

31voto

ghostdog74 Points 86060

utilisez line.next (), par exemple

 f=open("file")
for line in f:
    print line
    nextline=f.next()
    print "next line", nextline
    ....
f.close()
 

12voto

RedGlyph Points 6046

Je voudrais procéder de la même manière que ghostdog74, seulement avec de l'essayer dehors et quelques modifications:

try:
    with open(filename) as f:
        for line1 in f:
            line2 = f.next()
            # process line1 and line2 here
except StopIteration:
    print "(End)" # do whatever you need to do with line1 alone

Cela permet de maintenir le code simple et robuste. À l'aide de l' with ferme le fichier si quelque chose se passe, ou juste ferme les ressources une fois que vous avez épuisé et sortir de la boucle.

Notez que with des besoins de 2,6, soit 2,5 avec l' with_statement fonction est activée.

5voto

Stefano Borini Points 36904

Fonctionne pour les pairs et impairs, les fichiers de longueur. Il ignore le caractère de la dernière ligne.

f=file("file")

lines = f.readlines()
for even, odd in zip(lines[0::2], lines[1::2]):
    print "even : ", even
    print "odd : ", odd
    print "end cycle"
f.close()

Si vous avez de gros fichiers, ce n'est pas la bonne approche. Vous êtes le chargement de tous les fichiers dans la mémoire avec readlines(). Une fois, j'ai écrit une classe que lire le fichier d'enregistrement de la fseek position de chaque début de ligne. Cela vous permet d'obtenir des lignes spécifiques sans avoir tout le fichier en mémoire, et vous pouvez aussi aller de l'avant et vers l'arrière.

Je colle ici. La licence est dans le domaine Public, de sens, de faire ce que vous voulez avec elle. Veuillez noter que cette classe a été écrit il y a 6 ans et je n'ai pas touché ou vérifié depuis. Je pense que c'est même pas de fichier compatible. Caveat emptor. Aussi, notez que c'est exagéré pour votre problème. Je ne dis pas que vous devriez certainement aller de cette façon, mais j'ai eu ce code et j'ai plaisir à partager si vous avez besoin de plus d'accès complexes.

import string
import re

class FileReader:
    """ 
    Similar to file class, but allows to access smoothly the lines 
    as when using readlines(), with no memory payload, going back and forth,
    finding regexps and so on.
    """
    def __init__(self,filename): # fold>>
        self.__file=file(filename,"r")
        self.__currentPos=-1
        # get file length
        self.__file.seek(0,0)
        counter=0
        line=self.__file.readline()
        while line != '':
            counter = counter + 1
            line=self.__file.readline()
        self.__length = counter
        # collect an index of filedescriptor positions against
        # the line number, to enhance search
        self.__file.seek(0,0)
        self.__lineToFseek = []

        while True:
            cur=self.__file.tell()
            line=self.__file.readline()
            # if it's not null the cur is valid for
            # identifying a line, so store
            self.__lineToFseek.append(cur)
            if line == '':
                break
    # <<fold
    def __len__(self): # fold>>
        """
        member function for the operator len()
        returns the file length
        FIXME: better get it once when opening file
        """
        return self.__length
        # <<fold
    def __getitem__(self,key): # fold>>
        """ 
        gives the "key" line. The syntax is

        import FileReader
        f=FileReader.FileReader("a_file")
        line=f[2]

        to get the second line from the file. The internal
        pointer is set to the key line
        """

        mylen = self.__len__()
        if key < 0:
            self.__currentPos = -1
            return ''
        elif key > mylen:
            self.__currentPos = mylen
            return ''

        self.__file.seek(self.__lineToFseek[key],0)
        counter=0
        line = self.__file.readline()
        self.__currentPos = key
        return line
        # <<fold
    def next(self): # fold>>
        if self.isAtEOF():
            raise StopIteration
        return self.readline()
    # <<fold
    def __iter__(self): # fold>>
        return self
    # <<fold
    def readline(self): # fold>>
        """
        read a line forward from the current cursor position.
        returns the line or an empty string when at EOF
        """
        return self.__getitem__(self.__currentPos+1)
        # <<fold
    def readbackline(self): # fold>>
        """
        read a line backward from the current cursor position.
        returns the line or an empty string when at Beginning of
        file.
        """
        return self.__getitem__(self.__currentPos-1)
        # <<fold
    def currentLine(self): # fold>>
        """
        gives the line at the current cursor position
        """
        return self.__getitem__(self.__currentPos)
        # <<fold
    def currentPos(self): # fold>>
        """ 
        return the current position (line) in the file
        or -1 if the cursor is at the beginning of the file
        or len(self) if it's at the end of file
        """
        return self.__currentPos
        # <<fold
    def toBOF(self): # fold>>
        """
        go to beginning of file
        """
        self.__getitem__(-1)
        # <<fold
    def toEOF(self): # fold>>
        """
        go to end of file
        """
        self.__getitem__(self.__len__())
        # <<fold
    def toPos(self,key): # fold>>
        """
        go to the specified line
        """
        self.__getitem__(key)
        # <<fold
    def isAtEOF(self): # fold>>
        return self.__currentPos == self.__len__()
        # <<fold
    def isAtBOF(self): # fold>>
        return self.__currentPos == -1
        # <<fold
    def isAtPos(self,key): # fold>>
        return self.__currentPos == key
        # <<fold

    def findString(self, thestring, count=1, backward=0): # fold>>
        """
        find the count occurrence of the string str in the file
        and return the line catched. The internal cursor is placed
        at the same line.
        backward is the searching flow.
        For example, to search for the first occurrence of "hello
        starting from the beginning of the file do:

        import FileReader
        f=FileReader.FileReader("a_file")
        f.toBOF()
        f.findString("hello",1,0)

        To search the second occurrence string from the end of the
        file in backward movement do:

        f.toEOF()
        f.findString("hello",2,1)

        to search the first occurrence from a given (or current) position
        say line 150, going forward in the file 

        f.toPos(150)
        f.findString("hello",1,0)

        return the string where the occurrence is found, or an empty string
        if nothing is found. The internal counter is placed at the corresponding
        line number, if the string was found. In other case, it's set at BOF
        if the search was backward, and at EOF if the search was forward.

        NB: the current line is never evaluated. This is a feature, since
        we can so traverse occurrences with a

        line=f.findString("hello")
        while line == '':
            line.findString("hello")

        instead of playing with a readline every time to skip the current
        line.
        """
        internalcounter=1
        if count < 1:
            count = 1
        while 1:
            if backward == 0:
                line=self.readline()
            else:
                line=self.readbackline()

            if line == '':
                return ''
            if string.find(line,thestring) != -1 :
                if count == internalcounter:
                    return line
                else:
                    internalcounter = internalcounter + 1
                    # <<fold
    def findRegexp(self, theregexp, count=1, backward=0): # fold>>
        """
        find the count occurrence of the regexp in the file
        and return the line catched. The internal cursor is placed
        at the same line.
        backward is the searching flow.
        You need to pass a regexp string as theregexp.
        returns a tuple. The fist element is the matched line. The subsequent elements
        contains the matched groups, if any.
        If no match returns None
        """
        rx=re.compile(theregexp)
        internalcounter=1
        if count < 1:
            count = 1
        while 1:
            if backward == 0:
                line=self.readline()
            else:
                line=self.readbackline()

            if line == '':
                return None
            m=rx.search(line)
            if m != None :
                if count == internalcounter:
                    return (line,)+m.groups()
                else:
                    internalcounter = internalcounter + 1
    # <<fold
    def skipLines(self,key): # fold>>
        """
        skip a given number of lines. Key can be negative to skip
        backward. Return the last line read.
        Please note that skipLines(1) is equivalent to readline()
        skipLines(-1) is equivalent to readbackline() and skipLines(0)
        is equivalent to currentLine()
        """
        return self.__getitem__(self.__currentPos+key)
    # <<fold
    def occurrences(self,thestring,backward=0): # fold>>
        """
        count how many occurrences of str are found from the current
        position (current line excluded... see skipLines()) to the
        begin (or end) of file.
        returns a list of positions where each occurrence is found,
        in the same order found reading the file.
        Leaves unaltered the cursor position.
        """
        curpos=self.currentPos()
        list = []
        line = self.findString(thestring,1,backward)
        while line != '':
            list.append(self.currentPos())
            line = self.findString(thestring,1,backward)
        self.toPos(curpos)
        return list
        # <<fold
    def close(self): # fold>>
        self.__file.close()
    # <<fold

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X