 Hey, everybody, it's Brian, and in this video, we're going to continue our journey into the file system by learning how to read and write text files. To do this, we're going to use Q-File, which is a class that provides an interface for reading and writing files, and Q-TextStream, which is a class that provides a convenient interface for reading and writing text. Notice key word there, text. We're talking about human readable text here. So by default, this bad boy uses UTF-8, but it can auto-detect UTF-16 and 32. Let's dive in and take a look. Let's start with the basics here. So the first thing we need to do is we need to include the things we want to work with, in this case, Q-File and Q-TextStream. Remember, if you ever need the documentation, you simply highlight it, hit F1 on the keyboard, and it brings up the help file. The first confusing bit is we need to learn how to work with a file. Q-File inherits Q-File device, which inherits QIO device. QIO device is the base interface for all IO devices in Q. Really what you should take away from that is if you're working with any kind of hardware, 99% of the time in Q, it's going to be a QIO device at its heart. We want to work with the basics. We just want to learn how to work with a file. So I'm going to make a simple function called, well, basics. Why not? Let's live dangerously here. And from here, I'm going to say we want a Q-File, let's call it File, and we need a File Name. I'm just going to say Test.txt. Now if you omit the path, it's going to be in the application's current directory. However, definitely you can feel free to put it wherever you want. Just name the path. On Windows, of course, it would be something like whatever drive you want. Now that being said, what I want to talk to you about real quick is pathing. If you do something like if you're on Windows and you do this, you may have a bad time. So just note that Qt will automatically switch the path the way it needs to be. Just do the other slash and you're just fine. So what we want to do now is try to open that device. Think of a device as like a room in your house, but the door shut. So you got to open the door, but you got to use a special key to get into that door. So I'm going to say if not file.open. So we're going to try and open that door now. And this is where people instantly get confused. They see this open mode flags. What is this? Well, there's a couple of different flags out there. So for example, I can say QIO device and we want open mode flag. And then here are some of the flags. There are a lot of them, a lot more than you're probably going to use. But let's cover the major ones. So append will open the file, go all the way to the very end and allow you to add data to it. Notice you're writing to the file. Then you have read only meaning you're going to open the file, but you cannot change it. You have read write, which means you can open the file read and write to it. And then you have write only meaning of course you cannot read it. You can only write it. Very confusing, but a lot of this goes down to legacy operating systems and just simply the way computers work under the hood. For this example, I'm going to do read write meaning we're going to be able to both read and write. We can omit this open mode flag and just shorten it like that. It's actually very cool the way that works. Now the reason why we're checking to see if we can open it or not is because let's just face it on computers. Bad things tend to happen. So sometimes the hardware is not very friendly. This copy and paste isn't very friendly. So we're saying if we cannot open it, we want to know why and we're going to say we cannot open the file and then we want to get that error string from the file. Very cool how that works. We can just get a human readable description of what the heck just happened. From there, if we are able to successfully open the file, we can work with it. So I'm going to say writing the file. And this is where I'm going to say file dot write and you'll notice when you're working with Q file, it's very, very basic because you're not really most of the time meant to work with the file directly. You should work with the stream, which is this video, we're going to cover a Q text stream. And the next one, we're going to cover a Q data stream where you work with data instead of human readable text. Anyways, we're going to say write and you notice we got some options here. I almost always use Q byte array. Feel free to work with whatever you need to though. So we're going to say Q byte array, hello world. Now normally we wouldn't do what's called a flush, but understand what's happening here. We're writing out to some device that device is going to go at some unknown speed depending on what the device is. We as a programmer want to ensure that all of that information is written down to the disk. So I'm going to say file dot a lush. Normally you wouldn't really need to do this because you would just write and then close and when you close it automatically flushes for you. We'll get to that in a minute. But what flush does is it pushes the data to the disk or to the device more appropriately just to ensure that it gets there. Think of it as morbid as it sounds a lot like flushing a toilet. We're flushing it down to the desk. All right. Now let's go ahead and read the file. Notice we still have the file open. We haven't closed it. So that door is still wide open. And we need to know where in this room or in this file we want to read from. So I'm going to say file dot seek. Think of this like you're going through a child's room looking for their favorite toy that they were screaming because they lost. You need to go find it. So we're going to go to zero, which is the very beginning of the file. So basically we're saying go all the way back to the end. So think of it this way. If you have a room, you filled that room with boxes. Now you want to go to the very other end of the room, the very beginning of where you put the very first box. And we're just going to simply say queue info file dot. And we want to go with read all. Once we're done, of course, we're just going to simply close that. So think of it a lot like a house. You're leaving your house. You don't leave the door wide open. At least I hope you don't. You want to close that door. Really what this does is a few things. It will call flush under the hood to make sure anything we've written is flushed down to the device, and it ensures that those resources are released and sent back to the operating system. And the operating system knows that this file is not open by the application. All right, let's go ahead and grab this. And let's plop it right in here. Save and run. And let's see this in action. All right, so we're writing the file reading the file and hello world. There's our file in all of its glory. Let's actually go out to the hard drive. And let's check here. All right, so there is my little project here. We should have a text file in here called test. There it is. And if we open it up, hello world. Let's go ahead and learn how to write a file using QtecStream. So I've already got a function all ready to go. It's going to return a bool. And it takes a file name. Very simple. Once you know the pattern, which we just covered in the last section, it's very easy to work with. And literally just copy and paste huge fan of copy and pasting and then modify it to our heart's desire. Now we are going to change the mode. I'm going to say I want to write only. Why? Because that's what this function's doing. I don't want to inadvertently try to read from the file while I'm in the process of creating it. If for whatever reason I cannot open the file in write only, then I want to return false. So we know this did not work from here. What I almost always do is I make sure I close that file before I even do anything else just so I can ensure that resource was closed. Now I'm going to say it. It will close itself when it actually goes out of scope. But I always close it just to be sure. All right. You'd be surprised how much hate mail I get about using file close. People like you don't really need to do that and I just like to do it just so I know what I've done. From here, if I've gotten to this point, I'm going to return true. All right. So if we click on this, it says calls flush. What does that mean? We talked about that in the previous section. It's going to ensure that anything that we wrote is immediately flushed down to that disk. We want to make sure that everything we write is actually written and that's why it's baked right in there. And that's what I meant by the last section. We normally don't need to call flush. Just simply closing the file is enough to do it. From here, it's very, very simple. We're going to say queue text stream. Let's call this stream and we need to give it an address to our file. That's it. It's really that simple. Now you may be asking yourself, why do you even need to work with a text stream? Well, because in the basic example, we just wrote raw bytes out to the disk. It didn't take into account language differences and things like that where queue text stream will by default use UTF-8. You can use 16 and 32 as well. It's actually very, very flexible and very easy to work with as we're about to demonstrate. So I'm going to say for int i equals zero, i is less than five, but increment i. And I want to just write out to that stream. So what this is going to do is as we're giving the stream information, it's going to do any sort of formatting and encoding that it needs to do and we don't have to worry about the details. Let's just give it the queue string representation of i and then let's actually pump out some text here. So I'm say, hello world and we can do a carriage return line feed so you can actually do escapes. Or if you wanted to, you could say like QT and L and then give it your text stream. It'll do basically the same thing on a cross-platform manner. All right, it's really that simple. So if we go ahead and run this, we don't see anything on the screen because we're not reading it yet, but we can go out to the hard drive and drumroll 01234 hello world. Now that we've written a file, let's go ahead and read it back. So here's our file 01234 with hello world on each line, we're just going to read that bad boy back and see what it looks like here. So again, because we already know the pattern, we can actually just grab the bulk of this, do the old copy and paste and plop it in there. We've got our file. Let's go ahead and switch this over to a read only. Now someone out there is going to ask, why don't you do read write on every single thing? Well, it's poor form. A function should be self descriptive, meaning you don't want to write a file when you're reading it, so on and so forth. And also some older operating systems would incur some sort of speed penalty for reading and writing a file at the same time. Most modern operating systems, you're not going to see that speed penalty. So to some degree, yes, it is a little bit legacy. All right, so we've got our file, we're going to open it. If we can't open it, then we're going to have some sort of error string. Now I like to add an extra step here. So I'm going to just grab this and say, if not file dot exists, meaning I want to know that I misspell exists. What's going on here? There we go. Gosh, wasn't with it for a minute there. So if the file doesn't exist, I want to know, hey, the file simply doesn't exist. I'm sure you've seen that error message before file not found. Notice I'm using a q critical. So if we have any sort of logging, it's going to log it as a critical issue. So we're doing double checks here, we're checking to see if it exists. And then we're trying to open it. If we can't open it, then we got some sort of error string. Now of course we have our q tech stream, which has got our address of our file. But instead of writing, I'm going to blow this away. We want to read. Now remember the structure of our file is we've written multiple lines. So we can do, if we wanted to, something like this, q string data equal dream dot read all. But you notice there's a read up to a maximum length, a read all and a read line and then read line into. So they're very different functions. So read will read up to a number of bytes. Read all will read the entire thing. Read line will read up to a new line. It reads up to a new line, but puts it into a string. Very confusing. But if you do read all, it's exactly what it sounds like. It's going to read that entire file. So if this file is massive, you're now going to consume a lot of memory. So one of the questions we're often asked is, how do you read a file line by line? It's very, very simple. I'm just going to say while, not stream dot at end, very self descriptive here. While the stream is not at the end, then we're going to do something here. And all we're going to do is say q string, call this line equal stream read line. And notice you can also do up to a maximum length just in case you have some obnoxiously huge line. And then do something with that. It's actually very, very simple. And q tech stream does all the decoding for you. So you don't have to worry about how to figure it out. I've already got the function in there. Let's go ahead and give it a whirl and see what happens here. So we're going to write and then we're going to read and everything works perfectly. And because we added that little extra step, if we misspelled the file name, we get that added little message file not found. I hope you enjoyed this video. You can find the source code out on github.com. If you need additional help, myself and thousands of other developers are hanging out in the Voidrealms Facebook group. This is a large group with lots of developers and we talk about everything technology related, not just the technology that you just watched. And if you want official training, I do develop courses out on udemy.com. This is official classroom style training. If you go out there and the course you're looking for is just simply not there, drop me a note. I'm either working on it or I will actually develop it. I will put a link down below for all three of those. And as always, help me help you smash that like and subscribe button. The more popular these videos become, the more I'll create and publish out on YouTube. Thank you for watching.