with open(path) as f:
content = f.read() # the entire file, as one string
For line-oriented files, two other options are often more convenient:
with open(path) as f:
lines = f.readlines() # a list of lines, each still ending in "\n"
with open(path) as f:
for line in f: # iterate the file directly, one line at a time
print(line.strip()) # .strip() removes the trailing newline
Iterating a file object directly (the third form) is the most memory-efficient for large files — it reads one line at a time instead of loading everything at once — but for the file sizes in this course, all three approaches work fine.
content.splitlines() is another handy option: it splits content into a list of lines without the trailing "\n" on each one (unlike readlines()), which is usually what you actually want.
with open(path, "w") as f:
f.write("hello\n") # write() does NOT add a newline for you -- add \n yourself
f.write("world\n")
Unlike print(), f.write() writes exactly the string you give it — no automatic newline. If you want lines separated, you write the \n characters yourself.
def shout_file(in_path, out_path):
with open(in_path) as f:
content = f.read()
with open(out_path, "w") as f:
f.write(content.upper())
Two separate with blocks — one to read the input file (and let it close), one to write the output file. This is the exact shape you'll use for the read-then-transform-then-write problems ahead: read everything you need first, close that file, then open the output file and write to it.
The problems ahead run in a sandboxed in-memory file system — files you "seed" for a test exist only for that test, and reading/writing paths like /tmp/data.txt works exactly like it would on a real filesystem, just without touching anything on your actual computer.