c++ - properly writing std::string to binary file [duplicate] - c++

This question already has answers here:
How to write std::string to file?
(4 answers)
Closed 2 months ago.
I am trying to get strings from cin and than write it to binary file.
I've read that writing pure string won't work, so I tried to convert it to char*.
The following code writes it ok (...probably), but only the first 8 chars, so the output in file is incomplete.
std::string nick, ip, port;
std::cout << "IP: ";
std::cin >> ip;
std::cout << "port: ";
std::cin >> port;
ofstream file1("lastServers.bin", ios::out | ios::binary);
if (file1.good())
{
const char* p_IP = ip.c_str();
const char* p_PORT = port.c_str();
int length = sizeof(&p_IP)+sizeof(&p_PORT);
char* tmp1 = new char[length];
int index = 0;
memcpy((tmp1 + index), p_IP, sizeof(&p_IP));
index = index + sizeof(&p_IP);
memcpy((tmp1 + index), p_PORT, sizeof(&p_PORT));
file1.write(tmp1, length);
file1.close();
delete[] tmp1;
}
else
{
std::cout << "file error write" << endl;
}
Thanks in advance for any help :)

Your code can be written as
ofstream file1("lastServers.bin", ios::out | ios::binary);
if (file1.good()) {
file1.write(ip.c_str(), ip.size());
file1.write(port.c_str(), port.size());
file1.close();
}
else {
std::cout << "file error write" << endl;
}
string::c_str() returns a const pointer to the text in the string.
string::size() returns the number of characters in the string.
You don't need to concatenate the data before writing to the file, writing one then the other has the same result.
If you wanted to write C type code rather than C++, you can use strlen(p_IP) to get the length of the IP string rather than using sizeof.
The sizeof operator gives you the size of the class instance, i.e. the size of the object BUT the string object's size is never affected by the size of the string it manages.
In C++, objects that manage something (think strings managing characters, containers managing their contents, etc.) usually have a method to determine the size of what they're managing. For std::string and other STL containers that method is size().
Note that writing these strings in this format means you can't tell where one string ends and another one starts. Two options to consider are using a terminating character that you know won't appear in any strings, or writing the length of the string to the file before the text of the string itself. I won't elaborate here as it was not asked in the original question.

sizeof returns you the size of the string object in the memory, not the length of the string itself. Specifically, sizeof(&p_IP) returns the size of the pointer to p_IP, which is always 4 bytes on a 32-bit system. Your variable length simply does not compute to the correct value. To get the length of a char*, use strlen.

Related

C++ how to read a line of text from file to char array [duplicate]

This question already has answers here:
How to read a file line by line or a whole text file at once?
(9 answers)
Closed 5 years ago.
i am having difficulty reading a line of text from file to a char array.` assume that i have a text file named "sample.txt", and it contains only few words per line. here is what my code is:
char buffer[100];
ifstream file;
file.open("sample.txt")
file >> buffer;
this stops reading after space. I also tried:
file.getline(buffer,100);
but this does not give me the correct text. After the text, some random symbols were assigned to the remaining of the array.
any help would be deeply appreciated!
Edit:
This char array is a temporary array. Im passing text to this array, and then pass it to a class data member
For every and any input operation, you must check the return value; otherwise you cannot know whether the operation succeeded and what it did.
If you are reading into a fixed buffer, you need to check the stream object and the count of extracted characters:
file.getline(buffer, sizeof buffer);
auto n = file.gcount();
if (file) {
std::cout << "Read line with " << n << " characters: '";
std::copy_n(buffer, n, std::ostream_iterator<char>(std::cout));
std::cout << "'\n";
} else if (n > 0) {
std::cout << "Read incomplete line with prefix '";
std::copy_n(buffer, n, std::ostream_iterator<char>(std::cout));
std::cout << "'.\n";
file.clear();
} else {
std::cout << "Did not read any lines.\n";
}
Note that the extracted count (file.gcount()) includes the null terminator, which basic_istream::getline writes into the output buffer. (So the maximal length of a line that can be read completely is sizeof(buffer) - 1.)
Alternatively, you can read into a dynamic string. This means that memory will be automatically allocated to hold each complete line, but it's a lot easier to reason about:
for (std::string line; std::getline(file, line); ) {
std::cout << "Read one line: '" << line << "'\n";
}
Here we only check the success of the input operation, and we do this inside the loop condition. The number of extracted characters (this time excluding the null terminator) is precisely line.size() after the successful read.
It would be much easier to just use a std::string object to get the line content and later convert it to a char array:
std::string str;
std::getline(file,str);
const char* c = str.c_str();
Why is this happening
Try printing out the array before doing getline. You'll notice that the zany symbols populate the whole array.
getline() will plop characters into your array from the input stream until either 100 characters are read, or the delimiting character is reached. If the delimiting character is reached before 100 characters, the rest of your length-100 array will point to uninitialized memory.
If you want to interact with streams in this way, (using a char array instead of a string) you'll have to decide how you want the remainder of the array to be initialized if you'd like to avoid nonsense.

how can I limit a user to input 8 characters into string(dynamic char array)in c++?

int size;
cout<<"enter string size"<<endl;
cin>>size;
char * string1 = new char[size];
well hello,im working on a project and one of my functions purpose is to get a dynamic char array as arg which sends to the output the first letters of the words in the string.
so the user decides the string length (for the initialized dynamic array),
but then how can i ensure he won't exceed the length of the array he chose?(the compiler does not Refer to it as a mistake).
can i force the 'cin' operator to limit itself?
Using std::string is better, but...
char input [8];
cin.getline (input, 8);
Note, there may still be data in the input buffer after this that you may need to deal with.
Edit
Given the original code in the question:
cin.getline(string1, size);
Just have them input into a std::string. Don't bother with the manual memory management.
std::cout << "Enter your name.\n> ";
std::string name;
std::cin >> name;
Then, if you need to only pass the first 8 characters or something, use name.substr(...).
You really ought to be std::string, but if you really want to use a character array, you should use:
cin.getline(string1, 8);
This gets 8 characters from the user input.
cin getline works as followed:
It extracts characters, without any formatting and storms them as an c-string. It will stop extracting characters when either the new line character is reached, a set char deliminator, or until the number of characters specified has been extracted.
You can make this approach with std::string. For example:
const int maxchar = 8;
string str;
cin >> str;
if (str.size() > maxchar )
{
err << "The input is too long." << endl;
return 1;
}

Read file, line by line, and store values into a array/string?

I have learned my lesson, so i will be short, and to the subiect.
I need a function, in my class, that can read a file line by line, and store them into a array/string so i can use it.
I have the following example( please don`t laugh, i am a begginer):
int CMYCLASS::LoadLines(std::string Filename)
{
std::ifstream input(Filename, std::ios::binary | ios::in);
input.seekg(0, ios::end);
char* title[1024];
input.read((char*)title, sizeof(int));
// here what ?? -_-
input.close();
for (int i = 0; i < sizeof(title); i++)
{
printf(" %.2X ";, title[i]);
}
printf("\");
return 0;
}
I'm not sure exactly what your are asking.
However - below is some code that reads a file line-by-line and stores the lines in a vector. The code also prints the lines - both as text lines and the integer value of each character. Hope it helps.
int main()
{
std::string Filename = "somefile.bin";
std::ifstream input(Filename, std::ios::binary | ios::in); // Open the file
std::string line; // Temp variable
std::vector<std::string> lines; // Vector for holding all lines in the file
while (std::getline(input, line)) // Read lines as long as the file is
{
lines.push_back(line); // Save the line in the vector
}
// Now the vector holds all lines from the file
// and you can do what ever you want it
// For instance we can print the lines
// Both as a line and as the hexadecimal value of every character
for(auto s : lines) // For each line in vector
{
cout << s; // Print it
for(auto c : s) // For each character in the line
{
cout << hex // switch to hexadecimal
<< std::setw(2) // print it in two
<< std::setfill('0') // leading zero
<< (unsigned int)c // cast to get the integer value
<< dec // back to decimal
<< " "; // and a space
}
cout << endl; // new line
}
return 0;
}
I do not laugh due to your original code - no way - I was also a beginner once. But your code is c-style code and contains a lot of bugs. So my advice is: Please use c++ style instead. For instance: never use the C-style string (i.e. char array). It is so error prone...
As you are a beginner (your own words :) let me explain a few things about your code:
char* title[1024];
This is not a string. It is 1024 pointers to characters which can also by 1024 pointers to c-style strings. However - you have not reserved any memory for holding the strings.
The correct way would be:
char title[1024][256]; // 1024 lines with a maximum of 256 chars per line
Here you must make sure that the input file has less than 1024 lines and that each line each less than 256 chars.
Code like that is very bad. What to do if the input file has 1025 lines?
This is where c++ helps you. Using std::string you don't need to worry about the length of the string. The std::string container will just adjust to the size you put into in to it.
The std::vector is like an array. But without a fixed size. So you can just keep adding to it and it will automatically adjust the size.
So c++ offers std::string and std::vector to help you to handle the dynamic size of the input file. Use it...
Good luck.

C++ stringstream to char* conversion memory allocation

Can anyone explain how the following code is working and does not crash the application?
int main() {
char *tempStr = new char[5];
tempStr[0] = '\0';
string stemp = "helloworld";
stringstream sstream;
sstream.str(stemp);
cout << "len before = " << strlen(tempStr);
sstream >> tempStr;
cout << "len after = " << strlen(tempStr) << endl;
cout << tempStr << endl;
delete[] tempStr;
return 1;
}
I am getting the output as
len before = 0
len after = 10
helloworld
Did stringstream allocate memory for the extra characters in the char pointer?
Also want to know the correct way to copy data from stringstream to char* array, without exceeding the memory allocated for char*?
Did stringstream allocate memory for the extra characters in the char pointer?
No. Your code invokes undefined behavior.
Also want to know the correct way to copy data from stringstream to char* array, without exceeding the memory allocated for char*?
It is not a good idea to read into char*. Use std::string to read input from stream. But then if you still want to know for the sake of knowledge, use std::istream::read().
if ( sstream.read(tempStr, 5 ) )
{
//read succeeded
}
By the way, you can merge these two lines:
stringstream sstream;
sstream.str(stemp);
into one:
stringstream sstream(stemp);
or simply this:
stringstream sstream("helloworld"); //no need of stemp!
Hope that helps.
No. You overwrote memory, invoking undefined behavior, but nothing obvious happened, so the error went unnoticed. There is no requirement that doing something like this should trigger any kind of human-visible error or special action, thus the wording undefined behavior.
You're going to have to do it chunk by chunk, and re-allocate the char array if it runs out of space. In C++, there's little point in doing this manually. Just use std::string and be done.

C++ - Convert FILE* to CHAR*

I found a C++ source file which calculates expressions from a command line argument (argv[1]), however I now want to change it to read a file.
double Utvardering(char* s) {
srcPos = s;
searchToken();
return PlusMinus();
}
int main(int argc, char* argv[]) {
if (argc > 1) {
FILE* fFile = fopen(argv[1], "r");
double Value = Utvardering(fopen(argv[1], "r"));
cout << Value << endl;
}else{
cout << "Usage: " << argv[0] << " FILE" << endl;
}
cin.get();
return 0;
}
However the Utvardering function requires a char* parameter. How can I convert the data read from a file, fopen to a char*?
The function fopen just opens a file. To get a string from there, you need to read the file. There are different ways to to this. If you know the max size of your string in advance, this would do:
const int MAX_SIZE = 1024;
char buf[MAX_SIZE];
if (!fgets(buf, MAX_SIZE, fFile) {
cerr << "Read error";
exit(1);
}
double Value = Utvardering(buf);
Note: this method is typical for C, not for C++. If you want more idiomatic C++ code, you can use something like this (instead of FILE and fopen):
ifstream in;
in.open(argv[1]);
if (!in) { /* report an error */ }
string str;
in >> str;
Use the fread() function to read data from the FILE* into a buffer. Send that buffer into Utvardering().
I have no idea what "Utvardering" expects, or how it's using the information.
There are two possibilities -
1) Utvardering may be defined using char*, but expecting a FILE* (in effect, treating char* like void*). I've seen this before, even though it's pretty awful practice. In that case, just cast fFile to char* and pass it in.
2) Utvardering may be expecting a null terminated string (char*) as input. If you're using fopen like this, you can use fread to read the file contents into a buffer (char[]), and pass it to your function that takes a char*.
It looks like you need to write code to read the file into a character array and pass that to Utvardering.
Just passing the return value of fopen will cause the address of the opaque data structure pointed to by that pointer to be passed to Utvardering. Utvardering will happily treat those bytes as character data when they are not. Not good.
Good example of reading data from a file here:
http://www.cplusplus.com/reference/clibrary/cstdio/fread/
then pass the buffer to your function