What is wrong with this code of file handling in c++? - c++

I am new to c++ and specifically file handling.
I made this code.
#include <iostream>
#include <fstream>
using namespace std;
class student
{
public:
char s;
int age;
};
int main (void)
{
student a;
a.s = 'a';
a.age = 1;
ofstream myfile;
myfile.open("a1.txt",ios::app);
myfile.write(reinterpret_cast<char*>(&a),sizeof(student));
return 0;
}
When I opened the file a1.txt, it had the character a correctly in there, but for the integer it had some weird encoding and a message that if you continue to edit this file, it will be corrupted. Can't I write an object to a file containing an integer and a character or a string as well?

You're writing the binary representation of student into the file. The character will come out as expected; but the int will be the bytes used to represent the value, not a readable number.
If you want the output to be formatted as readable text, use formatted output:
myfile << a.s << ' ' << a.age << '\n';
For convenience, you could overload the operator for your class:
ostream & operator<<(ostream & os, student const & a) {
return os << a.s << ' ' << a.age;
}
myfile << a << '\n';
For more complex structures, you might consider the Boost.Serialization library. Or you might do what I tend to do, with tuples instead of plain structures, and variadic templates to read and write them, but that might be rather more fiddly than you'd like.

I would define your own << operator that handles your custom type.
#include <iostream>
#include <fstream>
using namespace std;
class student
{
char s;
int age;
};
ostream& operator<<(ostream &output, const student &o)
{
output << o.s << " " << o.age;
return output;
}
int main (void)
{
student a;
a.s = 'a';
a.age = 1;
ofstream myfile;
myfile.open("a1.txt",ofstream::app);
ofstream << a;
return 0;
}
When you called myfile.write(reinterpret_cast<char*>(&a),sizeof(student)); it was not converting your struct to a human-readable string then writing it to file. In reality, it was interpreting the memory of your struct as a series of characters then writing it to file.

You actually did write the int to the file, but not readable (123 => '1' '2' '3')
but the 4 (or 8) byte of that int. Your program can read the value back too,
the only probem is that we humans can´t read that form well.
Concering Strings:Just writing the whole struct will probably fail
(depending on the exact type of the string variable etc.), because the
"string" often stores only a pointer in the struct (which points to some
memory elsewhere, and this other memory isn´t written automatically to the file)
To be safe, write each variable of the struct explicitely (and handle different
var type appropiately) instead of writing them all together.
This way, things like different variable ordering and struct padding
can´t cause problems too. Other pitfalls to remember are different int
sizes and endianess on different computers... serialization isn´t trivial.

Related

cout operator << doesn't work for vector<char> [duplicate]

This question already has answers here:
How do I print out the contents of a vector?
(31 answers)
Closed 1 year ago.
Why doesn't this vector print out?
void str_read(std::vector<char> str);
int main() {
std::vector<char> str;
str_read(str);
std::cout << str << std::endl;
return -1;
}
void str_read(std::vector<char> str) {
while (1) {
char ch;
scanf("%c", &ch);
if (ch == '\n');
break;
}
}
I get an error:
error: no type named 'type' in 'struct std::enable_if<false, void>'
You get the error because there is no standard operator<< defined for std::vector. If you want that, you have to implement it yourself.
Even so, str_read() takes in a std::vector by value, so it receives a copy of the caller's vector, and thus any modifications it makes will be to that copy and thus lost when str_read() exits. That means the vector in main() is never populated.
Try this instead:
#include <iostream>
#include <vector>
#include <string>
std::ostream operator<<(std::ostream &out, const std::vector<char> &vec) {
for(auto ch : vec) {
out << ch;
}
return out;
/* alternatively:
return out.write(vec.data(), vec.size());
*/
}
void str_read(std::vector<char> &str) {
char ch;
while (cin.get(ch) && ch != '\n') {
str.push_back(ch);
}
}
int main() {
std::vector<char> str;
str_read(str);
std::cout << str << std::endl;
}
That being said, why not just use std::string instead of std::vector<char>? There is a standard operator<< defined for std::string, and a standard std::getline() function for reading characters into std::string until '\n':
#include <iostream>
#include <string>
int main() {
std::string str;
std::getline(cin, str);
std::cout << str << std::endl;
}
There are 3 BIG problems here.
You are passing the vector by value, not by reference. As a result, any changes made to the vector in the function will stay local to the vector initialised as a part of that function call stack. It will be deleted by the time you return. so change the signature to str_read(std::vector<char>& str).
You are going through the stdin capture character by character. AFAIK scanf although will read the new line character, will leave it in the buffer not appending it to your string. From the looks of it, all you are trying to do is read a string from stdin, store it in a char array, and print that char array out. Don't overengineer stuff. just use std::cin and store it in a string, to begin with, like
std::string captured_string;
std::getline(std::cin, captured_string);
You can then std:cout << captured_string << "\n"; directly
If you insist on storing characters in a vector, which I do not understand why would you need to, you can just do std::vector<uint8_t> char_array(captured_string.begin(), captured_string.end()). However, std::cout << char_array << "\n" will still not work. That is for 2 reasons, the first one being that std::vector<T, allocator> does not have an overload for << of std::ostream. The second is that the moment you convert your string to an array of characters, they do not mean the same thing.
I think you misunderstood what you were taught in class about strings being stored as arrays of characters. They are indeed stored as arrays, but an array type and a string type are fundamentally different and are not equivalent.
Capturing the string from stdin will anyway store it in a char* or a std::string as I have shown above. You can use that directly without having to convert a string to an array of characters.
In essence, your program decays to this
#include <iostream>
#include <string>
int main()
{
std::string captured_string;
std::getline(std::cin, captured_string);
std::cout << captured_string << "\n";
return EXIT_SUCCESS;
}
EDIT
I just realised that your objective may have been to print the string character by character. In that case, you don't need to convert it to an array. There are multiple ways of doing this, but the least confusing and probably the easiest way of doing this would be to use the range-based for loop.
int main()
{
std::string captured_string;
std::getline(std::cin, captured_string);
for (auto& ch: captured_string) // containers in the standard library support range based for loops.
std::cout << ch << "\n"; // print each character in the string on a new line
return EXIT_SUCCESS;
}
First, explain
error: no type named 'type' in 'struct std::enable_if<false, void>'
cout is character-oriented stream output and does not accept output formats of the vector<char> type.
Vector is an array of elements of the specified type. If you want to use a vector, you can store values in it first:
vector<char> str;
str.push_back('H');
An element of the array is then printed to standard output cout
cout << str[0] <<endl;
The second problem is that your function str_read is defined as void, which has no value, so it cannot be located in cout's standard output either.
It looks like you want to put the characters as input into a vector and print them out. If you position the function as void, you will need to change the value of str by reference
Next question, I see that your while loop break is mistimed and does not combine with if. You should not add a semicolon after an if statement.
if (ch == '\n')break;
Finally, if you choose to change the value of str by reference, you need to implement this in the function, as mentioned above, when you type a value of ch, you need to use str.push_back(ch) to store it.
Your 'str' variable doesn't have anything in it when you try to display it. You also don't initialize your vector to any variables for the function to read anyways.

Getting garbage when using getline()

I am practicing classes and I am trying to allow the user to enter its name using a space. When the user enters a space, my output is "garbage". Is it possible to use the get.line function with a class? This is what I have.
//Practicing classes.
#include <iostream>
#include <string>
using namespace std;
//Class decleration
class person
{
//Access Specifier
public:
string name; //Member Vairable Decleration
int number; //Member Function Decleration
};
//Main function.
int main()
{
//Object creation from class.
person obj;
//Get input Values for object Variables
cout<<"Enter the Name: \n";
cin>>obj.name;
cin.getline(obj.name);
cout<<"Enter the number:\n";
cin>>obj.number;
//Show the output
cout<< obj.name <<": " << obj.number << endl;
return 0;
}
Change this:
cin>>obj.name;
cin.getline(obj.name);
to:
std::getline (std::cin, obj.name);
as specified by the manual. You can omit std::, since you are already using namespace std.
Example run:
Enter the Name:
samaras
Enter the number:
1
samaras: 1
However notice that the data members of a class are usually on private scope and public functions can apply to them. Also, as Thomas Matthews put it: "This breaks data hiding and encapsulation rules. I highly recommend that the functionality for reading the data member be placed in the class.".
You may want to look into overloading operator >>. However, if you feel like you are not done with understanding the classes, I would suggest to leave that for later.
You said that you want to return the full name of the user, So I create a simple function that returns a concatenated value using the first last name. but if you want you should go over tuple or pair to make advance function.
#include <iostream>
#include <string>
using namespace std;
class person
{
//Access Specifier
public:
string firstname;
string lastname; //Member Vairable Decleration
int number; //Member Function Decleration
};
string returnCompleteName(string firstname, string lastname)
{
return firstname + " " + lastname;
}
//Main function.
int main()
{
//Object creation from class.
person obj;
//Get input Values for object Variables
cout<<"Enter the FirstName: \n";
cin>>obj.firstname;
cout<<"Enter the LastName: \n";
cin>>obj.lastname;
cout<<"Enter`enter code here` the number:\n";
cin>>obj.number;
//Show the output
cout<< returnCompleteName(obj.firstname,obj.lastname) <<": " << obj.number << endl;
return 0;
}
I also had the same problem using the specified answers, because I didn't realize my file was encoded in UCS-2. This helped : How to read a UCS-2 file?
Wide streams use a wide stream buffer to access the file. The Wide
stream buffer reads bytes from the file and uses its codecvt facet to
convert these bytes to wide characters. The default codecvt facet is
std::codecvt which converts between the
native character sets for wchar_t and char (i.e., like mbstowcs()
does).
You're not using the native char character set, so what you want is a
codecvt facet that reads UCS-2 as a multibyte sequence and converts it
to wide characters.
#include <fstream>
#include <string>
#include <codecvt>
#include <iostream>
int main(int argc, char *argv[])
{
wifstream fin("en.rc", std::ios::binary); // You need to open the file in binary mode
// Imbue the file stream with a codecvt facet that uses UTF-16 as the external multibyte encoding
fin.imbue(std::locale(fin.getloc(),
new std::codecvt_utf16<wchar_t, 0xffff, consume_header>));
// ^ We set 0xFFFF as the maxcode because that's the largest that will fit in a single wchar_t
// We use consume_header to detect and use the UTF-16 'BOM'
// The following is not really the correct way to write Unicode output, but it's easy
std::wstring sLine;
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> convert;
while (getline(fin, sLine))
{
std::cout << convert.to_bytes(sLine) << '\n';
}
}
Open the file you are reading with an hex editor and check if there are some bytes before your visible data. I ended up by removing these bytes overwriting them with 20 hex data which means space, and then cleaning the file with the editor.

Copy data between streams of different type

How can I effectively insert data from one stream into another stream of different type?
I have tried the following:
#include <iostream>
#include <sstream>
using namespace std;
int main(void)
{
basic_stringstream<unsigned short> uss;
stringstream cs;
unsigned short val = 0xffff;
uss.write(&val, 1); // write value to 'uss'
uss.read(&val, 1); // read data from 'uss' into 'val'
cout << hex << val << endl; // gives 0xffff
cs << uss.rdbuf(); // copy 'uss' contents into 'cs'
cs.read((char*) &val, 2); // read data from 'cs' into 'val'
cout << hex << val << endl; // gives 0x3030 ?
return 0;
}
First, as noted in this question, you can't instantiate basic_strings and streams with types like unsigned short without writing a hell lot of custom template specializations.
Second, this line
cs << uss.rdbuf();
doesn't do what you think it does. basic_ostream's operator<< that takes a basic_streambuf is
basic_ostream<charT,traits>& operator<< (basic_streambuf<char_type,traits>* sb);
where char_type is a typedef for charT. In other words, the character types must match.
In your case, they don't match, so you end up calling operator<<(const void *) instead, and just printing out the address. When I tested this on coliru, it printed out 7830 instead, for the characters 0x.

Why doesn't the string class have a << operator (operator<<) predefined so that strings work like ostringstreams?

It seems to me that defining the << operator (operator<<) to work directly with strings is more elegant than having to work with ostringstreams and then converting back to strings. Is there a reason why c++ doesn't do this out of the box?
#include <string>
#include <sstream>
#include <iostream>
using namespace std;
template <class T>
string& operator<<(string& s, T a) {
ostringstream ss;
ss << a;
s.append(ss.str());
return s;
}
int main() {
string s;
// this prints out: "inserting text and a number(1)"
cout << (s << "inserting text and a number (" << 1 << ")\n");
// normal way
ostringstream os;
os << "inserting text and a number(" << 1 << ")\n";
cout << os.str();
}
Streams contain additional state. Imagine if this were possible:
std::string str;
int n = 1234;
str << std::hex;
str << n;
return str; // returns "0x4d2" (or something, I forget)
In order to maintain this additional state, strings would have to have storage for this state. The C++ standards committee (and C++ programmers in general) have generally frowned upon superfluous resource consumption, under the motto "pay only for what you use". So, no extra fields in the string class.
The subjective answer: is that I think the std::string class was quite poorly designed to begin with, especially compared to other parts of C++'s excellent standard library, and adding features to std::string is just going to make things worse. This is a very subjective opinion and feel free to dismiss me as a raving lunatic.
The problem with the idea of strings being output streams is that they would become too heavy.
Strings are intended to "hold string data", not to format some output. Output streams have a heavy "state" which can be manipulated (see <iomanip>) and thus has to be stored. This means that, of course, this has to be stored for every string in every program, but almost none of them are used as an output stream; so it's a huge waste of resources.
C++ follows the "zero overhead" design principle (or at least no more overhead than totally necessary). Not having a string class which doesn't add any unnecessary overhead would be a huge violation of this design principle. If this was the case: what would people do in overhead-critical cases? Use C-strings... ouch!
In C++11, an alternative is to use the operator+= with std::to_string to append to a string, which can also be chained like the operator<< of the output stream. You can wrap both += and to_string in a nice operator<< for string if you like:
template <class Number>
std::string& operator<<(std::string& s, Number a) {
return s += std::to_string(a);
}
std::string& operator<<(std::string& s, const char* a) {
return s += a;
}
std::string& operator<<(std::string& s, const std::string &a) {
return s += a;
}
Your example, updated using this method: http://ideone.com/4zbVtD
Probably lost in the depths of time now but formatted output was always associated with streams in C (since they didn't have "real" strings) and this may have been carried over into C++ (which was, after all, C with classes). In C, the way to format to a string is to use sprintf, a variation on fprintf, the output-to-stream function.
Obviously conjecture on my part but someone probably thought similarly to yourself that these formatting things in the streams would be brilliant to have on strings as well, so they subclassed the stream classes to produce one that used a string as it's "output".
That seems the elegant solution to getting it working as quickly as possible. Otherwise, you would have had formatting code duplicated in streams and strings.

iostream limit max number of chars to read

Learning C++ with help of Bruce Eckel "Thinking in C++". Stuck in exercise 05 of chapter "Iostreams":
Text of exercise
We know that setw( ) allows for a minimum of characters read in, but what if you wanted to read a
maximum? Write an effector that allows the user to specify a maximum number of characters to
extract. Have your effector also work for output, in such a way that output fields are truncated, if
necessary, to stay within width limits.
I understand how to create manipulators both without and with parameter (which one is called effectors in the book terminology). But do not understand how to limit maximum number of characters to extract. std::ios_base::width specifies the minimum number of characters.
Shoud I do some tricks with underlying streambuf object?
#include <iostream>
#include <iomanip>
#include <string>
#include <cstring>
using namespace std;
class fixW{
char* chrP;
char str[1024];
size_t Max;
public:
fixW(char* p,size_t m=25):chrP(p),Max(m){}
friend istream& operator >>(istream& is,fixW fw){
is >>fw.str;
size_t n=strlen(fw.str);
cout <<" n= "<<n << endl;
if(n>=25){
fw.str[fw.Max]='\0';
}
strcpy(fw.chrP,fw.str);
return is;
}
friend ostream& operator<<(ostream& os, fixW fw){
for(size_t i= 0; i<fw.Max; ++i){
fw.str[i] = fw.chrP[i];
}
fw.str[fw.Max]='\0';
return os <<fw.str;
}
};
int main(){
char s[80];
cin >> fixW(s,25);
cout << s << endl;
cout << fixW(s,10)<<endl;
cout << s <<endl;
return 0;
}
Its not a perfect solution (but I can't think of another way at the moment without reading the iostream library).
Say you manipulator is:
class MaxFieldSize {/*STUFF*/};
When you write the stream operator(s) you write a slightly funky one that does not return an actual stream (but rather returns a stream with a wrapper around it).
MaxFieldWdithStream operator<<(std::ostream&, MaxFieldSize const& manip);
Now you overload all the stream operator of this class to truncate their input before returning a normal stream object.
class MaxFieldWithStream { std::ostream& printTruncatedData(std::string& value);};
Then all you need is the generic overloads:
template<typename T>
std::ostream& operator<<(MaxFieldWithStream& mfwstream, T const& value)
{
std::stringstream trunStream;
trunStream << value;
return mfwstream.printTruncatedData(trunStream.substr(0, mfwstream.widthNeeded));
}
// You will probably need another overload for io-manipulators.
I would also add a conversion operator that converts MaxFieldWithStream to std::iostream automatically so that if it is passed to a function it still behaves like a stream (though it will loose its max width property).
class MaxFieldWithStream
{
std::ostream& printTruncatedData(std::string& value);};
operator st::ostream&() const { return BLABLAVLA;}
};