Reading multiple items in data file C++ - c++

I'm a C++ beginner. I'm trying to read a file that is formatted like so:
5 Christine Kim # 30.00 3 1
15 Ray Allrich # 10.25 0 0
...
number string # number number number
where each number has its own significance and the name does as well. I can get the file open and read the first two items but after the name I can't get the numbers after. This is my function right now:
void getItems( ifstream& dataFile, // in file
Employee item[], // class array so I can store the data later
int &transNum) // number of transactions
{
int id; // employee ID
char name[20]; // employee name
double hourlyPay; // pay per hour
int numDeps; // number of dependents
int type; // employee type
transNum = 0;
dataFile >> id;
dataFile.ignore(); // discard space before name
dataFile.getline( name, '#');
dataFile >> hourlyPay >> numDeps >> type;
}
I need it to read the first number, read the name, then read the last 3 numbers. After every name (maximum 20 characters) there is a # symbol where we should stop reading the name. I've tried adjusting the size of my char array for my string and other small fixes but nothing works. I realize that I will only get 1 line right now... I was just trying to get the first line to work before I made a loop to grab the other lines.

istream:getline needs a length parameter when used with a char array. Currently it is using '#' as the length value and using the default delimiter.
Change your code to
dataFile.getline( name, sizeof name, '#');
alternatively, use a std:string as the parameter to getline, then you don't need to specify a maximum size.

You're very close. The problem is likely to be the line
dataFile.ignore();
By default, ignore ignores everything up to EOF. What you want to do instead is ignore up to some number of characters until the next space. So that call would instead be:
datafile.ignore(100, ' ');
Where 100 is a purely arbitrary choice of number. Substitute your own rational value.
Next is your use of getline which must be told how many characters to read. Since your buffer is 20 characters, you need to inform getline like so:
getline(name, 20, '#');

Related

How to keep taking 2 integers as input until program encounters a single integer?

I was given a question where the input will be like:
10 8
4 9
6 12
5 4
3
1
Here I don't know the number of lines that contains 2 integers. Those sets of 2 integers will be taken into an array. But when the program encounters "3", it will start taking input in another array.
I have tried this with
while(cin>>a>>b){ //some porcess with a and b }
but it doesn't work because it recognizes 3 and 1 as another set of two integers. Please help me to solve this problem.
cin >> a >> b skips not only spaces, but any delimeter characters too ('\n', '\t', ' ').
Here you actually may want to read input line-by-line and then check if there are two integers or one. Consider use of std::getline for retrieving each line of text. Then you can use read string as std::istream (like in example in the link above) and read from it with counting, how many numbers you read totally.
So think about your problem. Essentially it is, read one line at a time, and if it contains two numbers do one thing, but if it contains one number do something else.
But the code you have written reads numbers not lines. That is where the problem is.
Instead write your code to read only line at a time, analyse that line to see if it contains one or two numbers (or something else) and then proceed from there.
What you need is the ability to read a line of text into a string, and then read from that string into your numbers. To do that you use an istringstream. Something like this
#include <iostream>
#include <sstream>
#include <string>
int a, b;
string s;
getline(cin, s); // read one line from standard input
istringstream line(s); // put that string to a stream we can read from
if (line >> a) // try and read the first number from the stream
{
// got the first number
if (line >> b) // try and read the second number from the stream
{
// got the second number
...
}
else
{
// only one number
...
}
}
else
{
// didn't get any numbers, some sort of error
...
}

Reading integer then rest of the line as a single string from input

Suppose I'm trying to read from the following input.txt
6 Jonathan Kim Jr
2 Suzie McDonalds
4 Patty
... and I want to store the first integers from every line and the rest of the strings as a string variable. This is what I have tried:
int num;
string name1, name2, name3;
while ( ins >> num >> name1 >> name2 >> name3 )
{
// do things
}
Unfortunately this won't work since line 2 and line 3 only has 2 and 1 strings in a respective order so the loop will terminate at the very first loop.
Is there a way to store the rest of the strings after an integer in a single variable, including the white spaces? For example, string variable name would hold:
"Jonathan Kim Jr" // first loop
"Suzie M" // second loop
"Patty" // third loop
I thought about using getline to achieve this as well, but that would require me to isolate the integers from the string and I was hoping there's a better approach do this. Perhaps using a vector?
By default, the >> operator splits on a space, so you could use that to pull the integer into the variable. You could then use getline to grab the rest of the line, and store that into the variable.
Example (if you are reading from std::cin):
int num;
std::string name;
std::cin >> num;
std::getline(std::cin, name);
Why don't use regular expressions (https://en.wikipedia.org/wiki/Regular_expression) to parse (https://en.cppreference.com/w/cpp/regex) the input? I think an expression something along the line of 2 subexpressions:
'^([0-1]+)\s([A-Za-z0-9_-/s]+)'

how to ignore n integers from input

I am trying to read the last integer from an input such as-
100 121 13 ... 7 11 81
I'm only interested in the last integer and hence want to ignore all
previous integers.
I thought of using cin.ignore but that won't work here due to
unknown integers (100 is of 3 digits, while 13 is of 2 digits & so on)
I can input integer by integer using a loop and do nothing with them. Is there a better way?
It all depends on the use case that you have.
Reading a none specified number of integers from std::cin is not as easy at it may seem. Because, in contrast to reading from a file, you will not have an EOF condition. If you would read from a file stream, then it would be very simple.
int value{};
while (fileStream >> value)
;
If you are using std::cin you could try pressing CTRL-D or CTRL-Z or whatever works on your terminal to produce an EOF (End Of File) condition. But usually the approach is to use std::getline to read a complete line until the user presses enter, then put this line into a std::istringstream and extract from there.
Insofar, one answer given below is not that good.
So, next solution:
std::string line{};
std::getline(std::cin, line);
std::istringstream iss{line};
int value{};
while (iss >> value)
;
You were asking
Is there a better way?
That also depends a little. If you are just reading some integers, then please go with above approach. If you would have many many values, then you would maybe waste time by unnecessarily converting many substrings to integers and loose time.
Then, it would be better, to first read the complete string, then use rfind to find the last space in the string and use std::stoi to convert the last substring to an integer.
Caveat: In this case you must be sure (or check with more lines of code) that there are no white space at the end and the last substring is really a number. That is a lot of string/character fiddling, which can most probably avoided.
So, I would recommend the getline-stringstream approach.
You can try this simple solution for dynamically ignoring rest of the values except the last given in this problem as shown:
int count = 0;
int values, lastValue; // lastValue used for future use
std::cout << "Enter your input: ";
while (std::cin >> values) {
lastValue = values; // must be used, otherwise values = 0 when loop ends
count++;
}
std::cout << lastValue; // prints
Note: A character must be required to stop the while(), hence it's better put a . at last.
Output example
Enter your input: 3 2 4 5 6 7.
7
Try this:
for( int i=0; i<nums_to_ignore; i++) {
int ignored;
std::cin >> ignored;
}

C++ check if the last thing read from file was a number

this is quite a primitive problem, so I guess the solution shouldn't be hard, but I didn't find a way how to do it simply, neither have I summarized it to actually find it in the internet.So going to the question, I have a file of information like this:
1988 Godfather 3 33 42
1991 Dance with Wolves 3 35 43
1992 Silence of the lambs 3 33 44
And I have a requirement to put all the information in a data structure, so lets say it will be int year, string name and three more int types for numbers. But how do I know if the next thing I read is a number or not? I never know how long is the word.Thank you in advance for anyone who took their time with such a primitive problem. :)
EDIT: Don't consider movies with numbers in their title.
You're going to have some major issues when you go to try to parse other movies, like, Free Willy 2.
You might try instead to treat it as a std::stringstream and rely on the last three chunks being the data you're looking for rather than generalizing with a Regular Expression.
your best bet would be to use C++ regex
That would give you a more fine grained control over what you want to parse.
examples:
year -> \d{4}
word -> \w+
number->\d+
If you do not have control over the file format, you may want to do something along these lines (pseudo-process):
1) read in the line from the file
2) reverse the order of the "words" in the file
3) read in the 3 ints first
4) read in the rest of the stream as a string
4) reverse the "words" in the new string
5) read in the year
6) the remainder will be the movie title
Read every field as a string and then convert the appropriate string to integers.
1)initially
1983
GodFather
3
33
45
are all strings and stored in a vector of strings (vector<string>).
2)Then 1983(1st string is converted to integer using atoi) and last three strings are also converted to integers. Rest of the strings constitute the movie_name
Following code has been written under the assumption that input file has already been validated for the format.
// open the input file for reading
ifstream ifile(argv[1]);
string input_str;
//Read each line
while(getline(ifile,input_str)) {
stringstream sstr(input_str);
vector<string> strs;
string str;
while(sstr>>str)
strs.push_back(str);
//use the vector of strings to initialize the variables
// year, movie name and last three integers
unsigned int num_of_strs = strs.size();
//first string is year
int year = atoi(strs[0].c_str());
//last three strings are numbers
int one_num = atoi(strs[num_of_strs-3].c_str());
int two_num = atoi(strs[num_of_strs-2].c_str());
int three_num = atoi(strs[num_of_strs-1].c_str());
//rest correspond to movie name
string movie_name("");
//append the strings to form the movie_name
for(unsigned int i=1;i<num_of_strs-4;i++)
movie_name+=(strs[i]+string(" "));
movie_name+=strs[i];
IMHO Changing delimiters in the file from space to some other character like , or ; or : , will simplify the parsing significantly.
For example , if later on the data specifications change and instead of only last three , either last three or last four can be integers then the code above will need major refactoring.

cin.ignore() is not working in program

My program is suppose to output First Middle Last name and ignore the , that is in the input. But in my program the comma is still in my output so obviously I am missing something.
#include <iostream>
#include <string>
using namespace std;
char chr;
int main()
{
string last, first, middle;
cout<< "Enter in this format your Last name comma First name Middle name."<<endl; //Input full name in required format
cin>>last; //receiving the input Last name
cin>>first; //receiving the input First name
cin>>middle; //receiving the input Middle name
cout<<first<<" "<<middle<< " " <<last; //Displaying the inputed information in the format First Middle Last name
cin.ignore(','); //ignoring the , that is not neccesary for the new format
cin>>chr;
return 0;
}
The ignore function acts on the current input stream (e.g. cin), and it discards as many characters as indicated in the first argument, until it finds the delimiter given as the second argument (default as EOF).
So, the way you have it, cin.ignore(','); will ignore 44 characters until EOF, after you have printed the inputs given. This is almost certainly NOT what you wanted to do.
If you want to skip past a comma, then you will want to call cin.ignore(100, ','); between the input of the last name and the input of first name. That will skip to the next comma in the input (up to 100 characters).