The Nightman
The Nightman

Reputation: 5759

Use readline() from python to read specific line

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)

Upvotes: 4

Views: 5089

Answers (4)

hajtos
hajtos

Reputation: 473

Reading a file is always done from the first character. The reader has no knowledge of the content, so it doesn't know where lines begin and end. readline just reads until it observes a newline character. This is actually true for any language, not only Python. If you want to get the nth line, you can skip n-1 lines:

def my_readline(file_path, n):
    with open(file_path, "r") as file_handle:
        for _ in range(1, n):
            file_handle.readline()
        return file_handle.readline()

Do note that with this solution you need to open the file with each function call, which can seriously lower your program's performance.

Upvotes: 1

Padraic Cunningham
Padraic Cunningham

Reputation: 180522

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)

Upvotes: 3

TigerhawkT3
TigerhawkT3

Reputation: 49330

with open('file.txt', 'r') as f:
    next(f)
    for line in f:
        print(line.rstrip('\n'))
        for skip in range(3):
            try:
                next(f)
            except StopIteration:
                break

File:

1
2
3
4
5
6
7
8
9
10

Result:

2
6
10

This will work for a script or function, but if you want it to hide the skipped lines in the interactive shell you'll have to save the next(f) calls to a temporary variable.

Upvotes: 2

davidism
davidism

Reputation: 127390

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

Upvotes: 3

Related Questions