Hello again! In this video, we are going to look at unbuffered input and output. We saw in the last
video
that the stream can hold up data. And sometimes we want data to be sent to the output immediately.
So we can use the flush to override the stream's behavior.
Sometimes we want to go even further and override the buffer altogether, To bypass them.
An example of that is a network application.
There is a network protocol, which is basically a set of rules for communicating between computers.
And these will say that the data has to be sent in a so-called "packet", a block of memory which has a
fixed size or a given size.
And almost certainly that is not going to be the size that the stream chooses for the buffer.
It may also be critical to send or receive data at a particular time.
So really, we do not want to have stream buffers getting in the way and delaying data and sending the
wrong size of data.
C++ provides some lower-level operations on streams which do bypass the stream;s buffer.
They also avoid performing any formatting.
If you have an output stream, it may add spaces or change numbers to make them look nice.
If you have an input stream and you're using the right shift operator, it will
remove all the white space. So none of that will happen with these low-level operators.
These are lower-level, so they are more difficult to use.
You should really use the left shift and right [shift] operators unless you actually need these lower level
ones.
Some circumstances where they are useful is for networking applications and also for communicating directly
with hardware.
If you have a network card or a sound card or a hard drive, it probably requires that you communicate
[with] it with memory with a fixed block. And you need to have control over the timing of when data is sent and
received.
The simplest memory block is a single character, and there are member functions for that. An input screen
has a get() member function, which will fetch the next character from the stream.
An output stream has a put() member function, which will send a single character to the stream.
get() will take a character by address, so it can be modified, and put() will take it by value.
get() will return true unless it reads the end of input character.
So we can try that out. We have a program which prompts the user to enter some text, so they do not
see a blank screen, and then we have this loop.
So, keep getting a single character until the end of input, and then send that character to the output.
So let's enter some text.
So it doesn't actually read the data until I press the return key.
So this is still line-oriented input.
Obviously, if you are reading from a file or a network socket, then it would not wait until the end
of the line.
So there we are. We got the text that we entered. The space characters were not removed.
It has not added any extra space characters.
So once I pressed the return key, the stream got the data. And then it went through and took the first character,
and stored it in this variable 'c', and then we called put() on the output stream.
And that sent the character to the output.
And then the next character and so on.
And because I have not entered the end-of-input [character], it is still waiting for more input.
So I will enter control-Z, which should be the Windows end of input character. And there, the program has now ended.
The loop has terminated because get() saw the end-of-input input symbol.
If we want to read or write more than one character at a time, the read and write member functions will
do that for us.
We are not using the stream's buffer, so we need to provide one ourselves.
This will be an array of characters. For write, this buffer will contain all the data that we are going
to send to the output. For a read operation,
this buffer must be large enough to store all the data that we are going to receive.
These member functions take two arguments. The first one is the array, a pointer to the first element, and
the second one is the number of characters.
So for a read operation, this must be the number of characters we expect to receive, and the buffer
must be big enough to hold at least that many characters.
And then, when we have a write operation, the buffer will contain the data that is going to be written.
And then we have the number of elements in that data.
We are going to read from a file, "input.txt".
There is nothing very exciting here, just the numbers from 0 to 9.
So we open that file.
We do the usual checks.
Then we read into this buffer. So we pass our array of character and the size of the array. And then we close
the file, because we have finished reading from it.
And then we now have the data in the "filebuf" [array], that we read from the file, so we can send that to
the output, using the write member function.
So there we are, we got the data from the file, 0 to 9.
Usually when you are receiving data, you will have some idea how much you are going to get. The network
protocol will probably have something which says how much data it is going to send.
Sometimes it does not.
So you need to be able to find out.
And also, you need it anyway. If you are going to process the data, you may need to allocate memory,
so you need to know how much memory to allocate.
Or you may have to pass it to a function which has the number of elements as an argument.
And also you need to check that the transfer was successful.
If the other computer only sent part of the data and is going to send the rest later, you need to
know about that.
If something went wrong, if the packages is corrupt or the connection has been dropped, then you need
to know about that as well.
So we need to know the number of characters that we have read. And the gcount member function will
return that.
So we call that on the stream, and this will return the number of characters that it read in the last read operation.
The type of this is - I think there is a special type for this, but it is a number. That is usually all
you need to know.
So this is a valid situation for using "auto".
So I have added that to the program that we had to before. I am storing the number of bytes that we
read in this variable.
And then in this example, we just print it out.
So we read 10 bytes from the input file, which is correct. And we still have the same data.
Okay, so that's it for this video.
I'll see you next time.
Meanwhile, keep coding!