Dnaiel
Dnaiel

Reputation: 7832

Read a file line by line, sometimes reading the next line within same loop

I'd like to read a file in python line by line, but in some cases (based on an if condition) I'd also like to read the next line in the file, and then keep reading it the same way.

Example:

    file_handler = open(fname, 'r')
    for line in file_handler:
       if line[0] == '#':
           print line
       else:
           line2 = file_handler.readline()
           print line2

basically in this example I am trying to read it line by line, but when the line does not start with # I'd like to read the next line, print it, and then keep reading the line after line2. This is just an example where I got the error for similar stuff I am doing in my code but my goal is as stated in the title.

But I'd get an error like ValueError: Mixing iteration and read methods would lose data.

Would it be possible to do what I am trying to do in a smarter way?

Upvotes: 2

Views: 3068

Answers (6)

Coyo
Coyo

Reputation: 766

I think what you are looking for is next rather than readline. A few things. In your code, you use = rather than ==. I will use startswith instead. If you call next on an iterator, it will return the next item or throw a StopIteration exception.

The file

ewolf@~ $cat foo.txt
# zork zap
# woo hoo
here is 
some line
# a line
with no haiku

The program

file_handler = open( 'foo.txt', 'r' )
for line in file_handler:
    line = line.strip()
    if line.startswith( '#' ):
        print "Not Skipped : " + line
    elif line is not None:
    try:
        l2 = file_handler.next()
            l2 = l2.strip()
            print "Skipping. Next line is  : " + l2
        except StopIteration:
            # End of File                                                       
            pass

The output

Not Skipped : # zork zap
Not Skipped : # woo hoo
Skipping. Next line is  : some line
Not Skipped : # a line
Skipping. Next line is  : 

Upvotes: 0

tdelaney
tdelaney

Reputation: 77337

You can save a bit of state information that tells you what to do with the next line:

want_next = False
for line in open(fname):
    if want_next:
        print line
        want_next = False
    elif line[0] == '#':
        print line
        want_next = True

Upvotes: 0

abarnert
abarnert

Reputation: 365617

If you just want to skip over lines not starting with #, there's a much easier way to do this:

file_handler = open(fname, 'r')
    for line in file_handler:
       if line[0] != '#':
           continue
       # now do the regular logic
       print line

Obviously this kind of simplistic logic won't work in all possible cases. When it doesn't, you have to do exactly what the error implies: either use iteration consistently, or use read methods consistently. This is going to be more tedious and error-prone, but it's not that bad.

For example, with readline:

while True:
    line = file_handler.readline()
    if not line:
        break
    if line[0] == '#':
        print line
    else:
        line2 = file_handler.readline()
        print line2

Or, with iteration:

lines = file_handler
for line in file_handler:
    if line[0] == '#':
        print line
    else:
        print line
        print next(file_handler)

However, that last version is sort of "cheating". You're relying on the fact that the iterator in the for loop is the same thing as the iterable it was created from. This happens to be true for files, but not for, say, lists. So really, you should do the same kind of while True loop here, unless you want to add an explicit iter call (or at least a comment explaining why you don't need one).

And a better solution might be to write a generator function that transforms one iterator into another based on your rule, and then print out each value iterated by that generator:

def doublifier(iterable):
    it = iter(iterable)
    while True:
        line = next(it)
        if line.startswith('#'):
            yield line, next(it)
        else:
            yield (line,)

Upvotes: 6

Hai Vu
Hai Vu

Reputation: 40688

file_handler = open(fname, 'r')
for line in file_handler:
   if line.startswith('#'): # <<< comment 1
       print line
   else:
       line2 = next(file_handler) # <<< comment 2
       print line2

Discussion

  1. Your code used a single equal sign, which is incorrect. It should be double equal sign for comparison. I recommend to use the .startswith() function to enhance code clarity.

  2. Use the next() function to advance to the next line since you are using file_handler as an iterator.

Upvotes: 1

marmeladze
marmeladze

Reputation: 25

try if line[0] == "#" instead of line[0] = "#"

Upvotes: -2

Ant
Ant

Reputation: 5404

add a flag value:

if flag is True:
    print line #or whatever
    flag = False
if line[0] == '#':
    flag = True

This is versatile version :-)

Upvotes: 0

Related Questions