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.
Related
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.
If we take the input from user by asking them, like below:
cout << "Enter your course code and course name: ";
Now if the user enters CS201 Introduction to Programming, how can I only assign the code part, i.e. CS201 to an array, let's say;
char courseCode[10];
And how can I assign the name part in the array, let's say:
char courseName[50];
I want to do this to 5 students, using the structure defined below:
struct student
{
char courseName[50];
char courseCode[10];
};
student stu[5];
It's actually kind of simple once you remember that the input operator >> stops on white-space, and also know about the std::getline function.
Then you can do something like
std::string courseCode;
std::string courseName;
std::cin >> courseCode;
std::getline(std::cin, courseName);
Note that I use std::string for the strings instead of arrays. This is what you really should use. If you're not allowed (by your teacher or something) and must use arrays, then you can't use std::getline but instead have to use std::istream::getline instead.
Store the input in a single string say x
Now on x perform linear search for the first whitespace and split the string about the first whitespace. Store the two resultant strings in your struct.
I solved my problem using the cin.getline() functions to get the string in token pointer and then used strchr(char [], cahr) of <string> header file to separate the current string from the place where the first white space comes. Then I copied both separated strings into my desired elements of my structure using strcpy() function.
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());
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).
4;
Spadina;76 156
Bathurst;121 291
Keele;70 61
Bay;158 158
This is what file contains in it. I need to read them and save them into variables.
4 is for dynamic memory allocation. 4 means there are 4 stations.
Spadina, Bathrust, etc.. they are station names. first number, which comes right after station names, is number of student passes and the second number is number of adult pass.
So, basically I have 4 variables and they are;
int numberOfStation;
int studentPass;
int adultPass;
string stationName;
I spent 4 hours but still cannot read the file and save it into variable
Thank you.
A possible solution is to read every line with e.g. std::getline then parse each such line string. You'll use the appropriate methods of std::string to search inside it (with find) and split it (with substr). You might also access some individual character in that string using at or the [] operator of std::string; alternatively, you might perhaps parse each line -or relevant parts of them- using std::istringstream (I am not sure it is appropriate in your case). You might be interested by std::to_string...
An important thing is to define exactly (not only thru examples) the possible acceptable inputs. You could for example use some EBNF to formalize that. You should probably care about character encoding (try first by assuming a simple single-byte encoding like ASCII, then later consider UTF-8 if your system uses it).
For example, can the station names (I guess you talk about subway stations) contain digits, or spaces, or underscores, or commas, etc.... ? Could they be French names like Hôtel de Ville (a metro station in central Paris) or Saint-Paul or Bourg-La-Reine (where I am), or Russian names like Молодёжная in Moscow? (I guess that station names in Tokyo or in Jerusalem might be even funnier to parse).
BTW, explicitly entering the number of entries (like your initial 4) is very user-unfriendly. You could have some lexical conventions and e.g. use some tagging or separators.
At last, you might want to keep the information for every travel. Then you'll probably need to define some struct or class (not simply four scalar variables). At that point your program is becoming more interesting!
First group your variables in struct.
struct MyStruct
{
int studentPass;
int adultPass;
string stationName;
};
Now read size of struct in file and allocate it dynamically
MyStruct *p;
s >> N;
p = new MyStruct[N];
Now in for loop you read string with delimiter ';' and other two vars are ints
for (int i = 0; i < N; i++)
{
getline(s, p[i].stationName, ';');
s >> p[i].studentPass >> p[i].adultPass;
}
Where var s is istream type of variable with flag std::in
I recommend that you create a struct to hold your stations:
struct station{
string _stationName;
int _studentPass;
int _adultPass;
};
Then create an operator to use with your struct (props to Sly_TheKing for the getline idea):
std::istream& operator>>(std::istream& is, station& rhs){
getline(is, rhs._stationName, ';');
is >> rhs._studentPass >> rhs._adultPass >> ws;
return is;
}
Say that your ifstream is called foo. You can read these into a vector like this:
foo.ignore(numeric_limits<streamsize>::max(), '\n');
vector<station> bar{istream_iterator<station>(foo), istream_iterator<station>()};