1

I am relatively new to Python, but after reading a lot of posts about using readline() and readlines(), I can't seem to figure this one out.

quotes = open("quotes.txt", "r")
#Opens the quotes file
quote = quotes.readlines()
#Uses the readlines() method to read each line of text file
lineCount = 0

for line in quote:
    lineCount += 1
    print("{}".format(line.strip()))

So, here I am using the readlines() method, but the problem is, this code prints every line at once.

So then I tried readline() in the code, but then the coded only prints one line, I won't show the code for this because I had no luck figuring it out.

I'm looking to print a line, increment a counter and break.

Then on the next run of code, it prints the next line and breaks.

So, in essence:

When I run my program the first time it would print:

"Quote 1" - Author

Then, on the next run it would be:

"Quote 2" - Author

Anyone who can help figure this out for me, it would be greatly appreciated.

Thanks!

Additional information:

I have compiled a list of quotes from an old podcast which is written line by line in the quotes.txt file, this is for a Twitter bot that I am currently developing using the Tweepy module. For now, I have collected a large number of quotes so I am not currently worried about the program looping back around and starting again until I get closer to that time.

Thanks all for the help already.

Scott Wilks
  • 37
  • 1
  • 5
  • "I'm looking to print a line, increment a counter and break." By "break", do you mean quit the program? Because that is not what `break` actually means. If you mean to break out of the loop, then why write a loop if you only ever want it to run exactly once? – Karl Knechtel Apr 16 '21 at 10:40
  • "Then on the next run of code" Okay, so you seriously want to run the program multiple times, as in type in `python myfile.py` at the command line multiple times? Well, then. Each time the program runs, what's your plan for the program to *figure out how many times it ran before*? That information needs to be kept track of somewhere, right? Like... a file, maybe? – Karl Knechtel Apr 16 '21 at 10:41
  • "So then I tried readline(), which only prints one line" No, it doesn't. Neither `readline` nor `readlines` nor anything else you do with the open file object prints *anything at all*. These methods *read* data from the file, which is why they have the names that they do. The *printing* happens when you call `print`. I know this sounds pedantic, but *you cannot expect to write correct programs if you are not careful in your thinking*. – Karl Knechtel Apr 16 '21 at 10:42
  • Read up on the [methods](https://docs.python.org/3/tutorial/inputoutput.html#methods-of-file-objects) `.tell()` and `.seek(...)`. –  Apr 16 '21 at 10:49
  • @KarlKnechtel Yes I do want to run the program multiple times, the code will be ran as a scheduled task on a website on the hour every hour, so I won't need to type anything. When I said it prints, I was referring to what my code currently does, which is undesirable, but I understand I could have worded that better, but maybe try being less pendantic as I believe most know what I would mean. As for the for loop, I thought with each loop it would increment the counter once which is what I desire. – Scott Wilks Apr 16 '21 at 11:05
  • Do you need to preserve the contents of the `"quotes.txt"`? Could you reorder them? What should happen when you get to the end? If the idea is to cycle through them repeatedly, and you don't care about the file but only the cycling behaviour, then one thing you could do is rewrite the file each time to move the first line to the end. – Karl Knechtel Apr 16 '21 at 11:10
  • @KarlKnechtel, I have compiled a list of quotes from an old podcast which are written line by line in the quotes.txt file, this is for a Twitter bot that I am currently developing using the Tweepy module. For now, I have collected a large amount of quotes so I was going to cross the bridge regarding 'what should happen when you get to the end' when I got closer to that time. But you're right, I would like it to loop back once it reached the end of the quotes.txt file. – Scott Wilks Apr 16 '21 at 11:13

2 Answers2

2

In order to know which line to read on the next script execution, we need to save this information permanently. To do so, you could write the line counter to a text file and read its contents upon startup. However, there might be a problem on the very first script execution: If the script was never run before, there is no file saved. Reading from a non-exisitng file would raise an error. Therefore we try to read from SAVE_FILE. If the file is available, we use the saved number. If it is not available we initilize the line number by defining line_number_to_show = 0. We then open the file containing the quotes, iterate over the line numbers and look for the line number we are interested in. If the specified line is found, we print it. The next step is to save the line number for the next program execution.

A very basic approach would look like this:

SAVE_FILE = 'line_number.txt'

try:
    with open(SAVE_FILE) as save_file:
        line_number_to_show = save_file.read().strip()
    line_number_to_show = int(line_number_to_show)
except (FileNotFoundError, ValueError) as _:
    line_number_to_show = 0

with open("quotes.txt") as quotes_file:
    for line_number, line in enumerate(quotes_file):
        if line_number == line_number_to_show:
            print(line)

with open(SAVE_FILE, 'w') as save_file:
    line_number_to_show = line_number_to_show + 1
    save_file.write(f'{line_number_to_show}')

Important notes:

  • Reading the line number from the SAVE_FILE the way I did is error prone as we do not perform any sanity checks or catch possible errors in try/except blocks. Production code would require further countermeasures to deal with these issues.
  • Instead of reading the the file the way I did, you could use .readlines() to read the complete file contents into a list. As the list is stored in memory and the complete file is read, this might lead to high memory consumption when dealing with huge files. Doing it via enumerate() reads the file chunk-wise and is considered to be more memory efficient.
  • For scaling applications and depending on your infrastructure/architecture (as you said something about a Bot in one of your comments) you might consider using a database to store and read the quotes from. If doing so, you would also store the required information about which quote to deliver upon the next request. However, approaching this would be part of several other design decisions and (SO) questions.
albert
  • 8,027
  • 10
  • 48
  • 84
  • So this code falls over on the second run due to line 15, "TypeError: can only concatenate str (not "int") to str". Any ideas? – Scott Wilks Apr 16 '21 at 11:44
  • @ScottWilks: See my updated code snippet. Need to cast the value read from the file to an `int` and catch `ValueError` if something went wrong. – albert Apr 16 '21 at 12:02
  • Works perfectly, thank you, I am going to upload the files to my web application now and give it a test. Thank you again. – Scott Wilks Apr 16 '21 at 12:17
1

Just use:

f = open("quotes.txt","r")
lineCount = 0
for i in range(4):
    lineCount+=1
    print(f.readline())
lakedue
  • 44
  • 1
  • 5