Writing CSV files
A CSV file is written like any other text file, one line at a time. Each line is the values of one record with the separator between them, and a line break at the end. An f-string builds such a line neatly. Here the data comes from a dictionary, with a header line first:
stock = {"pens": 120, "notebooks": 45, "staplers": 7}
with open("stock.csv", "w") as file:
file.write("item;amount\n")
for item, amount in stock.items():
file.write(f"{item};{amount}\n")
with open("stock.csv") as file:
print(file.read())
item;amount
pens;120
notebooks;45
staplers;7The f-string turns the numbers into text on the way, so write gets the string it needs.
Lines with many values
Some records hold a whole list of values, such as a day's temperature readings, and the lists can be of different lengths. An f-string needs a {...} for every value, so it can't write a list whose length it doesn't know. The string method join, from the module on strings and lists, can: it puts the separator between the strings of a list, and never after the last one. join only accepts strings, so the loop below turns each number into one with str first:
readings = {
"Mon": [12, 15, 9, 11],
"Tue": [14, 16],
}
with open("readings.csv", "w") as file:
for day, values in readings.items():
fields = [day]
for value in values:
fields.append(str(value))
file.write(";".join(fields) + "\n")
with open("readings.csv") as file:
print(file.read())
Mon;12;15;9;11
Tue;14;16The list fields starts with the day, and every reading is added after it as a string. Leave out the str, and join stops at the first number with TypeError: sequence item 1: expected str instance, int found.
Read, process, write
Many programs read data from one file, work something out and write the results to another file. Such a program is easiest to write, and to fix, in pieces: a function for each part. Here a shop keeps its prices in prices.csv, and for a sale it wants a second file with the old and the new price of every item:
item;price
coffee;3.20
bagel;2.50
juice;4.00def read_prices(filename):
prices = {}
with open(filename) as file:
for line in file:
parts = line.strip().split(";")
if parts[0] == "item":
continue
prices[parts[0]] = float(parts[1])
return prices
def sale_lines(prices, percent):
lines = ["item;price;sale price"]
for item, price in prices.items():
sale = price * (100 - percent) / 100
lines.append(f"{item};{price:.2f};{sale:.2f}")
return lines
def write_lines(filename, lines):
with open(filename, "w") as file:
for line in lines:
file.write(line + "\n")
prices = read_prices("prices.csv")
write_lines("sale.csv", sale_lines(prices, 20))
with open("sale.csv") as file:
print(file.read())
item;price;sale price
coffee;3.20;2.56
bagel;2.50;2.00
juice;4.00;3.20Look at what each function needs to know. read_prices is the only one that knows what the input file looks like, header line and all. sale_lines never touches a file: it gets a dictionary and returns a list of strings, so you can try it out with print(sale_lines({"tea": 2.0}, 50)) before any file exists. And write_lines knows nothing about prices: it writes any list of lines into any file, so your next program can use it just as it is. For a sale of 30 percent, only the number in the call changes.