ifstream::read just reads n bytes into a buffer, but doesn't append '\0' at the end of the buffer, right? Then when I use the buffer, how does it know the end of the buffer?
Should I manually append a '\0' at the end of the buffer?
The ifstream is used for reading from a file, binary or text. When dealing with a binary file with read, one cannot be sure the origin of a zero byte (from the file itself or appended by read) so read does not append a zero to destination buffer.
If you are working on a text file, then you can use std::getline, and receive a std::string:
istream& getline ( istream& is, string& str );
If you are reading into a std::string say, with std::getline, then string carries a length - std::string is not terminated by '\0' as C-style char* strings are. If you read into std::string and then use string.c_str() then that is null terminated. You can use this for a C-style string.
If you are reading into char* you must supply a length - that is how many bytes are read independent of any '\0' are found. In this case you should manually append a '\0' at the end of the buffer.
Yes, you are correct, you need to manually append it to the end of the buffer: buffer[length] = '\0'; if you want a null terminator.
When you call write you can also specify the length of the output: std::cout.write(buffer, length); which will output only length number of data to tell it when to stop requiring you not to use the null terminator.
I would look into std::getline and std::string for a more robust approach though.
You can put whatever you want at the end of the buffer, as long as there is space for it.
istream::read doesn't tell you how many bytes it read; it either reads everything you asked for, or changes the state of the stream to fail/eof.
If you want to handle the case where you might have less bytes than expected, use istream::readsome, it returns the number of bytes extracted.
As other answers mentioned, if you are dealing with strings, use functions that read strings, like std::getline, or the >> extractors. istream::read is for binary data - in which case std::streambuf is usually more convenient to use.
You can call ifstream::gcount() to determine the number of characters read in the read() operation.
Related
I have a quick question - how can I possibly write something in console window to std::cin without assigning it to a string or char[]? And then how to read the number of characters that are stored in buffer?
Let's say that I want to create an array of char, but it shall has the size of the input length. I might create a buffer or a variable of big size to store the input and then read its length, allocate memory to my char array and copy it. But let's also say that I am a purist and I don't want any additional (other than stream buffer) memory used. Is there a possibility to access std::cin buffer, read the number of characters stored and copy them to my array? I was trying to find the answer for several hours, reading cpp reference but I really couldn't find solution. I couldn't even find if there is a possibility to write something to std::cin buffer without assigning it to a variable, aka executing cin >> variable. I would appreciate any help, also if you have alternative solutions for this problem.
Also, does somebody know where can I find information about how buffers work (means where computer stores input from keyboard, how it is processed and how iostream works with computer to extract data from this).
Many thanks!
First of all in order for the input buffer to be filled you need to do some sort of read operation. The read operation may not necessary put what is read in to a variable. For example, cin.peek() may block until the user enters some value and returns the next character that will be read from the buffer without extracting it or you could also use cin.get along with cin.putback.
You can then use the streambuf::in_avail function to determine how many characters are in the input buffer including a new line character.
With that in mind you could do something like this:
char ch;
cin.get(ch);//this will block until some data is entered
cin.putback(ch);//put back the character read in the previous operation
streamsize size=cin.rdbuf()->in_avail();//get the number of character in the buffer available for reading(including spaces and new line)
if(size>0)
{
char* arr=new char[size];//allocate the size of the array(you might want to add one more space for null terminator character)
for(streamsize i=0;i<size;i++)
cin.get(arr[i]);//copy each character, including spaces and newline, from the input buffer to the array
for(streamsize i=0;i<size;i++)
cout<<arr[i];//display the result
}
That being said, i am sure you have a specific reason for doing this, but i don't think it is a good idea to do I/O like this. If you don't want to estimate the size of the character array you need for input then you can always use a std::string and read the input instead.
I am learning c++, however, I can not understand what is the difference BTW:
std::cin.get();
and
std::cin.getline();
although;I know how to use each of them, but can't understand why there are two?
I've read this explanation :
getlinereads the newline character then discard it; whereas .get()reads it then leaves it in the input queue ..!! why each of them does what it does ?
sorry for bad English :(
std::cin.get(), when called without parameters, reads one single character from input and returns it.
std::cin.getline(char* str, std::streamsize count) reads one line of input and copies it into the buffer str, followed by an extra null character to form a C-string. count must be the size of that buffer, i.e. the maximal number of characters (plus the null byte) it can copy into it.
To read a line without caring about the buffer size it may be better to use std::getline:
#include <string>
std::string line;
std::getline(std::cin, line);
Reads a line from cin into line.
See http://en.cppreference.com/w/cpp/io/basic_istream and http://en.cppreference.com/w/cpp/string/basic_string/getline .
"get" only retrieves a character, "getline" gets all characters to a line terminator. Thats the main difference.
I have string of more than 4 million characters and I want to send this string via a socket. However, send () can only accept const char. I tried using c_str().
My string contains more than one null terminator \0, therefore c_str () does not work correctly. any idea how to delete or replace \0
You can also specify the length of the buffer you are sending in that same function. I'd also recommend breaking that 4MB buffer into smaller pieces and sending them one after the other, as there's probably a limit on the buffer size the send function allows.
str.erase(std::remove(str.begin(), str.end(), '\0'), str.end());
I'm currently trying to write a bit of code to read a file and extract bits of it and save them as variables.
Here's the relevant code:
char address[10];
ifstream tracefile;
tracefile.open ("trace.txt");
tracefile.seekg(2, ios::beg);
tracefile.get(address, 10, ' ');
cout << address;
The contents of the file: (just the first line)
R 0x00000000
The issue I'm having is that address misses the final '0' because it puts a /0 character there, and I'm not sure how to get around that? So it outputs:
0x0000000
I'm also having issues with
tracefile.seekg(2, ios::cur);
It doesn't seem to work, hence why I've changed it to ios::beg just to try and get something work, although obviously that won't be useable once I try to read multiple lines after one another.
Any help would be appreciated.
ifstream::get() will attempt to produce a null-terminated C string, which you haven't provided enough space for.
You can either:
Allocate char address[11]; (or bigger) to hold a null-terminated string longer than 9 characters.
Use ifstream::read() instead to read the 10 bytes without a null-terminator.
Edit:
If you want a buffer that can dynamically account for the length of the line, use std::getline with a std::string.
std::string buffer;
tracefile.seekg(2, ios::beg);
std::getline( tracefile, buffer );
Edit 2
If you only want to read to the next whitespace, use:
std::string buffer;
tracefile.seekg(2, ios::beg);
tracefile >> buffer;
Make the buffer bigger, so that you can read the entire input text into it, including the terminating '\0'. Or use std::string, which doesn't have a pre-determined size.
There are several issues with your code. The first is that
seekg( 2, ios::beg ) is undefined behavior unless the stream
is opened in binary mode (which yours isn't). It will work
under Unix, and depending on the contents of the file, it
might work under Windows (but it could also send you to the
wrong place). On some other systems, it might systematically
fail, or do just about anything else. You cannot reliably seek
to arbitrary positions in a text stream.
The second is that if you want to read exactly 10 characters,
the function you need is istream::read, and not
istream::get. On the other hand, if you want to read up to
the next white space, using >> into a string will work best.
If you want to limit the number of characters extracted to a
maximum, set the width before calling >>:
std::string address;
// ...
tracefile >> std::setw( 10 ) >> address;
This avoids all issues of '\0', etc.
Finally, of course, you need error checking. You should
probably check whether the open succeeded before doing anything
else, and you should definitely check whether the read succeeded
before using the results. (As you've written the code, if the
open fails for any reason, you have undefined behavior.)
If you're reading multiple lines, of course, the best solution
is usually to use std::getline to read each line into a
string, and then parse that string (possibly using
std::istringstream). This prevents the main stream from
entering error state if there is a format error in the line, and
it provides automatic resynchronization in such cases.
I read istream::get and a doubt still hangs. Let's say my delimiter is actually the NULL '\0' character, what happens in this case? From what I read:
If the delimiting character is found, it is not extracted from the input sequence and remains as the next character to be extracted. Use getline if you want this character to be extracted (and discarded). The ending null character that signals the end of a c-string is automatically appended at the end of the content stored in s.
The reason I would prefer "get" over "readline" is because of the capability to extract the character stream into a "streambuf".
I dont' quite get your problem.
On the msdn website, for the get function, it says:
In all cases, the delimiter is neither extracted from the stream nor returned by the function. The getline function, in contrast, extracts but does not store the delimiter.
In all cases, the delimiter is neither extracted from the stream nor returned by the function. The getline function, in contrast, extracts but does not store the delimiter.
http://msdn.microsoft.com/en-us/library/aa277360(VS.60).aspx
I don't think your going to have a problem, since the msdn site tells that the delimiter is neither extracted from the stream, nor returned vy the function.
Or maybe I'm missing a point here?
If you have something like this, then delimiter will not get stuck in the input stream:
std::string read_str(std::istream & in)
{
const int size = 1024;
char pBuffer[size];
in.getline(pBuffer, size, '\0');
return std::string(pBuffer);
}
just an example if you have '\0' as delimiter and strings are not bigger than 1024 bytes.