The course is part of this learning path
This course covers file input/output (file I/O). Being able to read from and write to secondary storage is important because often we want to keep some data persistently. By the end of this course, you will have the skills necessary to do this.
- Understand the fundamentals of both file input and file output and how to use both in tandem
- Use stream manipulators to make the structure of the data more organized
- Use dynamic memory, pointers, and classes to make more complex and interesting applications
- Beginner coders, new to C++
- Developers looking to upskill by adding C++ to their CV
- College students and anyone studying C++
To get the most out of this course, you should have a basic understanding of the fundamentals of C++.
In this lecture, we will begin our discussion of File I/O which stands for file input/output, which involves the techniques we use to maintain data persistently. This lecture will focus on file output which involves writing data from main memory to some form of secondary storage device. To ensure we better understand what our motivation is for using File I/O, we need to review some concepts. As has been mentioned throughout the course, variables and constants live in memory which is also called main memory. It is also called RAM or Random Access Memory. This is because bytes throughout the memory can be read and written to without sequentially going through every byte to get to the byte the program wants. So, similar to our notion of arrays which support random access, typically using indices, the system does a little math and can jump to the correct area in memory as needed. So, the advantage of main memory is that it is very fast and the CPU can read and write data in main memory very quickly and fluidly. What's the disadvantage then? Well, main memory is volatile which means that when your device loses power, the data and instructions in main memory are lost. So, is there any way to save the data long-term? The answer is yes. We can store data long-term in some form of secondary storage. Generally the disadvantage to these is that they are slower than main memory. Many of them have moving mechanical parts and even though they can move thousands of times per minute, this is terribly slow compared to what happens in main memory and the processor. Even with solid state technology which generally has no moving parts, it is still typically slower than main memory. So, our variables and constants and literals live in main memory. If we want to keep data long-term, we have to store it in a secondary storage device. So, how do we do that. For a relatively simple and common way to store data, we can use files. So, reading from files known as file input and writing to files known as file output are both important skills to have to make sure your programs can work with data long-term. We will begin with a focus on file output or writing to file. Let's create a project called FileOutputFun. So, create new project, empty project, FileOutputFun. And we will also provide a main file with a skeleton. I'll write main.cpp. So, we're going to need iostream so we can interact with the console. But we also need this fstream for file stream library using namespace std, int main(). All right. So, output file stream, ofstream, outfile is what we'll call that and we're going to say, hey we're writing to file, we're just going to say this to the console, rrint this to the console and then we have bookends where these outfiles and even where infiles are concerned. When we're writing to or reading from files, we have these bookends open and close. So, output.txt is the file name I'm going to use and then we're going to have at some point outfile.close to say that we're no longer interested in writing to the file at this point. So, what are we going to write to file? Outfile, it seems appropriate. We started this way with the iostream, the cout, so you'll notice that the syntax is extremely similar. We have the stream insertion operator used just like it is with cout, right? So, the two less than signs, we use the same exact type of formatting with our outfile here except now, instead of going to standard output, we are telling it we want it to write it to a file namely this one. All right. So, we also need to put maybe just show that our program is done here, we will put done at the end, and of course let's run it. Debug, start without debugging. All right. Writing to file and done. Awesome. All right, so we need to go find the file that was output because obviously this didn't show on the console at all. There's no indication that any of this got done, so we need to find, well, where did this file go? Well, in Visual Studio or with Visual Studio, we have... Hit go into the project directory. So, what I'm going to do is I'm going to go under the C Drive, users and then where my profJ is, source, repos, but it's wherever you saved your projects and then you go to the specific project like FileOutputFun right here and then here, this is actually called the solution directory even though we generically called the thing a project in Visual Studio. A lot of times it's actually a solution because it could have more than one project. Most of the time or a lot of the time, you'll be dealing with just one project at a time but for really complicated solutions that might involve multiple projects, they can all live inside of the same solution. For our purposes, it's 1-1 though. All right, so if we open up this project directory, you will notice that there is a text file here. Open it in Notepad or whatever and we have "Hello world!" Excellent. So, we have a file that's been written to in the project directory with our simple string 'Hello world" printed into it. So, let's make this a little more interesting by printing data to file in a formatted manner using iomanip and some other features from the iostream library which worked with cout as well as our output file stream. So, we're going to be using things called stream manipulators. One is going to be called set w. That will set the field width, that is, how many characters the field will occupy. We will also use the sticky manipulator set precision to set the precision of floating point numbers. These are numbers with decimal places of course, to set the number of digits to the right of the decimal point. Some manipulators like set precision are called sticky as I mentioned because we set them for the output stream and don't need to use them again whenever we print anything. In other words they stick around in the output stream. We will also use the sticky manipulators fixed and show point from the iostream library as well. The fixed stream manipulator is used in tandem with stream manipulators set precision to force any of the floating point values to display the given number of digits, indicated by set precision stream manipulator. Let's update our code and see what some of this output will look like. We'll use our manipulators for console output as well as file output to show that they work for both streams. So, I need to, I'll minimize this because I plan on going back to it. Let's include iomanip for input output manipulators, it works for iostream and fstream and we've got our fstream here, let's see what else we want to do. Okay, so we're opening the file, we need to do this. So, here's one thing before we do any of the actual printing. We'll say outfile fixed and showpoint, and then cout fixed showpoint Excellent. And then we can remove our 'Hello world" here because that's a little too simple for us right now. We got a taste of it, but we really want this to be more complicated. So, more interesting really. So, i = 1; i <= 10; i++. That is our for loop. outfile, now set w to 12. We'll set the field width of the next thing. We will set the precision to 2 on this one and then we'll say i *, just to get a variety of numbers maybe 5.7575. All right. So, I'll take whatever the value of i is 1, 2, 3, 4, 5, 6, 7 etc. multiplied by that. And this will also prove that our set precision is working and set width is working. Set precision too, we'll say 3 for this one and i * 3.14159. You knew pie would make another appearance, right? Okay, now we'll do the same thing for cout. So, I want to do it to the console as well so I'm just copying this whole thing, I'm going to paste it and just really just change this to cout. It still uses the stream insertion operator so everything should be good with that. We've got done and we've got the close and of course we need to run it just to make sure it works. All right, and this is what we get to console which is pretty organized, right? So, we got 2,3,4,5,6,7,8,9,10,11,12. So, you'll notice that by default right to justify within a field with of 12. If we start right after that 1,2,3,4,5,6,7,8,9,10. 1,2,3,4,6,7,8,9,10,11,12, there we go, counted it right that time. So, that's also a field width of 12. You'll notice that these have a precision of 3. So, even if there is no third digit, we forced it to show all three and then over here it's 2 because that's what we got here. Let's make sure it did the same thing to the file. So, output.txt and low and behold, we have the numbers that we were expecting with 2 and 3 digit formatting and also the field width of 12. Nice work everyone. Before moving on though, I would like to issue you a challenge. So, using the same FileOutputFun, I want you to refactor the code so that the loop will iterate up to a high value entered by the user whom you will prompt for the number inside main. So, Instead of the hard coded 10 that we have, you will use the integer that the user enters but don't just leave the loop in main, I'd like you to break it out into a function named printFormatted which will need to take two parameters, a reference to an ofstream stream, ofstream, right? That we called outfile, that will be the one you actually pass in but it could be any output file stream as well as the high value for the loop iteration. Also don't forget to call your printFormatted function in main. So, you're cleaning it up a bit and also adding some flexibility. So, pause the video and give this your best shot. Come back when you're done or if you need some help. How'd that go for you? Were you able to finish it? Let's do it together. All right, so we'll create the printFormatted, int highNum. At the function prototype, I'm going to copy this guy, put it down here, give it a body. We do need to prompt the user so we can get ready with that. Up here I'll say, int highNum and then maybe before writing the file, we'll set this up. We'll say, "Enter the high number", we don't want that, endl. Good, I was just writing to file and gets ready to write everything. We're going to kind of fix this a little bit, we're going to break out this loop and we're going to move it down here and we're going to do some fixing. So, after the fixed and showpoint, we're going to put printFormatted, pass it in outfile so it knows what it's printing to and also the highNum. But we need to do a little bit of manipulation down here. So, I need to change this to highNum instead of 10 and let's make sure everything else looks good. I think it's good, I think we're good. So, let's run this and it says enter the high number. Now, if we put 5, you'll notice there's only 5 and let's look at the file briefly. Again, five items each. And let's run this again. And let's try maybe 20. There we go. Very good. Nice and formatted, nice and printed out the way we expect to the file, same values, and of course, we will run it again. Maybe we'll do a really big number or a much bigger number like 50. Good, nice, looking pretty good, and I accidentally left the folder. So, let's go back there. Users, source, repos and FileOutputFun. Okay, there we go. Good. My finger gets a little trigger happy and I close these windows a little bit too quickly sometimes. All right, good. So, that looks pretty good. Looks like it matches up with what's over here, it's just in a file. So, the cool thing is power goes off, you turn off your computer, come back, this file is still going to be here. Barring any EM radiation or explosions or damage to your disk but that's why it's called persistent because we can keep it. I could store it and load it later into a program, our same program or a different program, and it's really the centerpiece to how programs work as far as persisting their data. So, great job. This challenge combines some of the earlier material with functions passed by reference and other topics with our current discussion of file output, as well as some formatting techniques. In the next lecture, we will discuss the opposite direction of file data transfer from what we discussed in this lecture, that is, we'll be discussing file input. Let's keep going.
John has a Ph.D. in Computer Science and is a professional software engineer and consultant, as well as a computer science university professor and department chair.