Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use readline() from python to read specific line

Tags:

python

When using readline() in python is it possible to specify what line to read? When I run the following code, I get lines 1,2,3 but I would like to read lines 2,6,10

def print_a_line(line, f):
    print f.readline(line)

current_file = open("file.txt")

for i in range(1, 12):
    if(i%4==2):
        print_a_line(i, current_file)
like image 446
The Nightman Avatar asked Jan 08 '23 05:01

The Nightman


2 Answers

No, you can't use readline that way. Instead, skip over the lines you don't want. You have to read through the file because you can't know ahead of time where to seek to to read a specific line (unless the newlines appear in some regular offset). You can use enumerate to determine what line you're on, so you only have to read the file once and can stop after the location you don't care about.

with open('my_file') as f:
    for i, line in enumerate(f, start=1):
        if i > 12:
            break
        if i % 4 == 0:
            print(i, line)

If you know that each line is a certain byte length, you can seek to the specific position for a given line, rather than iterating over the lines.

line_len = 20  # bytes

with open('my_file', 'rb') as f:
    for i in range(0, 13, 4):
        f.seek(i * line_len)
        print(f.read(line_len).decode())
like image 181
davidism Avatar answered Jan 16 '23 20:01

davidism


You can use the consume recipe from itertools, which is one of the fastest ways to skip lines:

from itertools import islice
from collections import deque

def consume(iterator, n):
    "Advance the iterator n-steps ahead. If n is none, consume entirely."
    # Use functions that consume iterators at C speed.
    if n is None:
        # feed the entire iterator into a zero-length deque
        deque(iterator, maxlen=0)
    else:
        # advance to the empty slice starting at position n
        next(islice(iterator, n, n), None)

with open("in.txt") as f:
    l = []
    sm = 0
    for i in (2, 6, 10):
        i -= sm
        consume(f, i-1)
        l.append(next(f, ""))
        sm += i

We just need to subtract what we have already consumed so we keep the lines matching each i. You can put the code in a function and yield each line:

def get_lines(fle,*args):
    with open(fle) as f:
        l, consumed = [], 0
        for i in args:
            i -= consumed
            consume(f, i-1)
            yield next(f, "")
            consumed += i

To use just pass the filename and the line numbers:

test.txt:

1
2
3
4
5
6
7
8
9
10
11
12

Output:

In [4]: list(get_lines("test.txt",2, 6, 10))
Out[4]: ['2\n', '6\n', '10\n']
In [5]: list(get_lines("stderr.txt",3, 5, 12))
Out[5]: ['3\n', '5\n', '12']

If you only wanted a single line you could also use linecache:

import linecache

linecache.getline("test.txt",10)
like image 41
Padraic Cunningham Avatar answered Jan 16 '23 20:01

Padraic Cunningham