extra characters inserted in strings (I inserted the null character '\0') - c++

I've been having troubles with the read() member function of the fstream object of the STL. I have a class ClientData with four members variables (int accountNumber, string firstName, string lastName, double balance).
When I read the information, I get some extra characters at the end of each string that were not typed. If I type "AVeryLongWord" and after that a "ShortOne" the result is "ShortOnegWord" or something of the sort.
I have a ClientData object called clientData and to store/retrieve the information, I use the following statements:
To write:
nombrArchivo.write(reinterpret_cast<const char*>(&clientData), sizeof(clientData));
And to read the data I use:
archivo.seekg(0);
archivo.read(reinterpret_cast< char*>(&cliente), sizeof(ClientData));
To set the strings I use:
int length = fName.size();
length = length<15?length:14;
fName.copy(firstName, length);
fName[length]='\0';
The whole thing is here. It's a CodeBlocks(13.12) project with Mingw (GCC 4.6).

Related

Extract Words from a string, then store in variables

I am trying to store user input into a string and then use that string to store individual words into character variables which I will later use to set up a class object. To be clear, I'd like a method that doesn't include using vector or stringstream since I haven't touched on those subjects yet and I'm hoping there's a way to do this without those functions or the libraries they are included in.
The class I'm working with:
class Name
{
char* m_firstName{};
char* m_middleName{};
char* m_lastName{};
}
So far the code I am using works, but obviously does not split up the names - I'm only able to send one string to one variable:
std::istream& Name::read(std::istream& istr){
string input;
getline(istr, input);
char* firstName = new char[input.length() + 1];
strcpy(firstName, input.c_str());
set(firstName);
return istr;
}
I need to be able to pull out words inside the string which will be delimited by a space. For example, if the input is "John Adam Smith", I'd like to be able to store "John", "Adam" and "Smith" inside of firstName, middleName and lastName character string variables respectively.
I will then be using those variables within a "set" function that I have for inserting the data into the class. I'm not worried about that part, once I have the variables I should be good to go. But I can't figure out how to separate the words inside the string.
Just go
string first;
string middle;
string last;
istr >> first >> middle >> last;
Things wrong in your current code:
There is no way to get data into Name all the fields a re private and you have no methods.
You should have strings everywhere, dont dynamically allocate char*s in c++ unless you have to. Dont juggle raw pointers.

How to save text file to struct with string in C++

I'm wanting to save the content of a file to a struct. I've tried to use seekg and read to write to it but it isn't working.
My file is something like:
johnmayer24ericclapton32
I want to store the name, the last name and the age in a struct like that
typedef struct test_struct{
string name;
string last_name;
int age;
} test_struct;
Here is my code
int main(){
test_struct ts;
ifstream data_base;
data_base.open("test_file.txt");
data_base.seekg(0, ios_base::beg);
data_base.read(ts, sizeof(test_struct));
data_base.close();
return 0;
}
It doesn't compile as it don't want me to use ts on the read function. Is there another way - or a way - of doing it?
Serialization/Deserialization of strings is tricky.
As binary data the convention is to output the length of the string first, then the string data.
https://isocpp.org/wiki/faq/serialization#serialize-binary-format
String data is tricky because you have to unambiguously know when the string’s body stops. You can’t unambiguously terminate all strings with a '\0' if some string might contain that character; recall that std::string can store '\0'. The easiest solution is to write the integer length just before the string data. Make sure the integer length is written in “network format” to avoid sizeof and endian problems (see the solutions in earlier bullets).
That way when reading the data back in you know the length of the string to expect and can preallocate the size of the string then just read that much data from the stream.
If your data is a non-binary (text) format it's a little trickier:
https://isocpp.org/wiki/faq/serialization#serialize-text-format
String data is tricky because you have to unambiguously know when the string’s body stops. You can’t unambiguously terminate all strings with a '\n' or '"' or even '\0' if some string might contain those characters. You might want to use C++ source-code escape-sequences, e.g., writing '\' followed by 'n' when you see a newline, etc. After this transformation, you can either make strings go until end-of-line (meaning they are deliminated by '\n') or you can delimit them with '"'.
If you use C++-like escape-sequences for your string data, be sure to always use the same number of hex digits after '\x' and '\u'. I typically use 2 and 4 digits respectively. Reason: if you write a smaller number of hex digits, e.g., if you simply use stream << "\x" << hex << unsigned(theChar), you’ll get errors when the next character in the string happens to be a hex digit. E.g., if the string contains '\xF' followed by 'A', you should write "\x0FA", not "\xFA".
If you don’t use some sort of escape sequence for characters like '\n', be careful that the operating system doesn’t mess up your string data. In particular, if you open a std::fstream without std::ios::binary, some operating systems translate end-of-line characters.
Another approach for string data is to prefix the string’s data with an integer length, e.g., to write "now is the time" as 15:now is the time. Note that this can make it hard for people to read/write the file, since the value just after that might not have a visible separator, but you still might find it useful.
Text-based serialization/deserialization convention varies but one field per line is an accepted practice.
You'll have to develop a specific algorithm, since there is no separator character between the "fields".
static const std::string input_text = "johnmayer24ericclapton32";
static const std::string alphabet = "abcdefghijklmnopqrstuvwxyz";
static const std::string decimal_digit = "0123456789";
std::string::size_type position = 0;
std::string artist_name;
position = input_text.find_first_not_of(alphabet);
if (position != std::string::npos)
{
artist_name = input_text.substr(0, position - 1);
}
else
{
cerr << "Artist name not found.";
return EXIT_FAILURE;
}
Similarly, you can extract out the number, then use std::stoi to convert the numeric string to internal representation number.
Edit 1: Splitting the name
Since there is no separator character between the first and last name, you may want to have a list of possible first names and use that to find out where the first name ends and the surname starts.

User's null-terminated input

just a quick question. I'm looking for the most efficient and clear way to get the user's input of any length and store it, so then I can retrieve it and compare to another input. I also need the user's input to be null-terminated. Can I write something like
string inp;
cin >> inp;
A std::string will manage itself and grow to accommodate the input that is given to the program. If you have
std::string input;
std::getline(std::cin, input);
This will get input from the user that includes spaces that can be as big as the input stream can hold. Now that you have the string if you need to pass it to some function that needs an old null terminated c-style string then you would use the c_str() function. c_str() does return a const char * so you will not be able to modify the string data with it.
If you really need a modifyable c-style string then you can make one with
char * old_style_string = new char[input.size() + 1];
std::strcpy(old_style_string, input.c_str());

Use C++ strings in file handling

How to use C++ strings in file handling? I created a class that had C++ string as one of its private data members but that gave an error while reading from the file even if I am not manipulating with it at the moment and was initialised with default value in constructor. There is no problem while writing to the file. It works fine if I use C string instead but I don't want to. Is there a way to solve this?
class budget
{
float balance;
string due_name,loan_name; //string objects
int year,month;
float due_pay,loan_given;
public:
budget()
{
balance=0;
month=1;
due_name="NO BODY"; //default values
loan_name="SAFE";
year=0;
balance = 0;
due_pay=0;
loan_given=0;
}
.
.
.
};
void read_balance() //PROBLEM AFTER ENTERING THIS FUNCTION
{
system("cls");
budget b;
ifstream f1;
f1.open("balance.dat",ios::in|ios::binary);
while(f1.read((char*)&b,sizeof(b)))
{ b.show_data();
}
system("cls");
cout<<"No More Records To Display!!";
getch();
f1.close();
}
String is non-POD data-type. You cannot read/write from/in string by read/write functions.
basic_istream<charT,traits>& read(char_type* s, streamsize n);
30 Effects: Behaves as an unformatted input function (as described in
27.7.2.3, paragraph 1). After constructing a sentry object, if !good() calls setstate(failbit) which may throw an exception, and return.
Otherwise extracts characters and stores them into successive
locations of an array whose first element is designated by s.323
Characters are extracted and stored until either of the following
occurs: — n characters are stored; — end-of-file occurs on the input
sequence (in which case the function calls setstate(failbit | eofbit),
which may throw ios_base::failure (27.5.5.4)). 31 Returns: *this.
There is nothing about, how members of std::string placed. Look at, or use boost::serialiation. http://www.boost.org/doc/libs/1_50_0/libs/serialization/doc/index.html And of course you can write size of string and then write data and when read - read size, allocate array of this size, read data in this array and then create string. But use boost is better.
While reading the string members (due_name,loan_name) of your class budget your code literally fills them byte by byte. While it makes sense for floats and ints it won't work for strings.
Strings are designed to keep 'unlimited' amount of text, therefore their constructors, copy constructors, concatenations and so on must ensure to allocate the actual piece of memory to store the text and expand it if necessary (and delete upon destruction). Filling strings this way from disk will result in invalid pointers inside your string objects (not pointing to the actual memory which contains the text), actually no text will be actually read this way at all.
The easiest way to solve this is to not use C++ strings in that class. Work out the maximum length for each of the strings you will be storing, and make a char array that is one byte longer (to allow for the 0-terminator). Now you can read and write that class as binary without worrying about serialization etc.
If you don't want to do that, you cannot use iostream::read() on your class. You will need member functions that read/write to a stream. This is what serialization is about... But you don't need the complexity of boost. In basic terms, you'd do something like:
// Read with no error checking :-S
istream& budget::read( istream& s )
{
s.read( (char*)&balance, sizeof(balance) );
s.read( (char*)&year, sizeof(year) );
s.read( (char*)&month, sizeof(month) );
s.read( (char*)&due_pay, sizeof(due_pay) );
s.read( (char*)&loan_given, sizeof(loan_given) );
size_t length;
char *tempstr;
// Read due_name
s.read( (char*)&length, sizeof(length) );
tempstr = new char[length];
s.read( tempstr, length );
due_name.assign(tempstr, length);
delete [] tempstr;
// Read loan_name
s.read( (char*)&length, sizeof(length) );
tempstr = new char[length];
s.read( tempstr, length );
loan_name.assign(tempstr, length);
delete [] tempstr;
return s;
}
ostream& budget::write( ostream& s )
{
// etc...
}
Notice above that we've serialized the strings by writing a size value first, and then that many characters after.

Getting input from user using cin [duplicate]

This question already has answers here:
C++ "cin" only reads the first word [duplicate]
(5 answers)
Closed 4 years ago.
I am using Turbo C++ 3.0 Compiler
While using the following code ..
char *Name;
cin >> Name;
cout << Name;
When I gave input with space ... its only saving characters typed before space ..
like if I gave input "QWERT YUIOP" ... Name will contain "QWERT";
Any explaination why ??
When cin is used to read in strings, it automatically breaks at whitespace unless you specify otherwise.
std::string s;
std::cin >> noskipws >> s;
Alternatively, if you want to get a whole line then use:
std::getline(cin, s);
You'll also want to allocate storage for a raw char array, but with C++ you should use std::string or std::wstring anyway.
You need to allocate space for the char array into which you want to read the Name. char *Name; will not work as it only declares a char pointer not a char array. Something like char Name[30];
Also the cin << only allows us to enter one word into a string (char name[30]).
However, there is a cin function that reads text containing blanks.
cin.get(name, MAX)
get will read all characters including spaces until Max characters have
been read or the end of line character (‘\n’) is reached and will put them
into the name variable.
You just declared a character pointer that doesn't point at anything. You need to allocate space for your string. The most common method would be to allocate space on the stack, IE:
char Name[50];
Remember a char pointer by itself is just a place to put an address to where the real memory is. You still have to get a block of memory and store the address in your pointer. The code above creates an array of Names on the stack and you can use Name to store up to 49 chars plus a null terminal.
Alternatively, for variable length strings use std::string.