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
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.)