-1

I am working on a project and want to print multiple lines in a text file. This is the method I used for this purpose.

def story_part(file_path,initial_index,final_index):
    line_number = list(range((initial_index -1) ,final_index ))
    mylines = []

    with open(file_path) as f:
        for i , line in enumerate(f):
            if i in line_number:

                mylines.append(line.rstrip())
            elif i > final_index:
                break
    for content in mylines:
        print(content)

can you type more efficient code?

I was trying to print in a specified portion from a text file. I have searched through several websites and didn't find something helpful.

and after scratching for some time, I come up with this function. Is this the correct way, or you can help it to improve?

Ajeet Verma
  • 2,938
  • 3
  • 13
  • 24

5 Answers5

3

One optimization you could probably do in your code (wihtout having to change too much) is to make your line_number to be a set so that the index lookup becomes constant.

i in [1 2 3] # O(n) 
i in {1 2 3} # O(1)

The current answers are perfectly fine. But, read(), readlines(), splitlines() loads the entire data into the memory. Which might not be very scalable when the file size is large.

You could probably make use of itertools to read the file as an iterator.

from itertools import islice
def story_part(file_path,initial_index,final_index):

    with open(file_path, "r") as f:
        # islice the file iterator from start index to end index. (You can also pass a step to islice)
        # map your rstrip to each line that is read.
        # list call is not necessary here if you are looping over the data within the scope of `with`. To use the data elsewhere, you will have to realize it with the list call.
        mylines = list(map(str.rstrip, islice(f, initial_index-1, final_index)))
    for content in mylines:
        print(content)

This way file is read only till final_index and only the required lines are ever loaded into the memory. (Note that all lines of the files prior to the initial_index will be read, but ignored.)

  • good answer. note, the OP could just use the `range` object for efficient membership testing (`i in my_range` is O(1)) – juanpa.arrivillaga Apr 30 '23 at 17:49
  • 1
    *"list call is not necessary here if you are looping over the data later on"* - Without the list call, that doesn't work. Did you test that? – Kelly Bundy May 01 '23 at 12:24
  • @KellyBundy -- ok, yeah. `list` call won't be necessary within the scope of `with open`. But, we won't be able to realize the data within once the `f` is closed. Thanks for pointing out. – amd.srinivas May 01 '23 at 14:25
1

I suggest to take look at .readlines method and slicing, this allows you to output range of lines more concise, say if I need lines 5,6,7,8,9,10 of file.txt I can do it following way

with open("file.txt") as f:
    lines = f.readlines()[4:10]
    print(''.join(lines), end='')

.readlines() returns list of lines including trailing newlines, [4:10] is so-called slicing, which allows you to get slice of list. Then we join lines using empty string (as they already have their newlines) and set end to empty string (again, due to newline being already present).

Daweo
  • 31,313
  • 3
  • 12
  • 25
0

You don't need a for loop. You can do the job only by the slicing feature of python lists.

def story_part(file_path, initial_index, final_index):
    with open(file_path) as f:
        file_data = f.read().splitlines()

    mylines = file_data[initial_index-1:final_index]

    for content in mylines:
        print(content)

In the above function, first I read the data and saved it in a variable called file_data which is a list of all lines in the file. Notice that using .read().splitlines() is better than .readlines() as it omits the \n character of each line. Then the file_data[initial_index-1:final_index] code selects only the indexes you want. After that, the for loop prints the data (This line is included only because you had it in your code. It is not necessary.)

This code doesn't have the extra for loop and if statement you used.

Also you can write the proposed code above in a much smaller case like this:

def story_part(file_path, initial_index, final_index):
    with open(file_path) as f:
        for content in f.read().splitlines()[initial_index-1:final_index]:
            print(content)

This is the exact same code but without extra variables and separation of code.

0

It can be a little bit improved in terms of readability but you still have to go through the whole file:

def story_part(file_path, initial_index, final_index):
    mylines = []

    with open(file_path) as f:
        for i, line in enumerate(f, start=1):
            if initial_index <= i <= final_index:
                mylines.append(line.rstrip())
            elif i > final_index:
                break

    for content in mylines:
        print(content)

Be aware of avoiding the use of f.readlines() when reading a heavy file because it stores the entire file in memory.

Taras Drapalyuk
  • 473
  • 3
  • 6
0
def print_lines_file(file_path: str,initial_index: int = 0,final_index:int = 1):
    with open(file_path) as f:
        for i in f.readlines()[initial_index:final_index]:
            print(i)
kavardak
  • 26
  • 3