Pythonを使用してファイルから一度に2行を読み取るにはどうすればよいですか?


82

テキストファイルを解析するPythonスクリプトをコーディングしています。このテキストファイルの形式は、ファイル内の各要素が2行を使用するようになっています。便宜上、解析する前に両方の行を読みたいと思います。これはPythonで実行できますか?

私は次のようなことをしたいと思います:

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

f.close

しかし、これは次のように言って壊れます:

ValueError:反復メソッドと読み取りメソッドを混在させるとデータが失われます

関連:


8
f.readline()をf.next()に変更すれば、準備は完了です。
ポール

その他の回答については、stackoverflow.com / questions / 1528711 / reading-lines-2-at-a-timeを参照してください。
foosion 2009年

@Paulこのf.next()はまだ有効ですか?このエラーが発生しますAttributeError: '_ io.TextIOWrapper'オブジェクトに属性がありません 'next'
SKR

1
代わりに、Python3の@SKRを実行する必要がありnext(f)ます。
ボリス

回答:


50

ここで同様の質問。イテレーションとreadlineを混在させることはできないため、どちらか一方を使用する必要があります。

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

48
import itertools
with open('a') as f:
    for line1,line2 in itertools.zip_longest(*[f]*2):
        print(line1,line2)

itertools.zip_longest() イテレータを返すので、ファイルが数十億行の長さであってもうまく機能します。

行数が奇数の場合、最後の反復でにline2設定さNoneれます。

Python2izip_longestでは、代わりに使用する必要があります。


コメントでは、このソリューションが最初にファイル全体を読み取り、次にファイルを2回繰り返すかどうかを尋ねられています。そうではないと思います。このwith open('a') as f行はファイルハンドルを開きますが、ファイルを読み取りません。fはイテレータであるため、その内容は要求されるまで読み取られません。zip_longestイテレータを引数として取り、イテレータを返します。

zip_longest実際、同じイテレータfが2回供給されます。しかし、最終的に発生するのはnext(f)、最初の引数で呼び出され、次に2番目の引数で呼び出されることです。next()は同じ基になるイテレータで呼び出されるため、連続する行が生成されます。これは、ファイル全体を読み取ることとは大きく異なります。実際、イテレータを使用する目的は、ファイル全体を読み取らないようにすることです。

したがって、ソリューションは希望どおりに機能すると思います。ファイルはforループによって1回だけ読み取られます。

これを裏付けるために、zip_longestソリューションとを使用したソリューションを実行しましたf.readlines()input()スクリプトを一時停止するために最後にを置き、ps axuwそれぞれで実行しました。

% ps axuw | grep zip_longest_method.py

unutbu 11119 2.2 0.2 4520 2712 pts/0 S+ 21:14 0:00 python /home/unutbu/pybin/zip_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

readlines明らかに、一度にファイル全体を読み込み。zip_longest_method使用するメモリがはるかに少ないため、ファイル全体を一度に読み取っていないと結論付けるのが安全だと思います。


6
(*[f]*2)数字を変更するだけで任意のサイズのチャンクを取得できることが示されているので気に入っています(したがって、回答を編集して変更することはしません)が、この場合(f, f)はおそらく入力が簡単です。
スティーブロッシュ

lines代わりにを使用する場合はline1, line2、一度に1行ずつ2読み取るために、1つの番号()を変更する必要がありますn
jfs 2009年

27

使用する next()、例えば

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

1
RedGlyphがこの回答の彼のバージョンで指摘しているように、奇数の行は発生しStopIterationます。
drevicko 2015

2
next()は、例外を回避するためにデフォルトの引数をサポートするようになりました。nextline = next(f,None)
gerardw19年

11

私はghostdog74と同じように進めますが、外で試してみて、いくつかの変更を加えます。

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

これにより、コードがシンプルでありながら堅牢に保たれます。を使用してwith、何か他のことが起こった場合にファイルを閉じるか、ファイルを使い果たしてループを終了したらリソースを閉じます。

with2.6、またはwith_statement機能を有効にした状態で2.5が必要であることに注意してください。


8

これはどうですか、問題を見ている人は誰でも

with open('file_name') as f:
    for line1, line2 in zip(f, f):
        print(line1, line2)

1
ファイルの行数が奇数の場合、これにより最後の行が破棄されます。良い点は、これを拡張して、一度に3行を読み取ることができることfor l1, l2, l3 in zip(f, f, f):です。ライン数が3で割り切れない場合は、再度、最後の1つのまたは2行が廃棄される
ボリス

4

偶数および奇数の長さのファイルで機能します。一致しない最後の行を無視するだけです。

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()

大きなファイルがある場合、これは正しいアプローチではありません。readlines()を使用してメモリ内のすべてのファイルをロードしています。私はかつて、各行頭のfseek位置を保存してファイルを読み取るクラスを作成しました。これにより、すべてのファイルをメモリに保存しなくても特定の行を取得できます。また、前後に移動することもできます。

ここに貼り付けます。ライセンスはパブリックドメインです。つまり、ライセンスを使ってやりたいことを実行します。このクラスは6年前に作成されたものであり、それ以来、触れたり確認したりしていないことに注意してください。ファイルにも準拠していないと思います。警告エンプター。また、これはあなたの問題にとってやり過ぎであることに注意してください。私はあなたが間違いなくこのように行くべきだと言っているわけではありませんが、私はこのコードを持っていて、より複雑なアクセスが必要な場合はそれを共有することを楽しんでいます。

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

特に大きなファイルの場合は、代わりにitertools.izip()を使用することをお勧めします。
RedGlyph 2009年

izipを使用しても、そのようにリストをスライスすると、すべてがメモリに取り込まれます。
Steve Losh

実際には、readlines()呼び出しによってすべてがメモリに取り込まれます。
Steve Losh

私はあなたのクラスが好きではありません。ファイルの初期化中に、ファイル全体を2回繰り返します。短い行の大きなファイルの場合、保存されるメモリはそれほど多くありません。
ゲオルクSchölly

@スティーブ:はい、悲しいことに十分です。ただし、zipはタプルのリスト全体を作成することでメモリに追加のレイヤーを追加し(Python 3でない限り)、izipはタプルを一度に1つずつ生成します。それがあなたの意図したことだと思いますが、とにかく以前のコメントを明確にしたいと思います:-)
RedGlyph 2009年

3
file_name = 'your_file_name'
file_open = open(file_name、 'r')

def handler(line_one、line_two):
    print(line_one、line_two)

file_open中:
    試してください:
        one = file_open.next()
        two = file_open.next() 
        ハンドラー(1、2)
    (StopIteration)を除く:
        file_open.close()
        ブレーク

1
while file_open:while True:この場合と同等であるため、誤解を招く可能性があります。
jfs 2009年

これは意図的なものですが、ループから抜け出すには休憩が必要であることを示す「whileTrue」を実行する方が間違いなくクリーンであることに同意します。私はそれをしないことを選択しました。なぜなら、この方法の方が読みやすく、ファイルを開いたままにしておく必要がある時間と、その間に何が行われるかについては疑いの余地がないと信じているからです。ほとんどの場合、私も「True」を自分で行います。
Martin P. Hellwig

2
def readnumlines(file, num=2):
    f = iter(file)
    while True:
        lines = [None] * num
        for i in range(num):
            try:
                lines[i] = f.next()
            except StopIteration: # EOF or not enough lines available
                return
        yield lines

# use like this
f = open("thefile.txt", "r")
for line1, line2 in readnumlines(f):
    # do something with line1 and line2

# or
for line1, line2, line3, ..., lineN in readnumlines(f, N):
    # do something with N lines

1

私の考えは、ファイルから一度に2行を読み取り、これを2タプルとして返すジェネレーターを作成することです。これは、結果を反復処理できることを意味します。

from cStringIO import StringIO

def read_2_lines(src):   
    while True:
        line1 = src.readline()
        if not line1: break
        line2 = src.readline()
        if not line2: break
        yield (line1, line2)


data = StringIO("line1\nline2\nline3\nline4\n")
for read in read_2_lines(data):
    print read

行数が奇数の場合、完全には機能しませんが、これでアウトラインが適切になります。


1

私は先月同様の問題に取り組みました。f.readline()とf.readlines()でwhileループを試しました。私のデータファイルは巨大ではないので、最終的にf.readlines()を選択しました。これにより、インデックスをより細かく制御できます。それ以外の場合は、f.seek()を使用してファイルポインターを前後に移動する必要があります。

私の場合はOPよりも複雑です。私のデータファイルは毎回解析する行数に関してより柔軟であるため、データを解析する前にいくつかの条件を確認する必要があります。

f.seek()について私が見つけたもう1つの問題は、codecs.open( ''、 'r'、 'utf-8')を使用すると、utf-8をうまく処理できないことです(犯人、結局私はこのアプローチをあきらめました。)


1

シンプルな小さなリーダー。2組で線を引き、オブジェクトを反復処理するときにそれらをタプルとして返します。手動で閉じるか、スコープから外れると自動的に閉じます。

class doublereader:
    def __init__(self,filename):
        self.f = open(filename, 'r')
    def __iter__(self):
        return self
    def next(self):
        return self.f.next(), self.f.next()
    def close(self):
        if not self.f.closed:
            self.f.close()
    def __del__(self):
        self.close()

#example usage one
r = doublereader(r"C:\file.txt")
for a, h in r:
    print "x:%s\ny:%s" % (a,h)
r.close()

#example usage two
for x,y in doublereader(r"C:\file.txt"):
    print "x:%s\ny:%s" % (x,y)
#closes itself as soon as the loop goes out of scope

1
f = open(filename, "r")
for line in f:
    line1 = line
    f.next()

f.close

現在、2行ごとにファイルを読み取ることができます。よろしければ、前にfステータスを確認することもできますf.next()


0

ファイルが適切なサイズである場合、リスト内包表記を使用してファイル全体を2タプルのリストに読み込む別のアプローチは、次のとおりです。

filaname = '/path/to/file/name'

with open(filename, 'r') as f:
    list_of_2tuples = [ (line,f.readline()) for line in f ]

for (line1,line2) in list_of_2tuples: # Work with them in pairs.
    print('%s :: %s', (line1,line2))

-2

このPythonコードは、最初の2行を出力します。

import linecache  
filename = "ooxx.txt"  
print(linecache.getline(filename,2))
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.