>> So in the last module,
we saw how we could
figure out what is the current working directory,
how we can get a list of the files if we're taking a directory,
check if a file exists, and so on,
which are great things to do before
you start reading and writing to files,
make sure everything is the way it should be,
or to get a list of all the files
you might need to open, for example.
But how do I actually read and write to specific files?
Well, we have a few different ways we can do that.
Most of the time, we use something called a file stream.
So if you want to open up a file,
you create a stream object,
and you specify the name of the file you want to open,
the mode, and the buffer size.
Now, most of the time,
we leave the buffer size to the default.
We don't worry about that too much.
The mode though can be quite important.
By default, when you open a file,
Python assumes you want to read the file
because that is the most common scenario.
But you can specify that you want to
write to the file or append to the file.
The difference between the W for truncate and write,
and A for append is simply,
let's say someone has passed you a file and you want
to add additional lines to the existing file,
you would use A to add
more lines without overwriting what's already there,
whereas the W will actually
overwrite the existing contents of the file.
So make sure you know which of those you want to use.
X actually allows you to say,
I want to write to the file,
but this should be a new file I'm creating.
So if that file already exists,
I actually want to get an error back.
So this is another one that can be useful.
You really have to think a bit about
your business logic if you're creating a file,
and then when you're done with that file,
some other program is supposed to
copy it and move it somewhere else.
Then you go to run your program again,
maybe you're overwriting a file but it hasn't been copied yet.
So sometimes we actually want to get
an error if that file already exists.
The plus indicates you're doing an updating,
and you can combine these as well. You'll see.
Depending on the type of data you're going
to be reading and writing to the file,
you can choose that if it is a text file,
so basic strings and so on,
versus a binary file,
which could be things like images.
Most common scenario you're going to encounter
is you're going to want to read a file.
So if I create a stream,
I just say open demo.txt.
By default, it's a text file.
By default, it assumes I'm going in read mode.
There's a few things we can do.
One, we can just check and see if it is readable at
all because every time you do file IO,
sometimes weird things happen and you're never
sure is it because maybe I
don't have permissions to read the file?
Or is it because there are some weird characters
in the file that will mess me up or I can't find the file?
So simple little things like, hey,
just check the stream is readable,
and then you at least know, I have the permissions,
I can read the stream.
Now I can focus on reading it.
So it just helps you debug what's
going wrong and reduces the chance of errors.
You can specify a specific number of characters you want to read.
So I could literally say, read one,
which will just read the very first character in the stream,
and readline will really until the end of the line.
Close simply closes a string.
So if I had a lorem ipsum file,
this would simply say, true,
yes, the file is readable.
Then when it reads the first character,
it reads the L. Here's something I want to really highlight,
is you'll notice that when I readline,
it continues from where the stream left off.
So once it read the first character,
think of a little pointer moving over
by one after it's read that first character.
So when I do a readline,
it reads from that point onward.
So it does not reread the L when you
do the readline because I read the first character separately.
Now, what if we want to write to a file?
Well, first of all, we're going to have to specify the mode.
I'm saying I want a W for writing
and T because I'm writing text to the file,
because of the default is reading.
So if I want to write, I have to specify it.
We can use the write method to pass
in a single string or one or more characters,
and I can use writelines to pass in multiple strings.
So now, it's really important to be aware here.
When you write the H, imagine the cursor now blinking
right after that H. So when you write ello,
it literally puts it right after the letter H,
and then when you say write the space,
it now puts in a space,
and then when you pass in the string world,
it puts the word world.
It does not put new lines after any of these.
If you want a new line,
you have to tell it to write a new line.
Now, what's really cool about writelines
is I can give it a number of
different string values to write or I can give it
an object that contains maybe a list of strings.
So one of the neat things that I can do is I can actually
pass in two names in a list.
So I can create an object,
which is a list of strings,
and I can write those names out to it,
and it'll actually read Susan and Christopher,
and write the name Susan and Christopher to the file as well.
So this is a really wonderful trick
if you've got a list of objects.
You don't have to write complicated loops
to go through that list of names and write each one.
You can actually pass the list of names directly to writelines.
Then you'd simply use close to say, I'm done,
close the stream, flush out the data,
and our file is ready to go.
Now, there's a couple of
things I just want to point out when you're working
with streams that might not be obvious
and it can be a little confusing when
you're working with this the first time.
You're not actually writing to the file,
you're writing to a file stream,
and that stream goes to the file.
So let's say I open up a file,
and I say I want to open up to write,
and it's a text file, and I write demo.
I'm writing that to this stream.
You can actually use the seek command
to reposition where you are in the stream.
So imagine moving the cursor
around for where things are being written.
So if I say seek zero,
that will literally put the cursor
right back at the beginning of a stream.
So if I do another write,
it's going to overwrite D-E-M-O with C-O-O-L.
But the fifth character still has
an exclamation mark on it
because I haven't overwritten the fifth character.
Now, the flush command.
Most of the time, you aren't going to need to use
the flush command because what the flush command is
going to do for you is it actually flushes the data out
of the stream to the file.
Now, what that means is if you do
a flush and there's another piece of code out there somewhere,
or somebody out there now opens up the file,
they're going to see the changes you've made.
So they're going to see the word cool in that file.
But it's not actually necessarily saved to disk yet.
It just means if somebody else opens up
the file right now in memory,
then those values you wrote can be read inside the file.
When you do the close,
that will actually flush anything you
haven't already flushed out and close the stream,
and now it's actually saved permanently to the file.
So a lot of times, you can just close it.
You don't have to call flush explicitly,
but just wanted to put that out there because it's
a very common point of confusion for a lot of new developers,
is what the heck does flush do as opposed to what does close do.
So flush simply takes what's in the file stream,
writes it to the file,
but it's not saved to disk yet.
It just means it's written to the file,
the operating system decides when to save it to disk.
So it's something we typically use just because the file stream is
getting big and we want to write that to the file,
or because we have some other code
somewhere and we want to make sure anybody
else who might be opening that file can see what we've written,
even though we might still be working on it.
So that's what flush will do for you.
I just want to highlight the neat aspect of using seek,
is you can actually go back and overwrite things.
So when I do seek, I move back to the beginning of the file.
So the word D-E-M-O, demo,
got overwritten by C-O-O-L,
but you'll still see the exclamation mark at the end of it.
Now let's go try that in some code.