Can read() and readline() be used together? - python

Is it possible to use both read() and readline() on one text file in python?
When I did that, it will only do the first reading function.
file = open(name, "r")
inside = file.readline()
inside2 = file.read()
print(name)
print(inside)
print(inside2)
The result shows only the inside variable, not inside2.

Reading a file is like reading a book. When you say .read(), it reads through the book until the end. If you say .read() again, well you forgot one step. You can't read it again unless you flip back the pages until you're at the beginning. If you say .readline(), we can call that a page. It tells you the contents of the page and then turns the page. Now, saying .read() starts there and reads to the end. That first page isn't included. If you want to start at the beginning, you need to turn back the page. The way to do that is with the .seek() method. It is given a single argument: a character position to seek to:
with open(name, 'r') as file:
inside = file.readline()
file.seek(0)
inside2 = file.read()
There is also another way to read information from the file. It is used under the hood when you use a for loop:
with open(name) as file:
for line in file:
...
That way is next(file), which gives you the next line. This way is a little special, though. If file.readline() or file.read() comes after next(file), you will get an error that mixing iteration and read methods would lose data. (Credits to Sven Marnach for pointing this out.)

Yes you can.
file.readline() reads a line from the file (the first line in this case), and then file.read() reads the rest of the file starting from the seek position, in this case, where file.readline() left off.
You are receiving an empty string with f.read() probably because you reached EOF - End of File immediately after reading the first line with file.readline() implying your file only contains one line.
You can however return to the start of the file by moving the seek position to the start with f.seek(0).

Related

python - Trying to do a program to replace a given line, by the same line but all CAPS

Trying to do a college exercise where I'm supposed to replace a given line in a file, by the same line but written in all caps. The problem is we can only write in the same file, and in that exact line, we can't write in the rest of the file.
This is the code I have so far, but I can't figure out how to go to the line I want
def upper(n):
count=0
with open("upper.txt", "r+") as file:
lines = file.readlines()
file.seek(0)
for line in file.readlines():
if count == n:
pos = file.tell()
line1 = str(line.upper())
count += 1
file.seek(pos)
file.write(line1)
Help appreciated!
The problem lies in that your readlines already has read the entire file, and so the position of the "file cursor" is always at the end of the file. In theory, a simple fix should be:
Initialize pos to 0.
Read a single line.
If the current line counter indicates this is the one you want, set the position to pos again, update that line, and exit.
Update pos to point to the end of this line (so it points to the start of the next line).
Loop until satisfied.
In code, that would be this:
def upper(n):
count=0
with open("text.txt", "r+") as file:
pos = 0
for line in file.readlines():
if count == n:
line1 = line.upper()
break
pos = file.tell()
count += 1
file.seek(pos)
file.write(line1)
upper(5)
However! There is a snag. File operations are heavily buffered, and the for loop on readlines does not read one line at a time. Instead, for efficiency, it reads as much as possible, but it only "returns" the next line to your program. On a next run through your loop, it simply checks if it already had read enough of your text file to return the following line, and if not, it fills its internal buffer again. So, even while tell() will correctly be updated to the external file position – the value you see –, it does not reflect the "cursor" position of what you are processing at the time.
One way to circumvent this is to physically mimic what readlines does: read a single byte at a time, determine whether you have read an entire line (then this byte would be \n), and update your position and status based on this.
However, a more proper way of updating a file is to read it into memory in its entirety, change it, and write it back to disk. Changing part of an existing file with "r+" is usually recommended to use binary mode (where the position of each byte is known beforehand); admittedly, in theory your method should have worked as well, but as you see the file buffering defeats this.
Reading, changing, and writing the file entirely is as simple as this:
def better_upper(n):
count=0
with open("text.txt", "r") as file:
lines = file.readlines()
lines[n] = lines[n].upper()
with open("text.txt", "w") as file:
file.writelines(lines)
better_upper(5)
(Where the only caveat is that it always overwrites the original file. That is: if something unexpected goes wrong, it will probably erase text.txt. If you want a belt-and-suspenders approach, write to a new file, then check if it got written correctly. If it did, delete the old file and rename the new one. Left as an exercise to the reader.)

parse a file, appending each line at the end and removing the line from the top

I am trying to move each line down at the bottom of the file; this is how the file look like:
daodaos 12391039
idiejda 94093420
jfijdsf 10903213
....
#completed
So at the end of the parsing, I am planning to get all the entry that are on the top, under the actual string that says # completed.
The problem is that I am not sure how can I do this in one pass; I know that I can read the whole file, every single line, close the file and then re-open the file in write mode; searching for that line, removing it from the file and adding it to the end; but it feels incredibly inefficient.
Is there a way in one pass, to process the current line; then in the same for loop, delete the line and append it at the end of the file?
file = open('myfile.txt', 'a')
for items in file:
#process items line
#append items line to the end of the file
#remove items line from the file
suggest to keep it simple read and writeback
with open('myfile.txt') as f:
lines = f.readlines()
with open('myfile.txt', 'w') as f:
newlines = []
for line in lines:
# do you stuff, check if completed, rearrange the list
if line.startswith('#completed'):
idx=i
newlines = lines[idx:] + lines[:idx]
break
f.write(''.join(newlines)) # write back new lines
below is another version i could think of if insist wanna modify while reading
with open('myfile.txt', 'r+') as f:
newlines = ''
line = True
while line:
line = f.readline()
if line.startswith('#completed'):
# line += f.read() # uncomment this line if you interest on line after #completed
f.truncate()
f.seek(0)
f.write(line + newlines)
break
else:
newlines += line
Not really.
Your main problem here is that you're iterating on the file at the same time you want to change it. This will Do Bad Things (tm) to your processing, unless you plan to micro-manage the file position pointer.
You do have that power: the seek method lets you move to a given file location, expressed in bytes. seek(0) moves to the start of the file; seek(-1) to the end. The problem you face is that your for loop trusts that this pointer indicates the next line to read.
One distinct problem is that you can't just remove a line from the middle of the file; something exists in those bytes. Think of it as lines of text on a page, written in pencil. You can erase line 4, but this does not cause lines 5-end to magically float up half a centimeter; they're still in the same physical location.
How to Do It ... sort of
Read all of the lines into a list. You can easily change a list the way you want. When you hit the end, then write the list back to the file -- or use your magic seek and append powers to alter only a little of it.
I'll recommend you to do this the simple way: read all the file and store it in a variable, move the completed files to another variable and then rewrite your file.

Can you read first line from file with open(fname, 'a+')?

I want to be able to open a file, append some text to the end, and then read only the first line. I know exactly how long the first line of the file is, and the file is large enough that I don't want to read it into memory all at once. I've tried using:
with open('./output files/log.txt', 'a+') as f:
f.write('This is example text')
content = f.readline()
print(content)
but the print statement is blank. When I try using open('./output files/log.txt') or open('./output files/log.txt', 'r+') instead of open('./output files/log.txt', 'a+') this works so I know it has to do with the 'a+ argument. My problem is that I have to append to the file. How can I append to the file and still get the first line without using something like
with open('./output files/log.txt', 'a+') as f_1:
f.write('This is example text')
with open('./output files/log.txt') as f_2:
content = f_2.readline()
print(content)
When you open a file with the append flag a, it moves the file descriptor's pointer to the end of the file, so that the write call will add to the end of the file.
The readline() function reads from the current pointer of the file until the next '\n' character it reads. So when you open a file with append, and then call readline, it will try to read a line starting from the end of the file. This is why your print call is coming up blank.
You can see this in action by looking at where the file object is currently pointing, using the tell() function.
To read the first line, you'd have to make sure the file's pointer is back at the beginning of the file, which you can do using the seek function. seek takes two arguments: offset and from_what. If you omit the second argument, offset is taken from the beginning of the file. So to jump to the beginning of the file, do: seek(0).
If you want to jump back to the end of the file, you can include the from_what option. from_what=2 means take the offset from the end of the file. So to jump to the end: seek(0, 2).
Demonstration of file pointers when opened in append mode:
Example using a text file that looks like this:
the first line of the file
and the last line
Code:
with open('example.txt', 'a+') as fd:
print fd.tell() # at end of file
fd.write('example line\n')
print fd.tell() # at new end of the file after writing
# jump to the beginning of the file:
fd.seek(0)
print fd.readline()
# jump back to the end of the file
fd.seek(0, 2)
fd.write('went back to the end')
console output:
45
57
the first line of the file
new contents of example.txt:
the first line of the file
and the last line
example line
went back to the end
Edit: added jumping back to end of file
You need to go back to the start of the file using seek(0), like so:
with open('./output files/log.txt', 'a+') as f_1:
f_1.write('This is example text')
f_1.seek(0)
print(f_1.readline())

Why does readline() put the file pointer at the end of the file in Python?

I have a code, that is supposed to write a text file, and then replace some text in one line with something else.
def read(text):
file = open('File.txt', 'r+') #open the file for reading and writing
x = file.readline() # read the first line of the file
file.seek(0, 0) #put the pointer back to the begining
file.readline() #skip one line
file.write(text) #write the text
file.close() #close the file
read('ABC')
At the beginning it's fine. It reads the first line and sets pointer to the beginning of the file. But then when it's supposed to read one line and put the pointer at the second line, it puts it at the end of the file. If I assign that to a variable, it only reads one line, but it still sets the pointer at the end of the file.
Apparently readline() doesn't work as I thought it was, so please tell me how I could read some lines of the text and the write something to the specific line.
Writing, by default, always takes place at the end of the file. Calling file.readline() doesn't change this behaviour, especially since readline() calls can use a buffer to read in larger blocks.
You could override by using file.seek() explicitly to go to the end of a line; you just read the line, you know the length, seek to that point:
x = file.readline()
file.seek(len(x), 0)
file.write(text) #write the text
Note that you cannot insert lines, or easily replace lines. A file is a stream of individual bytes, not lines, so if you write in a line of 10 characters (including the newline) you can only replace that line with 10 other characters. Longer or shorter lines won't work here; you are just going to replace fewer or more characters in the file and either partially replace a line or overwrite (part of) the next line.

Prepend line to beginning of a file

I can do this using a separate file, but how do I append a line to the beginning of a file?
f=open('log.txt','a')
f.seek(0) #get to the first position
f.write("text")
f.close()
This starts writing from the end of the file since the file is opened in append mode.
In modes 'a' or 'a+', any writing is done at the end of the file, even if at the current moment when the write() function is triggered the file's pointer is not at the end of the file: the pointer is moved to the end of file before any writing. You can do what you want in two manners.
1st way, can be used if there are no issues to load the file into memory:
def line_prepender(filename, line):
with open(filename, 'r+') as f:
content = f.read()
f.seek(0, 0)
f.write(line.rstrip('\r\n') + '\n' + content)
2nd way:
def line_pre_adder(filename, line_to_prepend):
f = fileinput.input(filename, inplace=1)
for xline in f:
if f.isfirstline():
print line_to_prepend.rstrip('\r\n') + '\n' + xline,
else:
print xline,
I don't know how this method works under the hood and if it can be employed on big big file. The argument 1 passed to input is what allows to rewrite a line in place; the following lines must be moved forwards or backwards in order that the inplace operation takes place, but I don't know the mechanism
In all filesystems that I am familiar with, you can't do this in-place. You have to use an auxiliary file (which you can then rename to take the name of the original file).
To put code to NPE's answer, I think the most efficient way to do this is:
def insert(originalfile,string):
with open(originalfile,'r') as f:
with open('newfile.txt','w') as f2:
f2.write(string)
f2.write(f.read())
os.remove(originalfile)
os.rename('newfile.txt',originalfile)
Different Idea:
(1) You save the original file as a variable.
(2) You overwrite the original file with new information.
(3) You append the original file in the data below the new information.
Code:
with open(<filename>,'r') as contents:
save = contents.read()
with open(<filename>,'w') as contents:
contents.write(< New Information >)
with open(<filename>,'a') as contents:
contents.write(save)
The clear way to do this is as follows if you do not mind writing the file again
with open("a.txt", 'r+') as fp:
lines = fp.readlines() # lines is list of line, each element '...\n'
lines.insert(0, one_line) # you can use any index if you know the line index
fp.seek(0) # file pointer locates at the beginning to write the whole file again
fp.writelines(lines) # write whole lists again to the same file
Note that this is not in-place replacement. It's writing a file again.
In summary, you read a file and save it to a list and modify the list and write the list again to a new file with the same filename.
num = [1, 2, 3] #List containing Integers
with open("ex3.txt", 'r+') as file:
readcontent = file.read() # store the read value of exe.txt into
# readcontent
file.seek(0, 0) #Takes the cursor to top line
for i in num: # writing content of list One by One.
file.write(str(i) + "\n") #convert int to str since write() deals
# with str
file.write(readcontent) #after content of string are written, I return
#back content that were in the file
There's no way to do this with any built-in functions, because it would be terribly inefficient. You'd need to shift the existing contents of the file down each time you add a line at the front.
There's a Unix/Linux utility tail which can read from the end of a file. Perhaps you can find that useful in your application.
If the file is the too big to use as a list, and you simply want to reverse the file, you can initially write the file in reversed order and then read one line at the time from the file's end (and write it to another file) with file-read-backwards module
An improvement over the existing solution provided by #eyquem is as below:
def prepend_text(filename: Union[str, Path], text: str):
with fileinput.input(filename, inplace=True) as file:
for line in file:
if file.isfirstline():
print(text)
print(line, end="")
It is typed, neat, more readable, and uses some improvements python got in recent years like context managers :)
I tried a different approach:
I wrote first line into a header.csv file. body.csv was the second file. Used Windows type command to concatenate them one by one into final.csv.
import os
os.system('type c:\\\header.csv c:\\\body.csv > c:\\\final.csv')
with open("fruits.txt", "r+") as file:
file.write("bab111y")
file.seek(0)
content = file.read()
print(content)

Categories