Problem in Python with function only writing one line to file - python

I am trying to write a function in Python 3 that will write all lines that end with the string 'halloween' to a file. When I call this function, I can only get one line to write to the output file (file_2.txt). Can anyone point out where my problem is? Thanks in advance.
def parser(reader_o, infile_object, outfile_object):
for line in reader_o:
if line.endswith('halloween'):
return(line)
with open("file_1.txt", "r") as file_input:
reader = file_input.readlines()
with open("file_2.txt", "w") as file_output:
file_output.write(parser(reader))

def parser(reader_o):
for line in reader_o:
if line.rstrip().endswith('halloween'):
yield line
with open("file_1.txt", "r") as file_input:
with open("file_2.txt", "w") as file_output:
file_output.writelines(parser(file_input))
This is called a generator. It can also be written as an expression instead of a function:
with open("file_1.txt", "r") as file_input:
with open("file_2.txt", "w") as file_output:
file_output.writelines(line for line in file_input if line.rstrip().endswith('halloween'))
If you're on Python 2.7 / 3.2, you can do the two withs like this:
with open("file_1.txt", "r") as file_input, open("file_2.txt", "w") as file_output:
You don't need to do readlines() on the file, just telling the loop to iterate over the open file itself will do the exact same thing.
Your problem was that return always would exit the loop on the first match. yield stops the loop, passes out the value, then the generator can be started again from the same point.

line.endswith('halloween') might only work on the last of a file, since all other lines will have a newline appended. rstrip the line first. Also, use yield instead of return.
if line.rstrip().endswith('halloween'):
yield line
Note that this will also strip off spaces at the end of the line, which may or may not be what you want.
You'll also have to modify your consumer to
with open("file_2.txt", "w") as file_output:
for ln in parser(reader):
file_output.write(ln)

Perhaps your parser function should be a generator. At the moment it's only called once and returns the first line that has "halloween" in it.
Like the following:
def parser(reader_o):
for line in reader_o:
if line.endswith('halloween'):
yield line
with open("file_1.txt", "r") as file_input:
with open("file_2.txt", "w") as file_output:
file_output.writelines(parser(file_input))

Related

Why I can't read the first line of the file?

When I read line by line from the file, the first line is skipped. Why does this happen and How can I solve this?
web="www.instagram.com/"
f= open("accounts.txt","r")
ft=open("add.txt", "w")
links=[]
for l in f:
name=f.readlines()
print(name)
fullweb=web+name
links.append(fullweb)
ft.writeline(fullweb)
f.close()
ft.close()
print(links)
You were starting iterating over the file f (as iterator)
for l in f:
getting the 1st line and then, immediately read all its remaining contents with f.readlines().
That makes no big sense (if not saying - no sense).
You should either do for l in f or f.readlines, not both!
with open("accounts.txt") as f:
lines = f.readlines()
...
or
with open("accounts.txt") as f:
for line in f:
...
Explanation:
With the iterator, you already iterate over the lines in the file. So it already "eats up" the first line, than you reach "readlines" readlines reads the other lines of the file. So doing both, is plainly not helpful.
I think I should have used the iteration variable instead of calling the function to readline().Anyway this solved the problem.
for l in f:
print(l)
fullweb=web+l
links.append(fullweb)
ft.writelines(fullweb)
But I don't know the theory behind how the for loop reads the file line by line without any function.

Concenating to every value in a list

So I have a file with some lines of text:
here's a sentence
look! another one
here's a third one too
and another one
one more
and I have some code that takes the each line and puts it into a list and then reverses the order of the whole list but now I don't know how to write each line back to the file and delete the existing ones in the text file.
Also when I run this code:
file_lines = open(file_name).readlines()
print(file_lines)
file_lines.reverse()
print(file_lines)
everything works and the line order is reversed, but when I run this code:
text_file = open(file_name, "w")
file_lines = open(file_name).readlines()
print(file_lines)
file_lines.reverse()
print(file_lines)
for line in file_lines:
text_file.write(line)
it prints empty lists for some reason.
You can fix it by doing just 2 little changes in your script.
Use \r+ in place of \w+
Before performing write operation, place file position indicator to the beginning
text_file.seek(0)
» rw_file.txt - before operation
here's a sentence
look! another one
here's a third one too
and another one
one more
Below is your modified script to reverse the content of file (It worked).
def reverseFile(file_name):
text_file = open(file_name, "r+") # Do not use 'w+', it will erase your file content
file_lines = [line.rstrip('\n') for line in text_file.readlines()]
file_lines.reverse()
print(file_lines)
text_file.seek(0) # Place file position indicator at beginning
for line_item in file_lines:
text_file.write(line_item+"\n")
reverseFile("rw_file.txt")
» rw_file.txt - after operation
one more
and another one
here's a third one too
look! another one
here's a sentence
If you open the file in 'w' mode, the file is erased. From the docs:
'w' for only writing (an existing file with the same name will be
erased)
You should also use the with keyword:
It is good practice to use the with keyword when dealing with file
objects. The advantage is that the file is properly closed after its
suite finishes...
I would recommend you read the contents of the file first, process that data, and then write:
def reverseFile(file_name):
with open(file_name, 'r') as f:
file_lines = [line.rstrip('\n') for line in f.readlines()]
file_lines.reverse()
with open(file_name, "w") as f:
for line in file_lines:
f.write(line + '\n')
reverseFile('text_lines.txt')

Appending characters to each line in a txt file with python

I wrote the following python code snippet to append a lower p character to each line of a txt file:
f = open('helloworld.txt','r')
for line in f:
line+='p'
print(f.read())
f.close()
However, when I execute this python program, it returns nothing but an empty blank:
zhiwei#zhiwei-Lenovo-Rescuer-15ISK:~/Documents/1001/ass5$ python3 helloworld.py
Can anyone tell me what's wrong with my codes?
Currently, you are only reading each line and not writing to the file. reopen the file in write mode and write your full string to it, like so:
newf=""
with open('helloworld.txt','r') as f:
for line in f:
newf+=line.strip()+"p\n"
f.close()
with open('helloworld.txt','w') as f:
f.write(newf)
f.close()
well, type help(f) in shell, you can get "Character and line based layer over a BufferedIOBase object, buffer."
it's meaning:if you reading first buffer,you can get content, but again. it's empty。
so like this:
with open(oldfile, 'r') as f1, open(newfile, 'w') as f2:
newline = ''
for line in f1:
newline+=line.strip()+"p\n"
f2.write(newline)
open(filePath, openMode) takes two arguments, the first one is the path to your file, the second one is the mode it will be opened it. When you use 'r' as second argument, you are actually telling Python to open it as an only reading file.
If you want to write on it, you need to open it in writing mode, using 'w' as second argument. You can find more about how to read/write files in Python in its official documentation.
If you want to read and write at the same time, you have to open the file in both reading and writing modes. You can do this simply by using 'r+' mode.
It seems that your for loop has already read the file to the end, so f.read() return empty string.
If you just need to print the lines in the file, you could move the print into for loop just like print(line). And it is better to move the f.read() before for loop:
f = open("filename", "r")
lines = f.readlines()
for line in lines:
line += "p"
print(line)
f.close()
If you need to modify the file, you need to create another file obj and open it in mode of "w", and use f.write(line) to write the modified lines into the new file.
Besides, it is more better to use with clause in python instead of open(), it is more pythonic.
with open("filename", "r") as f:
lines = f.readlines()
for line in lines:
line += "p"
print(line)
When using with clause, you have no need to close file, this is more simple.

Looping over lines with Python

So I have a file that contains this:
SequenceName 4.6e-38 810..924
SequenceName_FGS_810..924 VAWNCRQNVFWAPLFQGPYTPARYYYAPEEPKHYQEMKQCFSQTYHGMSFCDGCQIGMCH
SequenceName 1.6e-38 887..992
SequenceName_GYQ_887..992 PLFQGPYTPARYYYAPEEPKHYQEMKQCFSQTYHGMSFCDGCQIGMCH
I want my program to read only the lines that contain these protein sequences. Up until now I got this, which skips the first line and read the second one:
handle = open(filename, "r")
handle.readline()
linearr = handle.readline().split()
handle.close()
fnamealpha = fname + ".txt"
handle = open(fnamealpha, "w")
handle.write(">%s\n%s\n" % (linearr[0], linearr[1]))
handle.close()
But it only processes the first sequence and I need it to process every line that contains a sequence, so I need a loop, how can I do it?
The part that saves to a txt file is really important too so I need to find a way in which I can combine these two objectives.
My output with the above code is:
>SequenceName_810..924
VAWNCRQNVFWAPLFQGPYTPARYYYAPEEPKHYQEMKQCFSQTYHGMSFCDGCQIGMCH
Okay, I think I understand your question--you want to iterate over the lines in the file, right? But only the second line in the sequence--the one with the protein sequence--matters, correct? Here's my suggestion:
# context manager `with` takes care of file closing, error handling
with open(filename, 'r') as handle:
for line in handle:
if line.startswith('SequenceName_'):
print line.split()
# Write to file, etc.
My reasoning being that you're only interested in lines that start with SequenceName_###.
Use readlines and throw it all into a for loop.
with open(filename, 'r') as fh:
for line in fh.readlines:
# do processing here
In the #do processing here section, you can just prepare another list of lines to write to the other file. (Using with handles all the proper closure and sure.)

How to open a file using the open with statement

I'm looking at how to do file input and output in Python. I've written the following code to read a list of names (one per line) from a file into another file while checking a name against the names in the file and appending text to the occurrences in the file. The code works. Could it be done better?
I'd wanted to use the with open(... statement for both input and output files but can't see how they could be in the same block meaning I'd need to store the names in a temporary location.
def filter(txt, oldfile, newfile):
'''\
Read a list of names from a file line by line into an output file.
If a line begins with a particular name, insert a string of text
after the name before appending the line to the output file.
'''
outfile = open(newfile, 'w')
with open(oldfile, 'r', encoding='utf-8') as infile:
for line in infile:
if line.startswith(txt):
line = line[0:len(txt)] + ' - Truly a great person!\n'
outfile.write(line)
outfile.close()
return # Do I gain anything by including this?
# input the name you want to check against
text = input('Please enter the name of a great person: ')
letsgo = filter(text,'Spanish', 'Spanish2')
Python allows putting multiple open() statements in a single with. You comma-separate them. Your code would then be:
def filter(txt, oldfile, newfile):
'''\
Read a list of names from a file line by line into an output file.
If a line begins with a particular name, insert a string of text
after the name before appending the line to the output file.
'''
with open(newfile, 'w') as outfile, open(oldfile, 'r', encoding='utf-8') as infile:
for line in infile:
if line.startswith(txt):
line = line[0:len(txt)] + ' - Truly a great person!\n'
outfile.write(line)
# input the name you want to check against
text = input('Please enter the name of a great person: ')
letsgo = filter(text,'Spanish', 'Spanish2')
And no, you don't gain anything by putting an explicit return at the end of your function. You can use return to exit early, but you had it at the end, and the function will exit without it. (Of course with functions that return a value, you use the return to specify the value to return.)
Using multiple open() items with with was not supported in Python 2.5 when the with statement was introduced, or in Python 2.6, but it is supported in Python 2.7 and Python 3.1 or newer.
http://docs.python.org/reference/compound_stmts.html#the-with-statement
http://docs.python.org/release/3.1/reference/compound_stmts.html#the-with-statement
If you are writing code that must run in Python 2.5, 2.6 or 3.0, nest the with statements as the other answers suggested or use contextlib.nested.
Use nested blocks like this,
with open(newfile, 'w') as outfile:
with open(oldfile, 'r', encoding='utf-8') as infile:
# your logic goes right here
You can nest your with blocks. Like this:
with open(newfile, 'w') as outfile:
with open(oldfile, 'r', encoding='utf-8') as infile:
for line in infile:
if line.startswith(txt):
line = line[0:len(txt)] + ' - Truly a great person!\n'
outfile.write(line)
This is better than your version because you guarantee that outfile will be closed even if your code encounters exceptions. Obviously you could do that with try/finally, but with is the right way to do this.
Or, as I have just learnt, you can have multiple context managers in a with statement as described by #steveha. That seems to me to be a better option than nesting.
And for your final minor question, the return serves no real purpose. I would remove it.
Sometimes, you might want to open a variable amount of files and treat each one the same, you can do this with contextlib
from contextlib import ExitStack
filenames = [file1.txt, file2.txt, file3.txt]
with open('outfile.txt', 'a') as outfile:
with ExitStack() as stack:
file_pointers = [stack.enter_context(open(file, 'r')) for file in filenames]
for fp in file_pointers:
outfile.write(fp.read())

Categories

Resources