I have a text file with a line like:
James Dean 10 Automotive 27010.43
and I need to read that file and put each of the 4 above into arrays.
char nameArray[MAX][NAME_MAX];
int yearArray[MAX];
char departmentArray[MAX][DEP_MAX];
double payArray[MAX];
while(i < MAX && infile) {
infile.getline(nameArray[i], 20);
infile >> yearArray[i];
infile.getline(departmentArray[i], 15);
infile >> payArray[i];
cout << nameArray[i] << " " << yearArray[i] << " " << departmentArray[i] << " " << fixed << setprecision(2) << payArray[i] << endl;
i++;
}
The code is cut down just to give you an idea of what I am trying to do, but when I run this, I get something like:
James Dean -858993460 -92559631349317830000000000000000000000000000
000000000000000000.00
Thanks for the help.
==== Edit ==========================================
I changed from getline to get, thanks for that. I have to use get and not >> because some of the lines I am reading in are more than just "James Dean", they are up to 20 char long...ex: "William K. Woodward" is another one.
So, if I just use get, then it reads the first line in fine, but then I get the same messed up text for the second line.
Here is the code:
infile.get(nameArray[i], 20);
infile >> yearArray[i];
infile.get(departmentArray[i], 15);
infile >> payArray[i];
The getline functions takes an input stream and a string to write to. So, two getline calls read in two lines. Your input mechanism is broken. Either, use getline or the stream extraction operator (i.e. >>) but not both.
If you plan to use getline you need to parse the string (which is effectively one line of input) into tokes, and then store them in appropriately typed arrays. The second and fourth tokens are numbers, hence you will need to convert these from string to int or double.
The operator >> approach:
string name, surname;
int year;
double pay;
while (infile) {
infile >> name >> surname >> year >> department >> pay;
namearray[ i ] = name + " " + surname;
// ...
payarray[ i ] = pay;
++i;
}
The getline approach:
string line;
while (getline(infile, line)) {
parse(line, tokens);
namearray[ i ] = token[ 0 ] + " " + token[ 1 ];
// ...
payarray[ i ] = strTodouble(token[ 4 ]);
++i;
}
// parse definition
void parse(string line, vector<string>& token) {
// roll your own
}
double strToDouble(string s) {
// ...
}
I dont see where you define infile but I will assume that it is an ifile . In that case you should use it the same way u use cin to get input.
Why do you do a getline () ?
That function will stop only at an '\n' char or at an EOF char. So it means, you start reading the int after the end of the line, some random data.
Correct me if i'm wrong, but are there 20 or 19 characters in that first string (James Dean) before the number (10) ?
Related
I am trying to read in the data in a names.txt file and output the full name and ideal body weight for each person. Using a loop to read the names and feet and inches of each person from the file.
The file reads:
Tom Atto
6
3
Eaton Wright
5
5
Cary Oki
5
11
Omar Ahmed
5
9
I'm using the following code for this:
string name;
int feet, extraInches, idealWeight;
ifstream inFile;
inFile.open ("names.txt");
while (getline(inFile,name))
{
inFile >> feet;
inFile >> extraInches;
idealWeight = 110 + ((feet - 5) * 12 + extraInches) * 5;
cout << "The ideal weight for " << name << " is " << idealWeight << "\n";
}
inFile.close();
when i run this im getting output:
The ideal weight for Tom Atto
is 185
The ideal weight for
is -175
Add this statement in while loop after reading the two extraInches value.
inFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
It ignores the '\n' after the second integer you read in while loop. You may refer: Use getline and >> when read file C++
You are running into problems because after the line
inFile >> extraInches;
is executed in the first iteration of the loop, there is still a newline character in the stream. The next call to getline simply returns an empty line. The subsequent call
inFile >> feet;
fails but you don't check whether the call was successful.
Couple of things I want to mention in relation to your problem.
Mixing unformatted input, using getline, and formatted input, using operator>> is fraught with problems. Avoid it.
To diagnose IO related problems, always check the state of the stream after an operation.
In your case, you can use getline to read lines of text, and then use istringstream to extract numbers from the lines.
while (getline(inFile,name))
{
std::string line;
// Read a line of text to extract the feet
if ( !(inFile >> line ) )
{
// Problem
break;
}
else
{
std::istringstream str(line);
if ( !(str >> feet) )
{
// Problem
break;
}
}
// Read a line of text to extract the inches
if ( !(inFile >> line ) )
{
// Problem
break;
}
else
{
std::istringstream str(line);
if ( !(str >> inches) )
{
// Problem
break;
}
}
idealWeight = 110 + ((feet - 5) * 12 + extraInches) * 5;
cout << "The ideal weight for " << name << " is " << idealWeight << "\n";
}
I am trying to read a line of string characters with numbers (e.g "30 40 50 20") and put them into a vector. I also need to avoid empty space and newlines. But when I read the input, it doesn't see the string "30", it sees the characters "3" and "4".
void Input() {
getline(cin,line, '\n');
for (int i = 0; i < line.length(); i++) {
if (! (isspace(line[i]))) {
cout << line[i] << ", ";
scores.push_back(line[i]);//(atoi(input));
}
}
cout << scores.size() << "! ";
}
A line like "30 40 50" won't give a vector size of 3, it will give a size of 6.
What are the optimal ways to get around this issue?
EDIT: I should have clarified in the original message that this is for a challenge, in which I am unable to include the string stream library in the original case.
I think you're doing the right thing grabbing the whole line before parsing, otherwise you get into a bit of a pickle. But you do actually have to do some parsing. Right now you're just pulling out individual characters.
The following isn't optimal but it'll get you started — continue using formatted stream extraction, but isolated to this line from the file.
So:
void Input()
{
getline(cin, line, '\n');
istringstream ss(line);
int val;
while (ss >> val)
scores.push_back(val);
cout << scores.size() << "! ";
}
Read the line and put into a std::istringstream, then read as "normally" using the >> operator from the string stream.
Putting the line into a std::istringstream and extracting the numbers from that is the best way.
Here's an alternative to a manual loop using the standard library:
std::istringstream numbers(line);
std::copy(std::istream_iterator<int>(numbers),
std::istream_iterator<int>(),
std::back_inserter(scores));
It is probably best to take advantage of an input stringsteam, example: http://www.cplusplus.com/reference/sstream/stringstream/stringstream/.
The extraction operator allows you to parse data from the stream to some variable of datatype T. Another advantage of input stringstreams is the ability to query whether the pass was successful, and in your case ignore whitespace characters by setting the skipws format flag.
Example:
int main () {
std::istringstream ss("30 40 50");
float val = 0.0f;
while( ss >> std::skipws >> val )
{
std::cout << val << "\n";
}
return 0;
}
Out: 30 40 50
I have a file I need to read in that looks somethig like this:
1 2.23 Dove Body Wash
3 .50 Bic Pen
11 12.99 Tombstone Pizza
Where the field with the names of the products can have either only one word (Shampoo) or any number of words (Big mama's homestyle steak fries). The input file can also have blank lines, which I just need to skip over.
So what I have right now looks like this (using getline and stringstream):
This struct:
struct CartItem {
string itemName;
int quantity;
double pricePerItem;
CartItem();
CartItem(string name, int qty,double price);
};
and then this code.
while (getline(itemList, inputline)) { //itemlist is my ifstream, inputline is a string declared.
ss.clear();
ss.str(inputline);
ss >> item.quantity >> item.pricePerItem >> item.itemName;
string word;
while (ss >> word)
{
item.itemName += " " + word;
}
if (ss.fail()) {
continue;
}
else
shoppingList.push_back(item);
}
sortItems(shoppingList, sortedList);
printReport(sortedList);
but it's not working (just crashing). If I replace the while(ss >> word) fragment with if(ss>>word) it works but I only get the second string from the file. Can anyone see what I'm doing wrong here?
You should provide more details, e.g. what "just crashing" means. However, I think you have problem with your logic.
ss >> word will be called on the final check in the while loop, when ss is already out of words, thus the word will fail to be extracted to word when the while loop exits, setting ss's fail bit. Now, ss.fail() will always be true and you will never add items to your list.
I am guessing that you later rely on shoppingList having at least one item and this is the crash.
Try changing:
while (ss >> word)
{
item.itemName += " " + word;
}
to:
while (ss.good())
{
ss >> word;
item.itemName += " " + word;
}
I am a beginner at C++ and I'm trying to use the getline() function for the first time.
When I wrote this code, 2 errors showed up.
What is this code supposed to do?
It is supposed to read 4 numbers from read.txt then calculate it to find the mean and write the output in output.txt.
The 4 numbers (in read.txt) are all on separate lines like this:
6
12
15
19
Here is the code:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main ()
{
ifstream readFile;
ofstream sendFile;
readFile.open ("read.txt");
sendFile.open ("output.txt");;
float mean;
int num, num2, num3, num4;
getline(readFile, num), getline(readFile, num2), getline(readFile, num3), getline(readFile, num4);
readFile >> num >> num2 >> num3 >> num4;
sendFile << "1. The mean of " << num << ", " << num2 << ", " << num3 << ", and " << num4 << "is " << (num + num2 + num3 + num4) / 4;
readFile.close();
sendFile.close();
system ("PAUSE") ;
return 0;
}
Here are the errors:
IntelliSense: no instance of overloaded function "getline" matches the argument list 20
IntelliSense: too few arguments in function call 20
std::getline() takes two arguments: a stream and the std::string object into which to read the next line (and an optional third argument, the delimiter). You are passing an int instead of a std::string.
You should probably use the ordinary formatted extraction:
if (readFile >> num >> num2 >> num3 >> num4) {
// extraction succeeded!
}
else {
// extraction failed; handle the error here
}
getline reads into std::string, it can't read into ints. Just use readFile >> num >> num2 >> num3 >> num4; as you already have, and delete the line with getlines.
On another note, you don't need to close the files explicitly here as the destructors of file stream objects will take care of that for you.
std::getline is a useful tool for reading a single line of text or reading text up to a specific character, and writing it to a std::string where it can then be read further. By default it uses newline i.e. '\n' as the delimiter but you can change this.
With regards to using a stream to read in a number of integers then output their mean, why not just read to the end of the file, thus:
int count = 0, total = 0, num;
while( instr >> num )
{
++count;
total += num;
}
float mean = (count > 0 ) ? total / num : std::numeric_limits<float>::quiet_NaN();
outstr << mean;
You could make that a function, taking istream & instr and ostream & outstr
Suppose now we want to change that to read multiple lines, each with numbers delimited by space or tab. In our output we write all the means on their own line.
Now do something like this:
std::string line;
while( std::getline( bigInStr, line ) )
{
std::istringstream iss(line);
outputMean( iss, outstr );
outstr << '\n';
}
although you might want to not actually output the NaN but just leave that line blank in the output. A function calculating the mean probably would want to use a NaN as a return value if it has to return a float. We could calculate the variance, skewness and kurtosis at the same time if we want whilst iterating.
Then you would output these as multiple values on the line and you would have to pick your own delimiter. My own preference is to use tab ('\t') in this situation.
_
I have a text file that I am inputting data in from, but I can't seem to get it right.
Here are two lines from the text file as an example (these aren't 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 the text file and put the data into a struct. I am still new to C++(obviously) and I'm having a hard time using get and >> together. The code I have below, works fine 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 the state at 8 char was just me messing around and then I forgot to change it back...sorry.
The problem is istream::get() breaks for streetAddress which has spaces in it.
One way is to tokenize the input line first into say, a vector of strings and then depending on the number of tokens convert these to appropriate fields of your 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'd rather a stream extraction 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;
}
You're getting 8 characters for State, which includes all your zipcode, and is larger than your field.
It'd also be tempting to use the skipws operator:
infile >> skipws >> CT_Struct.firstName
>> CT_Struct.lastName
>> ... ;
(Update: that's what I get for doing that from memory. This is more closely approximating correct.)
If I were you I would start again from scratch. I would:
use std::strings instead of character arrays for your data
reads line at a time from the file using std::getline
parse the line up using a stringstream
avoid mixing formatted and unformatted input
My approach to this would be the following:
1) Read each line into a null terminated buffer.
2) Use a split() function that you're gonna have to write. This function should take a string as its input and return a list. It should also take a separator. The separator in this case is ' '.
3) iterate over the list carefully (are there never middle names?) What about 1 word, or 3 word street names? Since many of these columns are really variable in number of words, and you have no seperator other than whitspace, this may prove a fairly tough task. If you NEVER have middle names, you could assume the first two columns are first and last name. You know for sure what the last two are. Everything between them could be assigned to a single address field.