How to skip to a line via character seeking in Python
<p>If I have a text file that has a bunch of random text before I get to the stuff I actually want, how do I move the file pointer there? </p>

<p>Say for example my text file looks like this:</p>

<pre><code>#foeijfoijeoijoijfoiej ijfoiejoi jfeoijfoifj  i jfoei joi jo ijf eoij oie jojf
#feoijfoiejf   ioj oij       oi jo ij   i joi jo ij oij  ####  oijroijf 3## # o
#foeijfoiej i jo i  iojf 3 ##  #io joi joij oi j## io joi joi j3# 3i ojoi joij
# The stuff I care about
</code></pre>

<p>(The hashtags are a part of the actual text file)</p>

<p>How do I move the file pointer to the line of stuff I care about, and then how would I get python to tell me the number of the line, and start the reading of the file there?</p>

<p>I've tried doing a loop to find the line that the last hashtag is in, and then reading from there, but I still need to get rid of the hashtag, and need the line number.</p>

<p>You can't seek to it directly without knowing the size of the junk data or scanning through the junk data. But it's not too hard to wrap the file in <a href="https://docs.python.org/3/library/itertools.html#itertools.dropwhile" rel="nofollow"><code>itertools.dropwhile</code></a> to discard lines until you see the "good" data, after which it iterates through all remaining lines:</p>

<pre><code>import itertools

# Or def a regular function that returns True until you see the line
# delimiting the beginning of the "good" data
not_good = '# The stuff I care about\n'.__ne__

with open(filename) as f:
    for line in itertools.dropwhile(not_good, f):
        ... You'll iterate the lines at and after the good line ...
</code></pre>

<p>If you actually need the file descriptor positioned appropriately, not just the lines, this variant should work:</p>

<pre><code>import io

with open(filename) as f:
    # Get first good line
    good_start = next(itertools.dropwhile(not_good, f))

    # Seek back to undo the read of the first good line:
    f.seek(-len(good_start), io.SEEK_CUR)

    # f is now positioned at the beginning of the line that begins the good data
</code></pre>

<p>You can tweak this to get the actual line number if you really need it (rather than just needing the offset). It's a little less readable though, so explicit iteration via <code>enumerate</code> may make more sense if you need to do it (left as exercise). The way to make Python work for you is:</p>

<pre><code>from future_builtins import map  # Py2 only
from operator import itemgetter

with open(filename) as f:
    linectr = itertools.count()
    # Get first good line
    # Pair each line with a 0-up number to advance the count generator, but
    # strip it immediately so not_good only processes lines, not line nums 
    good_start = next(itertools.dropwhile(not_good, map(itemgetter(0), zip(f, linectr))))

    good_lineno = next(linectr) # Keeps the 1-up line number by advancing once

    # Seek back to undo the read of the first good line:
    f.seek(-len(good_start), io.SEEK_CUR)

    # f is now positioned at the beginning of the line that begins the good data
</code></pre>

python loops text