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 the last project, you worked with reading data from file about rectangles, creating those rectangles as needed, that is, dynamically and then printing their areas and perimeters to file. For this project, you will read a file full of items. There is one item per line, all items are lowercase strings. You should keep track of the frequency of these items, that is, how many times each item occurs in the file. It is possible to do this by only going through the file once. The file shopping.txt will be provided for you as a resource for this lecture. When you're done, simply print the item name and the corresponding frequency to the console. I will leave the design of the solution up to you for the most part since you are now reasonably experienced with C++ if you've made it this far. This will be a great challenge for you to practice not only C++ syntax, but your own ability to solve reasonably sophisticated problems. You've seen some of the problem solving techniques that I've used and have done some practice on your own. So, I think you can handle this. Just take your time and do your best. I will give you a hint as to how I solved it. I created a class named item frequencies that holds parallel vectors, one containing the item name and the other containing the frequencies. I add new items to each vector with a frequency count of one to start with. When I come across an item that's already in the vectors, I increment the corresponding frequency for that item. The class also provides printing functionalities, but again, that's my design yours can be different. All right. Let's take a look at the shopping project, so you can see how it should look when it works. If I go to debug, start without debugging, you should see it says, the number of each item next to this. I'm using the iomanip and some stream manipulators to make this more organized but you really don't have to make it this pretty if you don't want to just as long as it works and you see each item listed once with the corresponding frequency. So, this means coke occurred in the input file 12 times, ham occurred nine, et cetera. So, hopefully, that was useful. So, pause the video, and create the shopping project, solve the project and come back when you're done or if you need some help. How did this interesting project go for you? I know it was a pretty solid challenge, so let's work on this together. So, let's create the shopping project. Create New project and we will put Shopping and we will also add the item frequencies. So, Add new item to header, we'll call it itemFrequencies, that's good, itemFrequencies and also the source file, we need two. We need the itemFrequencies.cpp, itemFrequencies.cpp, make sure it's spelled right, and we also need main.cpp. Okay. And also, it would behoove us to have the input file because we need the data. So, I conveniently have the repo and also the data that was downloaded as the resources, so we have shopping.txt. If I go to the Shopping project right here, and go into the project folder, I can copy over the shopping.txt and you can look in shopping.txt and just see that there's coke and ham and salad and ham occurs multiple times. So, every time it occurs you're going to be incrementally it once. So, looking at the data before you start is a good idea. So, if you want to take another shot at it, you can pause the video again and then come back again. If that didn't make sense to you the first time around or you didn't think to do that. It's very important to know what the format of your file is or the data that you're working with. Okay. Let's minimize this and let's do some work here. So, we'll at least set up the main file iostream, we need, of course, fstream and we're going to include itemFrequencies.h namespace std, and we'll just leave this entirely, there we go. That's pretty good. Now, itemFrequencies.h, go over there if indef ITEM FREQUENCIES H, if not defined then define it and endf. All right. Excellent. Now, inside here we're going to need the string class and also vectors class itemFrequencies. We have a public section and, of course, a private section and I'm going to tap those in. We don't really need a custom constructor for this, so I'm just going to put an add item and printFrequencies so it's going to be const. Again, this is just my implementation, you don't have to make yours exactly the same or even really that similar. I would say, it's a pretty decent implementation. Now, here's something different that I would probably guess you didn't do. This right here is actually a method but it's in the private section because I don't want the outside world to really do anything with index of item to know indices of a particular item. This is just what I would call a helper method to help me find if the item exists or find what a corresponding index is if the item does exist. And if it doesn't exist it will return a number to let me know that as well but I have to implement it just like I would any other. So, inside the itemFrequencies.h, over here, we also need iostream, it doesn't really matter what order we put these in, I guess, iomanip if I want to make it nice and organized. Again, this was my decision to do that. We do need addItem and printFrequencies over here and we also need to grab this private method. Even though it's private, it's still a member function or method and we still need to implement it. All right, give them bodies and, of course, itemFrequencies with the scope resolution operator. I'm going to copy that, paste and paste. Excellent. All right, so now for add item, printFrequencies and index of item. So, for print frequencies, printFrequencies is pretty simple, so we can do that one first. We'll just say index i=0, and then the less than the item.size i++ and then we're just going to print out because this is a console. We'll just do setw items[i] and then setw frequencies[i] endl and we can, kind of, put that on the next line to make it a little bit more pretty there. That one's pretty straightforward. Now, index of an item is going to be kind of interesting. That one's going to help me with the addItem. So, I'm going to assume that we don't find it and the indicator that we don't find the item is that the index is -1 because that's not a valid index but it will give me the ability to determine if we have a valid or invalid index or not. Remember, this is a helper method, this is actually a private method. So, forint i = i less than items.size and then i++ if the item that they passed in we're looking for is equal to items at the index I'm on as I iterate over this vector, resultIndex = i, then we can break out of the loop. So, if I found a valid item that matches the one I'm looking for, that means it already exists. If I go through the whole vector, whatever it contains thus far, and I never change the index, that means it will be -1. So, that'll be a signal that, appears sorry that should be items, but if it's -1 when this index of item returns and that means that that's a new item because we haven't found it, it's not in the vector yet. Excellent. So, I can make use of these inside of add item. In add item, I'll just literally take index of item and pass the item to it and then I'll use this; if (index != -1), then that means item already exists and we have to do something about it. And then, if it doesn't equal -1, that means it's a, sorry, if it does equal -1 that means it's a new item. Okay? So, we handle these cases differently. If (index != -1), no that's dumb, that should be an integer. So, if (index != -1) that means it's a valid index, so we found it based on the name but we can find the corresponding frequency and then increment it because we have the vector of frequencies. And if I have a particular item name at index two the corresponding frequency for that item will be at the other vector at frequencies at two, that makes sense. So, with a new item, items.push_back(item) so that will add this new string and then you want it to have the same index in the other array we just push back a one because that's the count. It's the first time it's been encountered so it's a count of one and if it never goes above that, that's fine. All right, so that's pretty cool. Let's see what we can do with main. So, inside main here we have a couple of things we need to do. So, we've got the itemFrequencies iostream and, we probably want to include string. Do I review these? Look at item again just make sure I got everything. ItemFrequencies, itemFrequencies with a lowercase i this time, if stream infile string tempName infile.open ("shopping.txt") and, of course, we have infile.close as the bookend. All right, good. We want to test the validity of the file, so if it is not valid we say, "Error opening file.... bailing out ...." or something to that effect and then if we get past that point we know it's a valid file or at least we can open it. Well not in infile.eof, we will take the string in as a tempName and then itemFrequencies.add itemFrequent, that's why cies.addItem, good, tempName and we know how that addItem functions. If it's brand new, it will add it with a count of one, if it already exists like we have over here, it finds the corresponding frequency internally and increments it. All right. So, of course, after we're done with the while loop before the close, we want itemFrequencies.printFrequencies and you'll notice this keeps main relatively clean. I could have even broken up some of this and do a function inside of main to make it even cleaner, but this is pretty clean, this is pretty nice. The class takes care of a lot of what we need, so let's see what we get. So, let's run this, see what we get. Okay. And it does count the items. So, coke occurred 12 times, ham occurred 9 times, salad, 5, honey, 3 et cetera, et cetera, pasta, 6, so this has all the different items, lists them only once here and then their entire frequency over here. You can verify that it does match the file, make your own file. Just make sure it's the same thing with the, all lowercase because the way we have it implemented now uppercase and lowercase coke would be different items. Okay, so you have to keep that in mind. You can always use the lower and upper methods with strings as well. So, and you can do some like case insensitive comparisons and things like that if you wanted to make it a little more flexible. All right, nice work everyone. So, even if you had some significant trouble with this project, it's a good experience to work with less of the instructions filled in for you. That makes the projects more realistic in some ways, because much of the times you have to take a set of requirements written in plain English or another natural language and converted into some sort of design. And then, from that design you construct an application out of it. I gave some hints about how I would accomplish this, but I also let you figure out a lot on your own. So, don't at all expect that yours would look exactly like mine. I guarantee that even professional developers taking the course will have differences with their code because I left a lot of the options up to you for this. So, you had to decide what to do with it. Coming up next is the section wrap up. I'll meet you over there.
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.