Reputation: 313
I am willing to concatenate two file on line basis, so that every line of each file is merged consecutively in a third file.
So I have the following code and the following text files:
file1.txt
1
3
5
7
file2.txt
2
4
6
code:
from ast import literal_eval
def merge_lines():
with open("file1.txt") as f1, open("file2.txt") as f2:
with open("file3.txt", "r+") as tfile:
f1_lists = (literal_eval(line) for line in f1)
f2_lists = (literal_eval(line) for line in f2)
for l1, l2 in zip(f1_lists, f2_lists):
tfile.write(str(l1))
tfile.write("\n")
tfile.write(str(l2))
tfile.write("\n")
combine_hands()
This works just fine as the output file looks likes:
file3.txt
1
2
3
4
5
6
My question is why the last line (number 7) of the file1.txt is not being merged?
Upvotes: 0
Views: 197
Reputation: 10219
The last line is omitted because zip
stops at the end of the shorter iterable.
What you want is probably
from itertools import zip_longest
def merge_lines():
with open("file1.txt") as f1,\
open("file2.txt") as f2,\
open("file3.txt", "w") as tfile:
for l1, l2 in zip_longest(f1, f2, fillvalue="Empty line"):
# Or you can place a sentinel value for `fillvalue`
# and check it and don't write to file when you see it.
tfile.write(l1.strip() + "\n")
tfile.write(l2.strip() + "\n")
or if you don't want to write out to file the empty line
for l1, l2 in zip_longest(f1, f2, fillvalue=None):
if l1:
tfile.write(l1)
if l2:
tfile.write(l2)
And since the default value of fillvalue
is None
, we can simplify it further to
for l1, l2 in zip_longest(f1, f2):
if l1:
tfile.write(l1)
if l2:
tfile.write(l2)
The following changes were made after reading the comment and answer from @DYZ:
Upvotes: 3
Reputation: 1060
As others mentioned, it's because you uses normal zip() , the last line of the longest list (file) got omitted.zip will form tuples only up to the length of shorter list
Instead you can use either of the below extended zip which will pad up to longest list
itertools.zip_longest -- in python 3.x +
itertools.izip_longest --in python 2.6+
Upvotes: 1
Reputation: 57135
With the function zip_longest
, your code could be written in a very compact way:
from itertools import zip_longest
with open("file1.txt") as f1,\
open("file2.txt") as f2,\
open("file3.txt", "w") as tfile:
for l1, l2 in zip_longest(f1, f2, fillvalue=''):
if l1 != '': tfile.write(l1)
if l2 != '': tfile.write(l2)
No explicit reading or type conversion is needed.
Upvotes: 2
Reputation: 7808
The expression zip(f1_lists, f2_lists)
returns a list of tuples only as long as the shorter of the provided iterables (in your case lists).
So this behavior is expected per zip documentation.
If you want to include the last line, try using itertools.ziplongest
from ast import literal_eval
from itertools import ziplongest
def merge_lines():
with open("file1.txt") as f1, open("file2.txt") as f2:
with open("file3.txt", "r+") as tfile:
f1_lists = (literal_eval(line) for line in f1)
f2_lists = (literal_eval(line) for line in f2)
for l1, l2 in zip_longest(f1_lists, f2_lists, fillvalue='x'):
tfile.write(str(l1))
tfile.write("\n")
tfile.write(str(l2))
tfile.write("\n")
combine_hands()
Upvotes: 0