pythonfileoop

Finding how many lines are in a txt file using a custom class in python


I am trying to create a class in python called EasyFile that would allow me to read and write to a file from any location I have called the cursor in the program. One test I made involves moving the cursor down a line from a txt file in a loop until I get an EOFError. I have removed unnecessary methods and exception handling to create a minimal reproducible example:

def main():
    file = EasyFile("numbers.txt")
    file.write("1, -2, 5, 0, 19, -7, end\n5, 5, -1, -10, 9, end")
    lineCount = 0
    while(True):
        try:
            print("Line #"+str(lineCount+1))
            file.moveCursorToLine(lineCount)
        except EOFError:
            break
        lineCount += 1
    print("The amount of lines are: "+str(lineCount + 1))    

class EasyFile():

    #Members:
        #filename
        #writer
        #reader
        #cursor

    def __init__(this,filename):
        this.filename = filename
        this.cursor = 0
        this.writer = open(filename,"w") #open one instance for writing
        this.reader = open(filename,"r") #open another instance for reading
        this.writer.seek(this.cursor)
        this.reader.seek(this.cursor) #set the cursor to the beginning

    def moveCursor(this, charNum): #charNum = 0 will reset file cursor. charNum is kinda like the incidices of an array where they start at 0
        this.cursor = charNum
        this.writer.seek(charNum)
        this.reader.seek(charNum)
        temp = open(this.filename,"r") #temp is created to check EOF without using this.reader so reader.tell() is in sync with this.cursor
        temp.seek(charNum)
        if(temp.read(1)==""): #if there is nothing after the file...
            temp.seek(charNum-1) #check the previous character
            if(temp.read(1)==""): #if there is still nothing...
                temp.close() 
                raise EOFError #we were at the end of the file
        temp.close()

    def moveCursorToLine(this, lineNum): #lineNum = 0 will reset file cursor. lineNum is kinda like the indices of an array where it starts at 0
        this.moveCursor(0)
        for i in range(0,lineNum):
           print(repr(this.readUntil()))

    def write(this,string):
        this.writer.write(string)
        this.writer.flush() #make sure the file is updated for future use
        this.moveCursor(this.cursor+len(string)) #move the cursor to the end of what was just written

    def readUntil(this,buffer="\n"):
        string = this.reader.read() #gather a string of everything past the cursor
        this.moveCursor(this.cursor) #re-sync this.reader.tell() and this.cursor
        endChar = 0
        while(len(string)>endChar):
            if(string[endChar:endChar+len(buffer)]==buffer): #check if a selected portion of the read file is equal to the buffer
                break
            if(len(string[endChar:endChar+len(buffer)])!=len(buffer)): #check if the previously selected portion is the same length as the buffer. If it's not that means we're at the end of the file
                this.moveCursor(this.cursor+len(string)) #move the cursor to the end of the file
                return string #return the enitre read content if there was no buffer present in the read content
            endChar += 1 #add one character to the future return value
        this.moveCursor(this.cursor+endChar+len(buffer)) #The cursor is currently on the buffer, so +len(buffer) would move it past the line
        return string[0:endChar] #take a substring
    
main()

The output I hoped to get from this test would be:

Line #1
'1, -2, 5, 0, 19, -7, end'
Line #2
'5, 5, -1, -10, 9, end'
The amount of lines are: 2

However, I am instead getting the output:

Line #1
Line #2
'1, -2, 5, 0, 19, -7, end'
Line #3
'1, -2, 5, 0, 19, -7, end'
''
Line #4
'1, -2, 5, 0, 19, -7, end'
''
The amount of lines are: 4

I am relatively new to Python coding and am a little stuck after working on this for the past two days. I think the problem is in def readUntil(this, buffer="\n") as I think the logic in moveCursor and moveCursorToLine are sound. I hope to avoid import os so I can just apply the vanilla tools for filehandling. Thank you in advance


Solution

  • moveCursorToLine always calls moveCursor(0), so every time you ask for a new line you jump back to the start of the file and keep rereading the same data.
    First of all add newlene argument to the writer and reader:

    this.writer = open(filename,"w", newline="\n")
    this.reader = open(filename,"r", newline="\n")
    

    Skip that seek-to-zero and read the requested line relative to the current cursor instead:

    def moveCursorToLine(self, line_num):     
        # advance past the previous `line_num` new-lines     
        for _ in range(line_num):        
            self.readUntil('\n')  # discard     
            return self.readUntil('\n')   # return the target line 
    

    And change the readUntil logic a little bit:

        def readUntil(this,buffer="\n"):
            string = this.reader.read() #gather a string of everything past the cursor
            if string == "":
                raise EOFError
            this.moveCursor(this.cursor) #re-sync this.reader.tell() and this.cursor
            endChar = 0
            while(len(string)>endChar):
                if(string[endChar:endChar+len(buffer)]==buffer): #check if a selected portion of the read file is equal to the buffer
                    this.moveCursor(this.cursor+endChar+len(buffer))
                    return string[0:endChar]
                endChar += 1 #add one character to the future return value
            this.moveCursor(this.cursor+len(string))
            return string[0:endChar] #take a substring
    

    Then drive the loop like this:

    line_count = 0
    while True:
        try:
            print(f"Line #{line_count + 1}")
            print(repr(file.moveCursorToLine(0)))   # 0 → “give me the next line”
        except EOFError:
            break
        line_count += 1
    
    print("The amount of lines are:", line_count)
    

    Output:

    Line #1
    '1, -2, 5, 0, 19, -7, end'
    Line #2
    '5, 5, -1, -10, 9, end'
    The amount of lines are: 2
    

    (You can make the whole class much simpler by opening the file once in 'r+' mode and using readline(), but the fix above shows why your current code repeats the first line and doubles the count.)