Python : Why do new lines keep appearing after every value amend? - python

I'm new to python and I'm trying various small things to see how they work:
items = dict()
with open(path) as f:
content = f.readlines()
for line in content:
splitStuff = line.split('!')
if splitStuff[0] in item:
items[splitStuff[0]] += ',' + Results[1]
else:
items[splitStuff[0]] = Results[1]
f.close()
with open(path2, 'a') as f:
for key, value in items.items():
f.write(key + '!' + value)
f.close()
It opens a file with this content:
3!Angel
3!Devil
4!Nasko
4!Pesho
4!Gosho
5!Kalin
6!Gancho
6!Boncho
6!Toncho
6!Moncho
And ends up writing a file with this content:
3!Angel
,Devil
4!Nasko
,Pesho
,Gosho
5!Kalin
6!Gancho
,Boncho
,Toncho
,Moncho
The part I don't understand is where are those new lines appearing from every time I edit a value?
EDIT: This is the desired output.
3!Angel,Devil
4!Nasko,Pesho,Gosho
5!Kalin
6!Gancho,Boncho,Toncho,Moncho
EDIT2: Never mind figured it out. Its because there are new lines in the original file and apparently reading file line by line catches them as well in python, unlike c# where they are ignored.

Lines you read with readlines() have a trailing newline.
for line in content:
line = line.rstrip()
splitStuff = line.split('!')
... etc ...

A solution could look like this:
path = "file1"
path2 = "file2"
items = dict()
with open(path) as f:
content = f.readlines()
for line in content:
splitStuff = line.split('!')
if splitStuff[0] in items:
items[splitStuff[0]] += ',' + splitStuff[1][:-1]
else:
items[splitStuff[0]] = splitStuff[1][:-1]
f.close()
with open(path2, 'a') as f:
for key, value in items.items():
f.write(key + '!' + value)
f.write("\n")
f.close()
You just had to remove the newline from each line of the file by adding [:-1].

Related

Update Txt file in python

I have a text file with names and results. If the name already exists, only the result should be updated. I tried with this code and many others, but without success.
The content of the text file looks like this:
Ann, 200
Buddy, 10
Mark, 180
Luis, 100
PS: I started 2 weeks ago, so don't judge my bad code.
from os import rename
def updatescore(username, score):
file = open("mynewscores.txt", "r")
new_file = open("mynewscores2.txt", "w")
for line in file:
if username in line:
splitted = line.split(",")
splitted[1] = score
joined = "".join(splitted)
new_file.write(joined)
new_file.write(line)
file.close()
new_file.close()
maks = updatescore("Buddy", "200")
print(maks)
I would suggest reading the csv in as a dictionary and just update the one value.
import csv
d = {}
with open('test.txt', newline='') as f:
reader = csv.reader(f)
for row in reader:
key,value = row
d[key] = value
d['Buddy'] = 200
with open('test2.txt','w', newline='') as f:
writer = csv.writer(f)
for key, value in d.items():
writer.writerow([key,value])
So what needed to be different mostly is that when in your for loop you said to put line in the new text file, but it's never said to Not do that when wanting to replace a score, all that was needed was an else statement below the if statement:
from os import rename
def updatescore(username, score):
file = open("mynewscores.txt", "r")
new_file = open("mynewscores2.txt", "w")
for line in file:
if username in line:
splitted = line.split(",")
splitted[1] = score
print (splitted)
joined = ", ".join(splitted)
print(joined)
new_file.write(joined+'\n')
else:
new_file.write(line)
file.close()
new_file.close()
maks = updatescore("Buddy", "200")
print(maks)
You can try this, add the username if it doesn't exist, else update it.
def updatescore(username, score):
with open("mynewscores.txt", "r+") as file:
line = file.readline()
while line:
if username in line:
file.seek(file.tell() - len(line))
file.write(f"{username}, {score}")
return
line = file.readline()
file.write(f"\n{username}, {score}")
maks = updatescore("Buddy", "300")
maks = updatescore("Mario", "50")
You have new_file.write(joined) inside the if block, which is good, but you also have new_file.write(line) outside the if block.
Outside the if block, it's putting both the original and fixed lines into the file, and since you're using write() instead of writelines() both versions get put on the same line: there's no \n newline character.
You also want to add the comma: joined = ','.join(splitted) since you took the commas out when you used line.split(',')
I got the result you seem to be expecting when I put in both these fixes.
Next time you should include what you are expecting for output and what you're giving as input. It might be helpful if you also include what Error or result you actually got.
Welcome to Python BTW
Removed issues from your code:
def updatescore(username, score):
file = open("mynewscores.txt", "r")
new_file = open("mynewscores2.txt", "w")
for line in file.readlines():
splitted = line.split(",")
if username == splitted[0].strip():
splitted[1] = str(score)
joined = ",".join(splitted)
new_file.write(joined)
else:
new_file.write(line)
file.close()
new_file.close()
I believe this is the simplest/most straightforward way of doing things.
Code:
import csv
def update_score(name: str, score: int) -> None:
with open('../resources/name_data.csv', newline='') as file_obj:
reader = csv.reader(file_obj)
data_dict = dict(curr_row for curr_row in reader)
data_dict[name] = score
with open('../out/name_data_out.csv', 'w', newline='') as file_obj:
writer = csv.writer(file_obj)
writer.writerows(data_dict.items())
update_score('Buddy', 200)
Input file:
Ann,200
Buddy,10
Mark,180
Luis,100
Output file:
Ann,200
Buddy,200
Mark,180
Luis,100

How to read a file and print it, skipping certain lines in python

I would like to read a file line by line but ignore any that contain a colon (:).
I'm currently opening one file, reading it, and trying to print it before eventually put it into a new file.
def shoppinglist():
infile = open('filename.txt')
contents = infile.readline()
output = open('outputfilename.txt', 'w')
while ":" not in contents:
contents = infile.readline()
else:
contentstr = contents.split()
print(contentstr)
output.write(contents)
infile.close()
output.close()
As it is, one line is repeated over and over and over.
Try:
def shoppinglist():
contents = ""
with open('filename.txt', 'r') as infile:
for line in infile.readlines():
if ":" not in line:
contents += line
with open('outputfilename.txt', 'w') as output_file:
output_file.write(contents)

How can I remove the method arguments from a method declaration from a source code file and write it back?

I have a .txt file that contains pre-processed source code. It looks like this:
public static <V, E> E addEdgeWithVertices(Graph<V, E> g, V sourceVertex, V targetVertex)
g.addVertex(sourceVertex);
g.addVertex(targetVertex);
return g.addEdge(sourceVertex, targetVertex);
I want to get rid of the method arguments in the first line(method declaration) i.e. Graph<V, E> g, V sourceVertex, V targetVertex and write this back to my same file.
This is my code, it doesn't seem to work:
with open('source_code.txt', 'r') as f:
lines = f.readlines()
match_parameters = re.search("(public|protected|private|static) .*\((.*)\)", lines[0])
remove_parameters =
match_parameters.group(0).replace(match_parameters[2], "")
with open('source_code.txt', 'w') as f:
f.writelines(remove_parameters)
Actual Output(remaining lines are missing):
public static <V, E> E addEdgeWithVertices()
Expected Output:
public static <V, E> E addEdgeWithVertices()
g.addVertex(sourceVertex);
g.addVertex(targetVertex);
return g.addEdge(sourceVertex, targetVertex);
If you know that it will always be in the first line, you can do:
import re
with open('source_code.txt', 'r') as f:
lines = f.readlines()
match_parameters = re.search("(public|protected|private|static) .*\((.*)\)", lines[0])
remove_parameters = match_parameters.group(0).replace(match_parameters[2], "")
lines[0] = remove_parameters + "\n"
with open('source_code.txt', 'w') as f:
f.writelines("".join(lines))
You should be searching all the lines. And don't forget to add the original line whenever search returns None.
clean_code = []
with open('source_code.txt', 'r') as f:
lines = f.readlines()
for line in lines:
match_parameters = re.search("(public|protected|private|static) .*\((.*)\)", line)
if match_parameters is not None:
clean_code.append(match_parameters.group(0).replace(match_parameters[2], "") + "\n")
else:
clean_code.append(line)
with open('source_code.txt', 'w') as f:
f.writelines(clean_code)
However, use Calvin's answer if in fact the replacement has to be done only on the first line.

replace line if found or append - python

I have text that is key-value pairs separated by '='. I would like to replace the line if the key matches. if not, i would like to append it at the bottom. I've tried several ways, including:
def split_command_key_and_value(command):
if '=' in command:
command2 = command.split('=')
return command2
def test(command, path):
command2 = split_command_key_and_value(command)
pattern = command2[0]
myfile = open(path,'r') # open file handle for read
# use r'', you don't need to replace '\' with '/'
result = open(path, 'w') # open file handle for write
for line in myfile:
line = line.strip() # it's always a good behave to strip what you read from files
if pattern in line:
line = command # if match, replace line
result.write(line) # write every line
myfile.close() # don't forget to close file handle
result.close()
I know the above is just to replace text, but it deletes the text in the file, and I can't see why. Could someone point me in the right direction?
Thanks
Update:
I'm almost there, but some of my lines have similar keys, so mutiple lines are matching when only 1 should. I've tried to incorporate a regex boundary in my loop with no luck. My code is below. Does anyone have a suggestion?
There is some text in the file that isn't key-value, so I would like to skip that.
def modify(self, name, value):
comb = name + ' ' + '=' + ' ' + value + '\n'
with open('/file/', 'w') as tmpstream:
with open('/file/', 'r') as stream:
for line in stream:
if setting_name in line:
tmpstream.write(comb)
else:
tmpstream.write(line)
I think I got it. See code below.
def modify(self, name, value):
comb = name + ' ' + '=' + ' ' + value + '\n'
mylist = []
with open('/file/', 'w') as tmpstream:
with open('/file/', 'r') as stream:
for line in stream:
a = line.split()
b = re.compile('\\b'+name+'\\b')
if len(a) > 0:
if b.search(a[0]):
tmpstream.write(comb)
else:
tmpstream.write(line)
I spoke too soon. It stops at the key-value I provide. So, it only writes one line, and doesn't write the lines that don't match.
def modify(name, value):
comb = name + ' ' + '=' + ' ' + value + '\n'
mylist = []
with open('/file1', 'w') as tmpstream:
with open('/file2', 'r') as stream:
for line in stream:
a = line.split()
b = re.compile('\\b'+name+'\\b')
if len(a) > 0:
if b.search(a[0]):
tmpstream.write(comb)
else:
tmpstream.write(line)
Can anyone see the issue?
Because when you open file for writing
result = open(path, 'w') # open file handle for write
you just erase it content. Try to write in different file and after all work done replace old file with new one. Or read all data into memory and then process it and write to file.
with open(path) as f:
data = f.read()
with open(path, 'w') as f:
for l in data:
# make job here
first of all you are reading an writing the same file ...
you could first read it all and the write line by line
with open(path,'r') as f:
myfile = f.read() # read everything in the variable "myfile"
result = open(path, 'w') # open file handle for write
for line in myfile.splitlines(): # process the original file content 1 line at a time
# as before
I strongly recommend reading python's documentation on how to read and write files.
If you open an existing file in write-mode open(path, 'w'), its content will be erased:
mode can be (...) 'w' for only writing (an existing file with the same name will be erased)
To replace a line in python you can have a look at this: Search and replace a line in a file in Python
Here is one the solutions provided there adapted to your context (tested for python3):
from tempfile import mkstemp
from shutil import move
from os import close
def test(filepath, command):
# Split command into key/value
key, _ = command.split('=')
matched_key = False
# Create a temporary file
fh, tmp_absolute_path = mkstemp()
with open(tmp_absolute_path, 'w') as tmp_stream:
with open(filepath, 'r') as stream:
for line in stream:
if key in line:
matched_key = True
tmp_stream.write(command + '\n')
else:
tmp_stream.write(line)
if not matched_key:
tmp_stream.write(command + '\n')
close(fh)
move(tmp_absolute_path, filepath)
Note that with the code above every line that matches key (key=blob or blob=key) will be replaced.

Python - read from file skip lines starts with #

Try to read file and make a dictionary from lines, skippipng lines starts with # symbol
file example:
param1=val1
# here is comment
My function:
def readFromFile(name):
config = {}
with open(name, "r") as f:
for line in f.readlines():
li=line.lstrip()
if not li.startswith("#"):
config[line.split('=')[0]] = line.split('=')[1].strip()
return config
I get list index out of range error
But!
if i try to skip lines starts with, for example, symbol "h" - function works well...
Try with:
def readFromFile(name):
config = {}
with open(name, "r") as f:
for line in f.readlines():
li = line.lstrip()
if not li.startswith("#") and '=' in li:
key, value = line.split('=', 1)
config[key] = value.strip()
return config
You maybe have a blank line which breaks your split()
Your code works just fine, except for lines that neither start with a # nor contain a = character. Usually, those are empty lines.
Test for the = character before splitting:
def readFromFile(name):
config = {}
with open(name, "r") as f:
for line in f.readlines():
li=line.lstrip()
if not li.startswith("#") and '=' in li:
config[line.split('=')[0]] = line.split('=')[1].strip()
return config
You can simplify the code and make it a dict comprehension:
def readFromFile(name):
with open(name, "r") as f:
return {k: v
for line in f
for (k, v) in (line.strip().split('=', 1),)
if '=' in line.strip() and line[:1] != '#'}
You can loop over f (a file object) directly; no need to read all lines into memory using f.readlines() first. I used .split('=', 1) to only split on the equals sign once.
You can return a dictionary directly:
def readFromFile(name):
with open(name) as f:
return { line.split('=')[0]:line.split('=')[1].strip() for line in f \
if not line.startswith('#' and '=' in line }

Categories