2

So I have a problem. I am working with .txt files which are comprised of multiple of 4 lines. I am working in python 3.

I wrote a code that is meant to take every 2nd and 4th line of a text file and keep only the first 20 characters of those two lines (while leaving the 1st and 3rd line unedited), and create a new edited file comprising of the the edited 2nd and 4th line and the unedited 1st and 3rd line. This trend would be the same for every line since all the text files I work with have line numbers that are always multiple of 4.

This works on small files (~100 lines total) but the files I need edition are 50 million+ lines and it is taking 4+ hours.

Below is my code. Can anyone give me a suggestion on how to speed up my program? Thanks!

import io
import os
import sys

newData = ""
i=0
run=0
j=0
k=1
m=2
n=3
seqFile = open('temp100.txt', 'r')
seqData = seqFile.readlines()
while i < 14371315:
    sLine1 = seqData[j] 
    editLine2 = seqData[k]
    sLine3 = seqData[m]
    editLine4 = seqData[n]
    tempLine1 = editLine2[0:20]
    tempLine2 = editLine4[0:20]
    newLine1 = editLine2.replace(editLine2, tempLine1)
    newLine2 = editLine4.replace(editLine4, tempLine2)
    newData = newData + sLine1 + newLine1 + '\n' + sLine3 + newLine2
    if len(seqData[k]) > 20:
         newData += '\n'
    i=i+1
    run=run+1
    j=j+4
    k=k+4
    m=m+4
    n=n+4
    print(run)

seqFile.close()

new = open("new_100temp.txt", "w")
sys.stdout = new
print(newData)
Daniel
  • 36,610
  • 3
  • 36
  • 69
Tom Anonymous
  • 173
  • 1
  • 3
  • 11

4 Answers4

2

You are working with the two files (input and output) in memory. It can cause time problems if files are big (pagination). Try (pseudocode)

Open input file for read
Open output file for write
Initialize counter to 1
While not EOF in input file
    Read input line
    If counter is odd 
        Write line to output file
    Else
        Write 20 first characters of line to output file
    Increment counter
Close files
MC ND
  • 69,615
  • 8
  • 84
  • 126
2

The biggest issue here seems to be reading the whole file at once:

seqData = seqFile.readlines()

Instead you should open you source file and output file at first. Then iterate over the first file and manipulate the lines as you wish:

outfile = open('output.txt', 'w')
infile = open('input.txt', 'r')

i = 0
for line in infile:
    if i % 2 == 0:
       newline = line
    else:
       newline = line[:20]

    outfile.write( newline )
    i += 1

outfile.close()
infile.close()
pajton
  • 15,828
  • 8
  • 54
  • 65
2

It is probably much faster if you just read 4 lines at a time and process those (untested):

with open('100temp.txt') as in_file, open('new_100temp.txt', 'w') as out_file:
    for line1, line2, line3, line4 in grouper(in_file, 4):
         # modify 4 lines
         out_file.writelines([line1, line2, line3, line4])

where grouper(it, n) is a function that yields n items of an iterabel it at a time. It is given as one of the examples of the itertools module (see also this anwer at SO). Iterating over a file in this way is similar to calling readlines() on a file and then manually iterating over the resulting list, but it only reads a few lines into memory at a time.

Community
  • 1
  • 1
Bas Swinckels
  • 18,095
  • 3
  • 45
  • 62
1

See the docs for the best way to read a file. Instead of keeping it all in memory, which is what you're doing with seqData = seqFile.readlines(), just iterate through. Python takes care of buffering et al. for you, so it is fast and efficient. Also, you shouldn't open and close files yourself (like the other answers)-- use the with keyword.

lineCount = 0
with open("new_100temp.txt", "w") as newFile, open("100temp.txt","r") as oldFile:
    for line in oldFile:
        #start on line 1, keep 1st and 3rd as is, modify 2nd and 4th
        lineCount += 1
        if lineCount%4 == 1 or lineCount%4 == 3: 
            newFile.write(line)
        else:
            newFile.write(line[:20] + "\n")
            # printing is really slow, so only do it every 100th iteration:
        if lineCount % 100 == 0:
            print lineCount

I just tried it on one million lines of garbage text and it finished it in less than a second. As Kevin said though, simple text jobs like this are good for the shell to handle.

kevinsa5
  • 3,301
  • 2
  • 25
  • 28