파이썬을 사용하여 파일에서 한 번에 두 줄을 읽는 방법


82

텍스트 파일을 구문 분석하는 파이썬 스크립트를 코딩하고 있습니다. 이 텍스트 파일의 형식은 파일의 각 요소가 두 줄을 사용하도록되어 있으며 편의를 위해 구문 분석 전에 두 줄을 모두 읽고 싶습니다. 파이썬으로 할 수 있습니까?

나는 다음과 같은 것을 원합니다.

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

f.close

그러나 이것은 다음과 같이 말합니다.

ValueError : 반복 및 읽기 메서드를 혼합하면 데이터가 손실됩니다.

관련 :


8
f.readline ()을 f.next ()로 변경하면 모든 설정이 완료됩니다.
Paul

추가 답변 은 stackoverflow.com/questions/1528711/reading-lines-2-at-a-time 을 참조하십시오 .
foosion

@Paul이 f.next ()가 여전히 유효합니까? 나는이 오류 AttributeError 무엇입니까 '_io.TextIOWrapper'객체 '다음'에는 속성이 없습니다
SKR

1
Python 3에서는 @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.

Python2에서는 izip_longest대신 사용해야 합니다.


주석에서이 솔루션이 먼저 전체 파일을 읽은 다음 파일을 두 번 반복하는지 묻는 질문이 있습니다. 나는 그렇지 않다고 믿습니다. 이 with open('a') as f줄은 파일 핸들을 열지 만 파일을 읽지는 않습니다. f반복기이므로 요청 될 때까지 내용을 읽지 않습니다. zip_longest반복자를 인수로 취하고 반복자를 반환합니다.

zip_longest실제로 동일한 반복자 f가 두 번 공급됩니다. 그러나 결국 일어나는 것은 next(f)첫 번째 인수에서 호출 된 다음 두 번째 인수에서 호출 된다는 것 입니다. next()동일한 기본 반복기에서 호출 되기 때문에 연속 행이 생성됩니다. 이것은 전체 파일을 읽는 것과는 매우 다릅니다. 실제로 반복자를 사용하는 목적은 정확히 전체 파일을 읽는 것을 피하는 것입니다.

따라서 솔루션이 원하는대로 작동한다고 믿습니다. for 루프에서 파일을 한 번만 읽습니다.

이를 입증하기 위해 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)입력하기가 더 쉬울 것입니다.
Steve Losh 2009

lines대신 사용 하는 경우 line1, line2한 번에 행 2을 읽으려면 하나의 숫자 ( ) 만 변경 n하면됩니다.
jfs

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-04-08

2
next ()는 이제 예외를 피하기 위해 기본 인수를 지원합니다.nextline = next(f,None)
gerardw

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다른 일이 발생하면 파일이 닫히고 리소스가 소진되면 리소스가 닫히고 루프가 종료됩니다.

기능이 활성화 된 with상태에서 2.6 또는 2.5 가 필요합니다 with_statement.


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 개 줄은 폐기됩니다.
Boris

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 2009

실제로 readlines()호출은 모든 것을 메모리로 끌어 올 것입니다.
Steve Losh 2009

나는 당신의 수업을 좋아하지 않습니다. 파일을 초기화하는 동안 전체 파일에 대해 두 번 반복합니다. 짧은 줄이있는 큰 파일의 경우 저장된 메모리가 많지 않습니다.
Georg Schölly

@Steve : 네, 슬프게도 충분합니다. 그러나 zip은 전체 튜플 목록을 생성하여 메모리에 추가 레이어를 추가합니다 (Python 3이 아닌 경우). 여기서 izip은 한 번에 하나씩 튜플을 생성합니다. 나는 그것이 당신이 의미 한 것이라고 생각하지만 어쨌든 내 이전 의견을 명확히하고 싶습니다 :-)
RedGlyph

3
file_name = 'your_file_name'
file_open = open (파일 _ 이름, 'r')

def 핸들러 (line_one, line_two) :
    인쇄 (1 행, 2 행)

file_open 동안 :
    시험:
        하나 = file_open.next ()
        two = file_open.next () 
        핸들러 (하나, 둘)
    except (StopIteration) :
        file_open.close ()
        단절

1
while file_open:while True:이 경우 와 동일하기 때문에 오해의 소지가 있습니다.
jfs

의도적으로, 루프에서 벗어나려면 휴식이 필요함을 나타내는 'while True'를 수행하는 것이 더 깨끗하다는 데 동의합니다. 나는 파일이 얼마나 오래 열려 있어야하고 그 동안 무엇을해야하는지 의심 할 여지없이 이런 방식으로 더 잘 읽혀 진다고 믿기 때문에 (다시 논쟁 할 수있는) 그렇게하지 않기로 결정했습니다. 대부분의 경우 나는 나 자신을 위해 'while 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- 튜플로 반환하는 생성기를 만드는 것입니다. 즉, 결과를 반복 할 수 있습니다.

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 ()에 대해 알게 된 또 다른 문제는 codecs.open ( '', 'r', 'utf-8')을 사용할 때 utf-8을 잘 처리하지 못한다는 것입니다. 범인, 결국 나는이 접근 방식을 포기했습니다.)


1

간단한 작은 독자. 두 쌍의 라인을 끌어 와서 객체를 반복 할 때 튜플으로 반환합니다. 수동으로 닫거나 범위를 벗어나면 자동으로 닫힙니다.

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

지금은 두 줄마다 파일을 읽을 수 있습니다. 당신이 좋아한다면 당신은 또한 전에 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 코드는 처음 두 줄을 인쇄합니다.

import linecache  
filename = "ooxx.txt"  
print(linecache.getline(filename,2))
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.