C ++ using get and >> for ifstream
I have a text file from which I am entering data, but I cannot figure out what is correct.
Here are two lines from a text file as an example (these are not real people, don't worry):
Michael Davidson 153 Summer Avenue Evanston CO 80303
Ingrid Johnson 2075 Woodland Road Aurora IL 60507
Here is the code I have to load a text file and put the data into a structure. I am still new to C ++ (obviously) and find it difficult to work with get and -> together. The code I have below works great until I get to the "state" and then something goes wrong. Thanks for the help!
//constants
const int FIRST_NAME_LEN = 11;
const int LAST_NAME_LEN = 13;
const int ADDRESS = 25;
const int CITY_NAME_LEN = 16;
const int STATE_LEN = 3;
//define struct data types
struct CustomerType {
char firstName[FIRST_NAME_LEN];
char lastName[LAST_NAME_LEN];
char streetAddress[ADDRESS];
char city[CITY_NAME_LEN];
char state[STATE_LEN];
int zipCode;
};
//prototype function
ifstream& getInfo(CustomerType& CT_Struct, ifstream& infile);
int main() {
//declare struct objects
CustomerType CT_Struct;
ifstream infile("PGM951_customers.txt");
if(!infile) {
cerr << "Could not open the input file." << endl;
exit(1); //terminates the program
}
//call the function
getInfo(CT_Struct, infile);
return 0;
}
ifstream& getInfo(CustomerType& CT_Struct, ifstream& infile) {
while(infile) {
infile.get(CT_Struct.firstName, sizeof(CT_Struct.firstName));
infile.get(CT_Struct.lastName, sizeof(CT_Struct.lastName));
infile.get(CT_Struct.streetAddress, sizeof(CT_Struct.streetAddress));
infile.get(CT_Struct.city, sizeof(CT_Struct.city));
infile.get(CT_Struct.state, sizeof(CT_Struct.state));
infile >> ws;
infile >> CT_Struct.zipCode;
cout << CT_Struct.firstName << " | " << CT_Struct.lastName << " | " << CT_Struct.streetAddress
<< " | " << CT_Struct.city << " | " << CT_Struct.state << " | " << CT_Struct.zipCode << endl;
}
return infile;
}
=== edit =========== Reading in a state at 8 char was just me messing around and then I forgot to change it back ... sorry.
a source to share
The task istream::get()
breaks for streetAddress
that contains spaces.
One way is to tokenize the input string first, say vector
of strings
, and then, depending on the number of tokens, convert them to the appropriate fields of yours CustomerType
:
vector<string> tokenize(string& line, char delim=' ') {
vector<string> tokens;
size_t spos = 0, epos = string::npos;
while ((epos = line.find_first_of(delim)) != string::npos) {
tokens.push_back(line.substr(spos, epos - spos));
spos = epos;
}
return tokens;
}
I would prefer the stream fetch operator for CustomerType
:
struct CustomerType {
friend istream& operator>>(istream& i, CustomerType& c);
string firstName, lastName, ...;
// ...
};
istream& operator>>(istream& i, CustomerType& c) {
i >> c.firstName >> c.lastName;
string s1, s2, s3;
i >> s1 >> s2 >> s3;
c.streetAddress = s1 + s2 + s3;
i >> c.city >> c.state >> c.zipCode;
return i;
}
a source to share
You get 8 characters for the status, which includes your entire zip code and more of your field.
It would also be tempting to use the operator skipws
:
infile >> skipws >> CT_Struct.firstName
>> CT_Struct.lastName
>> ... ;
(Update: This is what I get for this, from memory. This more closely approximates correctly.)
a source to share
My approach to this would be as follows:
1) Read each line into a null terminated buffer. 2) Use the split () function which you will need to write. This function should take a string as its input and return a list. It must also take a separator. The separator in this case is equal to ''. 3) iterate over the list carefully (are there any middle names?) How about one word or three words of the street name? Since many of these columns are indeed variable in word count and you have no other than whitspace, this can be quite a daunting task. If you NEVER have middle names, you can assume the first two columns are first and last names. You know exactly what the last two are. Everything in between can be assigned to one address field.
a source to share