<style>
.markdown-body h1:first-of-type {
margin-top: 24px;
}
.markdown-body h1 {
margin-top: 64px;
}
.markdown-body h1 + h2 {
margin-top: 32px;
}
.markdown-body h2 {
margin-top: 48px;
}
.markdown-body h2.topics {
font-size: 1.8em;
border-bottom: none;
}
.markdown-body h3 {
color: cornflowerblue;
}
.markdown-body h4 {
color: cornflowerblue;
}
.markdown-body p strong {
font-weight: normal;
color: red;
}
.exercise {
font-size: 150%;
font-weight: bold;
color: rgb(227,112,183);
}
.note {
color: red;
}
</style>
# ACIT 1515 - Lesson 7
<h2 class="topics">Topics</h2>
- [File I/O](#File-IO)
## File I/O
Python's built-in [file](https://docs.python.org/3/glossary.html#term-file-object) object allows us to read and write data to and from files, which in turn gives us ==persistence==: permanently stored data that we can access across multiple executions of a script.
Imagine a simple script allowing a user to store grades, without persistence:
```python=
grades = []
def enter_grades():
keep_entering = True
while keep_entering:
course = input('Please enter a course: ')
grade = input('Please enter a grade: ')
# store a tuple containing the course and grade in the 'grades' list
grades.append((course, grade))
response = input('Do you want to enter a grade? ')
if response == 'no':
keep_entering = false
def run():
while True:
menu = ['View grades', 'Edit grades', 'Add grade', 'Quit']
for index, option in enumerate(menu):
print(f'{index + 1}. {option}')
choice = input('Please choose an option: ')
if choice == '3':
enter_grades()
elif choice == '2':
# etc.
if __name__ == '__main__':
run()
```
The above program asks a user to choose between viewing grades, editing grades, and adding grade. When the user chooses 'Add grade', they can enter multiple courses and associated grades, which are stored in the 'grades' list. The 'grades' list could be used in the (not shown) edit_grades and view_grades functions to either a) allow changes to existing grades or b) view the full list of grades.
All of the above noted functionality works well enough, but: what happens when the user quits and re-runs the program? All of the courses and grades they previously entered are gone. As is the case with most Python scripts, any data that is created and stored in variables while the program is running is lost. To improve the experience for the user, we can *write the data to a file* while the program is running, and *read the data from the file* the next time the program starts. This prevents the user from needing to re-enter the entire list the next time they start the program.
## The `open` function
Python's `open` function gives a ==file== object that we can use for either reading and/or writing, depending on which [mode](https://docs.python.org/3/library/functions.html#open) we choose.
```python=
location = '/home/you/Documents/BCIT/ACIT1515/assignment1/metadata.txt'
f = open(location, 'r') # open the file in 'read' mode, 'r'
```
After opening the file, we can read the contents using `.read()` and then the file must be closed:
```python=
location = '/home/you/Documents/BCIT/ACIT1515/assignment1/metadata.txt'
f = open(location, 'r') # open the file in 'read' mode, 'r'
contents = f.read()
print(contents)
f.close()
```
Alternatively, we can use the `with` syntax to open the file - this has the benefit of automatically closing the file (no `f.close()` is required) and is considered best practice.
```python=
location = '/home/you/Documents/BCIT/ACIT1515/assignment1/metadata.txt'
with open(location, 'r') as f:
contents = f.read()
print(contents)
```
## `read`, `readline`, and `readlines`
Note that the `read()` method returns *all the content of the file* as a single string. In some cases it may be much more convenient to read and process a file line-by-line. This can be achieved in a few different ways.
Simply looping through the file object:
```python=
location = '/home/you/Documents/BCIT/ACIT1515/assignment1/metadata.txt'
with open(location, 'r') as f:
for line in f:
print(line, end='')
```
or by using the `readline` function:
```python=
location = '/home/you/Documents/BCIT/ACIT1515/assignment1/metadata.txt'
with open(location, 'r') as f:
while True:
line = f.readline()
if line == '':
break
else:
print(line, end='')
```
or using `readlines`, which returns a list of all the lines in the file:
```python=
location = '/home/you/Documents/BCIT/ACIT1515/assignment1/metadata.txt'
with open(location, 'r') as f:
lines = f.readlines()
for line in lines:
print(line, end='')
```
## `write`
To write data to a file, we can first open the file in *write* mode ('w'), and then call the file object's `write` method. Note that the code below **overwrites** any existing content in the file - if you want to *add* to existing content, use *append* mode ('a') which writes data to the end of a file.
```python=
location = '/home/you/Documents/BCIT/ACIT1515/assignment1/metadata.txt'
content = 'This is some test content'
with open(location, 'w') as f:
f.write(content)
```
## Adding persistence using `read` and `write`
Using Python's file object methods, we can now refactor the original example at the top of this page such that the grades are written to a file when added, and read from the file when the program begins:
```python=
grades = []
def enter_grades():
keep_entering = True
while keep_entering:
course = input('Please enter a course: ')
grade = input('Please enter a grade: ')
# store a tuple containing the course and grade in the 'grades' list
grades.append[(course, grade)]
response = input('Do you want to enter a grade? ')
if response == 'no':
keep_entering = False
"""
Add writing the grades list to a file when the user is done entering
"""
grades_string = ''
for value in grades:
grades_string += f'{value[0]},{value[1]}\n'
with open('grades.txt', 'w') as f:
f.write(grades_string)
def run():
while True:
menu = ['View grades', 'Edit grades', 'Add grade', 'Quit']
for index, option in enumerate(menu):
print(f'{index + 1}. {option}')
choice = input('Please choose an option: ')
if choice == '3':
enter_grades()
elif choice == '2':
# etc.
if __name__ == '__main__':
"""
Read the file and recreate the list of tuples stored in the
'grades' variable
"""
with open('grades.txt', 'r') as f:
for line in f:
# string.split() turns a string into a list
# and separates values by the 'delimiter' you provide
# as an argument, e.g. ','
value = line.split(',')
value[1] = value[1].replace('\n', '') # remove newline
value = tuple(value)
grades.append(value)
run()
```