 Hey everybody, it's Brian and in this video we're going to talk about the Q byte array or basically an array of bytes. I like to nickname this the best array ever. And there's a reason for that. I absolutely hate working with C++ arrays. I hate them with a passion. Yes, they're great. They're convenient. And for newbies, these seem like you're just in God mode. But really, once you get into more advanced programming, you constantly find yourself having to resize these and do things with them and it just becomes a royal pain. So let's dive in and take a look and see why I love the Q byte array. All right. So first thing we need to do is, well, start at the beginning, we need to actually create an array. So let's go ahead and do that. I'm going to say Q byte array, let's call this stuff. Now you may be going, no, wait a minute, you never included it. It's already built right in so you don't need to, it's right in Q core. Now before we really jump in, just remember, highlight it and hit F1 that brings up the help file. The help file is your friend. If you have questions or concerns or if you get lost and kind of scrolling through this, you can see right off the bat, append, that's right. You can modify this array dynamically. This is pretty awesome. Now there's other things out there in C++ land that do this, but I don't think they do it quite this well, which is why I get so super psyched up about this. I'm going to say Q info, let's go ahead and output our stuff. So we just have a blank byte array and we're just going to output it. The first thing you're going to know when we output this is that Q info treats this like it's a string. That's important for later on in this video when we talk about encoding. That's right. This class has encoding built right into it. I absolutely love that. So let's dive into some of the other constructors and you can, as you might imagine, just take this to some sort of extreme, but I'm just going to say data. And you see this little yellow box that pops up here? I'm just assuming you're still new to Q. Those little arrows up and down, you can actually use your arrow keys on the keyboard or if you're good with a mouse, you can kind of click these. And you can see there's seven different constructors and you can just pick and choose which one you want. It's just ridiculously awesome. So I'm going to say, hello, I'm just going to give it an array of characters there. Go ahead and grab this, all this data, save run. And again, this is going to display as if it's a string. Now this is a little bit confusing. I almost wish like they had a special character like a lowercase B or something in front of it. So you could see that's a byte array, but it's going to give you the string representation in the Q info. From here, let's go ahead and say Q byte array. And I want to do a buffer. This is pretty typical of some C++ programs where you need like some initialization buffer or something. So I want a size of 10 and I need a starting character. And let's say for whatever reason, they want a tab. There's multiple ways to do this, but I'm going to use an escape sequence. So slash T for tab. And then you guessed it. We can just Q info this out and see exactly what it's going to look like. Oops, I hit find instead of run. Forgive me there. In case you're wondering, find is just control F. But you can see now we have this buffer filled with tabs. There's an easier way of doing this. It's called the fill method, which we're going to talk about later. But I just wanted to show you can do that right in the constructor. It's just ridiculously simple. All right, go away, find. We don't need to find anything. I know what I'm doing sometimes. All right, so Q byte array. Let's say person. And this is something that I get asked quite a bit. So I wanted to do this even though this isn't really a normal constructor. How do you turn a Q string into a Q byte array? Well, it's very simple. You could just say Q string and let's say we had one floating out there, but I'm just going to construct it right here. And let's say Brian. And now Q string you see is just not compatible. It's not baked right into the constructor. So we have to tell Q string how to do this. So we say to and then you have some options. For example, to Latin one, to local 8 bit, to UTF-8, to whatever you want. Basically follow the help file again. Highlight and F1 brings it right up to what you're trying to do here. And it tells you what you're trying to do and some of the impacts of it. You may want to UTF-8. I'm just going to use to local 8 bit, but really at the end of the day, it just works. I absolutely love this. So let's go ahead and grab this and let's output the person. Save and run. Tada, it all just works. Regardless of which constructor you use, it's very, very simple. All right, let's talk about sizing the array. And this often is a very lengthy, boring C++ conversation. There's better ways of doing it, but of course you got to understand C++ to really talk about it. But Q byte array, of course, makes it ridiculously simple. Before we do that, though, I'm going to just copy and paste a simple function that's going to help us to reduce some clutter out on the screen. It's just a function called stats and it's going to take a Q byte array and we're doing the address of. Now, special note, Q byte array is not a Q object. So we can copy this all we want, but I'm just avoiding a copy. And from there, we're going to print out the length and the capacity. Very important to understand the difference between length and capacity here. So let's highlight this, F1. Length is the same as size. Very helpful. Okay, now that we actually understand here, returns the number of bytes in this byte array. The last byte in the byte array position is the size minus one. Kind of important to remember that later on because we'll end up doing something where we have to add one to get the correct position. All right, so basically size and length are interchangeable. They're exact same thing. And the number of bytes in the array is really what it denotes. Now capacity, this is different. So I'm going to highlight that F1 returns the maximum number of bytes that can be stored in the byte array without forcing a reallocation. So we're talking about memory here. Reallocation means that we've gone past the capacity here and Qt in the background is going to make a new array and reallocate it for you. And that can be expensive in terms of memory and time. So if you're really on some time sensitive thing, you're going to want to watch the capacity or you're going to end up doing a forced reallocation. All right, that's important because I get a lot of people that are like, hey, I stored, you know, 35 gigs of information inside a Q byte array. Why is it taking forever to add more to it? And that's why you're forcing a reallocation. So back here, let's go ahead and say data. We're just going to reuse this little guy right here, our Q byte array. We're going to say data.reserve. Now reserve is going to do exactly what you think it is. So I'm going to say 25. And let's call that stats function on our data variable. Remember, all stats is going to do is print out the length and the capacity and then the actual data here. So let's say run capacity or I'm sorry, the length is five. The capacity is 25. Notice the difference there. So we've reserved 25. If we go beyond that, so if we just start adding things and we go past 25, it will forcefully reallocate in the background. Now, if you're on a beefy computer or even a virtual machine, like I got that reallocation for 25 is nothing. I mean, you're talking, it's barely even worth talking about. But anyways, if you've got tons and tons of data or you're on a low end device, like an embedded device, that reallocation can be very expensive. So keep those in mind. So reserve is basically saying go out and reserve that memory so you don't have to reallocate. Now comes the tricky part of the conversation. I'm going to say data.resize. And notice it's got kind of the same signature. So I'm going to resize this to 10. And let's go ahead and go stats, type, data. There we go. Save, run, and let's see what resize does to this. So now we're going to resize the length is 10, capacity is 25. Wait, what? What is going on here? You see what's happened is it's actually resized our array and filled in some characters there. And these are just text zero is basically what you're looking at. So it's hello, and then a bunch of zeros. OK, so let's dive in and see what the difference here is. So if I f1 on reserve, attempts to allocate memory for at least bytes, you notice the word attempts, this isn't always going to be successful. And it also says, if you know in advance how large by the array be, you can call this function. And if you call resize down here, you often likely get better performance because what it's going to do is it's going to plop that data in there and there's other code in the background, which makes us slightly faster. It says, note, while resize will grow the capacity if needed, it never shrinks the capacity. So you're never going to lose data with resize. You can use squeeze, which is something we're not really going to talk about, but this will release the memory that's not required. Just in case you're on a low end device, you need to know how to do the opposite of resize. You can just squeeze it. So that's really the difference. Reserve and resize, reserve is going to allocate memory. Resize will allocate and use, but it's got better performance because it does something better under the hood. 99% of the time, you're going to want resize. So let's talk about truncating the data. Data dot truncate. So this is kind of the opposite here. I'm going to say I want eight and we're just going to print this out. So what is truncate going to do in case you've never ever heard of truncate? Truncate's the byte array at the index position. If the position is beyond the end of the array, nothing happens and they have a nice little example here, how a stock home truncated five is going to be stock. So it's exactly what you think it's going to do. You're giving it a length and it's going to chop it off right at that length. Let's run this and see what truncate looks like. You notice how the length is eight, but the capacity is still 25. So it's removing those bytes, but it hasn't changed the capacity. Very important you understand the difference there because a lot of people go, well, I truncated it. Why didn't the memory usage change? And of course we can if we wanted to say data dot clear. Clear does, and this is why I love Qt so much. That's exactly what you think it would. All of these are self descriptive. So the length is zero and the capacity zero. Special note on clear is it does reduce the capacity. So a super fast way of releasing all that is just saying, go ahead and clear it and let Qt deal with all the nonsense in the background. Now comes the fun part, let's modify some data. And again, this makes it just super ridiculously simple. So I'm going to say data dot and we're going to go ahead and resize this just for clarity. I'm going to resize that to five and then I'm going to say data dot fill. Now this is the interesting bit here. Notice how it's taking a character and it has only one way of doing this, a character. And then an optional size. So I'm going to do a hex encoded. So I'm going to say slash X and then the number I'm going to do O2. In case you're wondering how I magically knew that, it's just standard C++ escape sequence. Feel free to look that up on Google. So slash X says we're going to use O2 hex encoded. Bang, very, very simple. Now let's go ahead and see what this looks like. So stats, let's go ahead and say data. I shouldn't have said hex encoded is O2 in hex, basically. Or character two. So now you see we have O2, O2, O2, O2, O2, O2, link this five capacity 16. Notice how it's automatically tried to figure out the capacity for us. And it's pretty much triple what we shoved in there. That is key to note that this is going to grow dynamically. So if you're really, really focused on memory usage, you may fight a little bit in the background with this. All right, now that being said, there are tips and tricks but they're more advanced than what we're talking about how to limit that memory usage. We're going to try and cover that later on in the series. So data, let's go talk about replacing. So I'm going to say replace. Now, everybody gets super excited about replace but this doesn't really work the way you think it would. I mean, maybe I'm just thick headed but I'm going to say, okay. So at the index zero, so the starting index and then the length, I want one. This is key right here. Pay attention to what we're doing, zero to one. So we're basically doing one character, comma, and then we want to replace it with something. And I'm going to say Q byte array and this is where everybody in the comments is going to get super mad at me. Let's say sweet. Now, pop quiz, what do you think this is going to do? What is this going to look like? We're going to add zero to one to a Q byte array of sweet. Well, this is more than one. So let's dive in and see how bad this gets here. And sure enough, this is what it looks like. Did not work the way you think it would. So what it's really done here is it's put it at the zero position. But if we count one, two, three, four versus one, two, three, four, five. So basically we've annihilated one of those bytes. That's why you gotta be a little bit careful with that length because we just overwrote that. That can get a little cumbersome. So let's try that again. And let's say we want to do 99 bytes. We know there isn't even 99 bytes in here. Notice how now it just says sweet and it hasn't changed the capacity to 99. So really what we're doing is we're saying at the starting position up until this length we could go ahead and wipe out those bytes with whatever you want. So be a little bit careful with using replace. Now that can get a little bit freaky, especially if you can't figure out why it's doing that and which is why I wanted to really warn you about that. All right, so let's go ahead and say data.fill and we're just going to fill this in with, why not, an asterisk. Now let's talk about insert. So I'm gonna say data.insert. And this does exactly what you think it would. So we're going to insert. You can kind of feel free to go through these things and find the one that works for you. Again, that doesn't have to be selected on the screen for you to use it. C++ we'll just magically know. But I'm gonna go ahead and say at the third position, remember this is a zero based index. I'm going to insert a cubite array and let's go ahead and add this and then run it. Now I'll let you in on a little secret. A lot of people that record videos with code use a program that automatically types the code. Drop a comment below and let me know if you want me to start doing that or if you'd like watching me fumble with my keyboard. So anyways, what we've done is we've filled this with asterisks and then at the third position, we've inserted Hello World. So you can see Hello World. We've inserted that successfully. Insert is great, but notice what it did. It changed both the length and the capacity. So we're now growing automatically. It's automatic resizing of our array. All right, that can be a little bit scary if you're in a memory kind of constrained environment, but these computers nowadays don't really care. We've got more memory than what we know what to do with. So let's go ahead and append. So I'm gonna say data dot append. And this does exactly what you think it would. So I'm just going to append an exclamation there. And of course it's going to do exactly what you think. Just appended that character to the end of our byte array, increase the length, not the capacity. Now let's see what we can do with removing. So I'm gonna say data, remove. Now this is a little bit challenging to wrap your head around it first because you may have lost some trust in this with Replace and saw how that behaved, but it's actually pretty simple. So I'm gonna say from the index to the length, let's go with zero to three. So I'm gonna just chop off those first three asterisks. Is that the right word? Asterisks? Astri? I don't know, somebody tell me what it is. We're gonna get rid of those first three bytes. And instead of three of them, it's just now hello world. But notice we have these two here still. So it's not gonna go ahead and automatically remove all of them is kind of I guess what I'm getting at here. So if you're looking for that, it's a different solution, but I just wanted you to be aware because I always get questions on why didn't it automatically remove them? You're talking about a starting position to a length and that's exactly what it did. So let's talk about reading data. I mean, the whole point of this class isn't just to update it into it. It's quite often you're going to get data from something it's going to be in the form of a cubite array. It's a really great, I wanna call it a container for data, but it's really not a container, it's an array. So if we just save and run, let's just for clarification, see what's actually in there. We have hello world and then an asterisk and asterisk and an exclamation. Okay, makes simple sense here. Let's go ahead and say, we want to know the first asterisk position. So I'm gonna say int first equal data dot index of, remember this is a zero based index. So it's going to give us in terms of zero base, meaning the first one's always going to be zero. And then we want to grab this. Just do a little copy and paste action. Let's call this last and let's go dot last index of. So we're basically saying, go find the first occurrence of this and then go find the last occurrence of this and put them in two different variables. From here, it's just ridiculously simple. We can just simply say something like, and I'm gonna just for the sake of time, copy paste, start first in last and let's save and run and see where these positions are. So the first occurrence is at 11 and the last occurrence is 12. So 11 and 12 works as expected. This is actually extremely easy to work with and I absolutely love that. Now, if that wasn't easy enough, what if we wanted to get like a section of something here? And what do I mean by that? Well, we're gonna use what's called mid, which stands for middle. We talked about this with Q string where we want to get something inside of it. So I'm gonna say if the first is greater than negative one because index of and last index of are going to return negative one if it didn't find it. And the last is greater than negative one. Then I wanna go ahead and do something. I'm gonna say Q info. I'm gonna say data. We want to get something in the middle or mid. And then of course we gotta give it an index and it's a zero based index. So I'm just gonna say the first comma. And then this is where I said, remember how it's reducing one. I said this kind of in the beginning of this video and it's a zero based array. This is what we need to do here. We're going to take the last index minus the first index and then plus one. You may be looking at that going, wait, what? Let's go ahead and run this and see what it does. Notice how we have two asterisks. Astri, I gotta figure out what that is. It's gonna drive me nuts. Point is we had to add that additional one because it's a zero based and it's looking for the link. So we said looking at our code here we want to go at the first position but then we want a length, not an ending position. So this is really what this little code here is doing is getting the length, which is our last position minus the first position but then it's only going to have one number lower so we need to add one to get the two. Just for clarification, if you don't do that you're not gonna get both of them. You're gonna get just one of them at the speed of this compiler. There we go. See, ta-da, just one. So if you're ever short on something using the mid always refer back and go, oh, I know why I have to get the length plus one because this is a zero based index. There's other reasons why it's not so much because it's a zero based index but just kind of burn that your memory and you'll never forget because I've made that mistake time and time again. All right, now let's go ahead and say data.clear so I'm just gonna clear all that garbage data out and say data.append. I'm just gonna add my name. We're just gonna start with a fresh set of data here and we want a nice four loop. So I'm gonna say four int i equals zero because this is a zero based index. i is less than data. And we want the length. Notice I'm using length not capacity because we wanna know the actual bytes that are in there and then we're gonna say i plus plus and then I want to queue info that out and say queue info data.app. So you can do data.at or I love doing this or you can do it the way you're pretty much used to if you've worked with just plain old C plus plus you don't have to do it the cute way of doing things. So let's go ahead and save and run and you can see there it goes. You can do at or just the normal C plus plus Y it doesn't really matter. Now you may be wondering why use add at all F1 on the keyboard returns the bytes at the index of the position of the byte array must be a valid index. So this is actually doing range checking but it does say see the operators. So they are virtually identical. Now that being said older versions of cute I have noticed minute little differences between these two but it was very rare if I went outside the range. When I say older we're talking very, very older versions of cute. So let's go ahead and do a for each because I personally love for each and we want to know each character in our data. So I'm going to say you info and it's going to do exactly what you think it would do. I mean, there's really, I'd love to turn this sort of long boring conversation but it's just going to print it out. It's just very simple, very easy to work with but that's the whole point of the class is that it does make this ridiculously simple. And I actually compliment it when I say it's boring because it is boring once you really get used to it you're just like, okay whoopee do a cute byte array but it has all this functionality baked into it. So now what we're going to do is we're going to say for each and I'm going to say auto item data.split. So remember what splitting does it's a lot like splitting firewood. We're going to say take that data, look for this occurrence and then split it into something else. And I want to just say item. Let's go ahead and print that item out. So now you can very easily take your array and split it into multiple items in this case, two different items. Again, calling this boring is a compliment because it's so boring it makes our lives ridiculously easy. Okay, to wrap this video up let's talk about encoding the data and again, we're literally just scratching the tip of the iceberg on what a Q byte array can do but you're going to work with Q byte array more than you think once you start diving into Q. So let's take a look at what our little array looks like. So the normal is just my name, Brian Cairns or feel free to put in your name whatever you want. But let's go ahead and look at this and say we want to repeat that. So rather than do some for loop we can just say repeat or I should say repeated and we're going to repeat that three times just because we like to annoy people and it does exactly what you think it would. Now that seems very simplistic and it actually is. So let's start diving into a couple more well a little bit more difficult to wrap your head around examples. So let's look at a common scenario. I'm going to say data.end and we're going to add in Q byte array and we're going to add what's called a white space and this is actually very common where you'll have like somebody types a tab and then they hit a hard return and a line feed but if you print this out it's not actually a printable character it's just white space. And then we're also going to say data.insert and we want to insert at the first position or the zero. I'm going to say Q byte array and I've seen people do this, they'll go. Let's do that again. One, two, three, there we go. I wanted to make sure I got it right but just because why not? Let's just do this and then let's just add in a tab and another and another just because people end users are annoying they make mistakes. So all of this is white space. Uh-oh. So what does this actually do? Well, let's go ahead and see. So I'm going to say normal and let's run this and see what normal now looks like. Normal is anything but normal. You see we've got this a lot of spaces with some tabs and then my name and some tabs and then a return line feed. How do we get all of that out of there? I don't want any of that. Well, let's rename this to trimmed and then let's say put normal back down there. Actually, let's call this actual. Actually, let's call it actual. There we go. That way we can see the difference between the two. All right, so our trimmed version is this nice neat name and then our actual has not been modified. So it's just returning another Q byte array with all that garbage stripped out of there. Now, somebody may be going, well, I don't understand how to change your original Q byte array. It's very simple. You just say data equal data not trimmed. Somebody down in the comments may yell at me for doing that because we're reusing that variable but under the hood basically C++ and Qt are gonna wipe that out and make a copy of it and replace it with a copy. So it just works. Now, let's go ahead and look at something I actually get asked constantly. How do you convert to and from hex encoding? So say Q byte array, and let's call this, I need a really good name hex. Why not? Close data dot two. Now, when you say two, it's going to give you a list of all the things that are baked right in that you can convert it to and you notice two hex is right up here. So that's right. It's literally just that simple. Say run, let's see what the two hex now looks like. There it is. There's the hex representation of that. That is really cool. Now, let's actually, I love saying that. Let's actually print the actual here. That way we know that we've now modified that just so if somebody coming back says, wait a minute. So now that we've got this hex, how do we convert it from hex back to a normal Q byte array? It's actually just as simple. So I'm going to say Q byte array. Let's call this from X equal. And I'm going to use the static function Q byte array from hex. And we'll just give it our hex encoded Q byte array. And then we can just reuse this real quick. Say run and let's see what that looks like. All right. So we switched our actual back to Brian Karens. Here's the hex encoded and then from hex we have decoded it back into the plain text. Ridiculously simple. And it's baked right into the class. We can actually, if we wanted to, do the same thing, but for Bay 64 encoding. If you don't know what Bay 64 encoding is, it's a standard encoded, I want to say a standard coding way of sending data back and forth across the internet. When you send an email, usually it's Bay 64 encoded. So we're just going to say to Bay 64 and see the Bay 64 encoded version. And then from Bay 64. And again, we can just use the static. You don't have to, but you can just because somebody out there will say, Hey, you don't actually need to create an instance of the class. And let's call this from Bay 64. It really is that simple. I know I whipped right through that, but once you understand the pattern again, this becomes boring and boring as a compliment because it's that easy to work with. So from hex is the plain text and then Bay 64 encoded and then Bay 64 decoded is back to my name. It's just that simple. 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 Voidromes 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.