Splitting input string c++ [duplicate] - c++

This question already has answers here:
How do I iterate over the words of a string?
(84 answers)
Closed 6 years ago.
I am reading in a file, that contains data in this format on each line. 30304 Homer Simpson I need to be able to pass this to the following constructor, the integer being the regNo, the name the rest of the string, and every student would have their own map of marks.
Student::Student (string const& name, int regNo):Person(name)
{
regNo = regNo;
map<string, float> marks;
}
I then have to add each student to a collection of students, which would be best, and how do I do this?
So far all I've got is getting the file name and checking it exists.
int main()
{
//Get file names
string studentsFile, resultsFile, line;
cout << "Enter the Students file: ";
getline(cin, studentsFile);
cout << "Enter the results file: ";
getline(cin, resultsFile);
//Check for students file
ifstream students_stream(studentsFile);
if (!students_stream) {
cout << "Unable to open " << studentsFile << "\n";
return 1;
}
}
I tried using getline with 3 arguments and " " as the delimiter but that would also split the name part of the string, so I'm not sure how to do this another way.

Replace std::cin with your input file stream of course. It would be probably sane to "trim" the name result, unless you know by 100% the input is well formatted. I added only bare-minimal error state handling to somehow "survive".
Names are read also for single/three/more variants of course, as any real world application should.
#include <iostream>
#include <string>
#include <stdexcept>
int main()
{
std::string line, name;
unsigned long long regNo;
size_t nameOfs;
while (true) {
// Read full non-empty line from input stream
try {
std::getline(std::cin, line);
if (line.empty()) break;
}
catch(const std::ios_base::failure & readLineException) {
break;
}
// parse values:
// 1. unsigned long long ending with single white space as "regNo"
// 2. remaining part of string is "name"
try {
regNo = std::stoull(line, &nameOfs);
name = line.substr(nameOfs + 1);
}
catch(const std::logic_error & regNoException) {
// in case of invalid input format, just stop processing
std::cout << "Invalid regNo or name in line: [" << line << "]";
break;
}
// here values regNo + name are parsed -> insert them into some vector/etc.
std::cout << "RegNo [" << regNo << "] name [" << name << "]\n";
}
}

A regular expression could be used:
We can then select group 2 and 3 from the result.
std::vector<Student> students;
std::regex r{R"(((\d+) )(.+))"};
for(std::string line; getline(students_stream, line);) {
auto it = std::sregex_iterator(line.begin(), line.end(), r);
auto end = std::sregex_iterator();
if(it == end || it->size() != 4)
throw std::runtime_error("Could not parse line containing the following text: " + line);
for(; it != end; ++it) {
auto match = *it;
auto regNo_text = match[2].str();
auto regNo{std::stoi(regNo_text)};
auto name = match[3].str();
students.emplace_back(name, regNo);
}
}
Live demo

You can take input using getline()and read one complete line(no third argument) and then use stringstream to extract the number and the remaining string. Example of stringstream:
string s = "30304 Homer Simpson", name;
stringstream ss(s);
int num;
ss >> num; //num = 30304
getline(ss, name); //name = Homer Simpson
cout << num;
cout << name;

Related

How I do split a string into 2 different variables (string and integer) at a certain point? C++

My program receives input from the user, in this exact form: string, integer. An example would be: Brady, 12. I need to split the string at the comma, and store the first part into a vector of strings, and the 2nd part into a vector of integers. How do I go about this? (Sorry, I'm very new to programming)
This is what my code currently looks like:
while(true) {
cout << "Enter a data point (-1 to stop input): " << endl;
getline(cin, dataPoint);
if (dataPoint == "-1") { //Ends Program
break;
}
if (dataPoint.find(",") == std::string::npos) {
cout << "Error: No comma in string." <<endl;
}
}
We wouldn't normally write your code for you, but I'm going to offer a few hints.
First, storing the data the way you're going to do is probably the wrong approach, unless it's required for your homework. I'd keep it related:
class Person {
... various methods
private:
std::string name;
int age;
}
I would parse the data into one of these and store THAT into a std::vector<Person>. That keeps your data together.
As for splitting the string:
size_t pos = str.find(",");
if (pos == string::npos) {
... no comma found
}
else {
string name = str.substr(0, pos);
string ageStr = str.substr(pos + 1);
}
There's more you have to do, of course, but that's how you can divide the string into two pieces.
Here is the solution:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
cout << "Enter a data point (-1 to stop input): " << endl;
string input;
getline(cin, input);
if (input == "-1") return 0;
int comma = input.find(","); //find separator
string name = input.substr(0, comma); //find name and nuber using this number
string snumber = input.substr(comma + 2); // comma itself and space
cout << "The name is: " << name << " The number is: " << snumber << endl;
int number = stoi(snumber); //switch string to int
vector<string> names;
vector<int> numbers;
names.push_back(name); // add to vector
numbers.push_back(number);
return 0;
}

C++ Cannot return all Variables that have been added to a Vector List

In the program im currently writing, im up to a stage that requires the use of data that has been read through a file. Some of this data contains number elements, so the proper conversions have been done in order to store them in a vector of type string. Up to this point works.
I have a function called robotComplexity, which calculates a value based on other values found in the file, which have already been added to a vector List(all code shall be posted below). The text file contains the following data.
A:Head:1:2:15.
B:Torso:0:6:5.
C:Leg:0:4:6.
D:Arm:0:4:8.
E:Tail:0:6:2.
As seen in the code below, this file has been split by a dilimiter at the colon and fullstop to first seperate the variables, then the records. The variables have been stored in their respective holders as shown in the code. The function called robotComplexity contains a for loop
for(std::size_t i=0; i< partsVector.size(); ++i) {
if(partsVector[i] == userChoice) {
cout << userChoice << stoi(stringMaximum) << endl;
}
The issues with my Code stem from this for loop. The program is able to loop through the file and recongise the first variable, partCode(converted to newChar) of respective values A,B,C,D,E. So for example when a user enters A it returns A, enters B returns B etc. Now my issues come from trying to print out other variables stored in the vector. In the cout << userChoice...etc line, it successfully returns the Letter(example A) but does not return the correct Value for stringMaximum converted to an int. The returned value is 0 where it should be whatever it equals for a partCode A(in this case)
Im asking if anyone could create/fix my for loop such that when cout << "Variable" is called, it can successfuly print to the console the value of that variable according to the partCode
For Example, if the user enters A as the partcode the output should be
code
cout << userChoice << partName << stringMaximum << stringMinimum << stringComplexity << endl;
output
A
Head
1
2
15
file containing functions
struct Part {
char partCode;
std::string partName;
int maximum;
int minimum;
int complexity;
} myPart;
std::vector<string> partsVector;
std::ifstream partsList("Parts.txt");
std::string outputFile = "output.txt";
std::string input;
std::string newChar;
std::stringstream convertChar;
std::string stringMaximum = std::to_string(myPart.maximum);
std::string stringMinimum = std::to_string(myPart.minimum);
std::string stringComplexity = std::to_string(myPart.complexity);
void readFile() //function to read Builders, Customers and Parts text file
{
std::string line;
while (std::getline(partsList, line)) {
line.pop_back();//removing '.' at end of line
std::string token;
std::istringstream ss(line);
convertChar << myPart.partCode;
convertChar >> newChar;
// then read each element by delimiter
int counter = 0;//number of elements you read
while (std::getline(ss, token, ':')) {//spilt into different records
switch (counter) {//put into appropriate value-field according to element-count
case 0:
newChar = token; //convert partCode from a char to a string
break;
case 1:
myPart.partName = token;
break;
case 2:
myPart.maximum =stoi(token);
break;
case 3:
myPart.minimum = stoi(token);
break;
case 4:
myPart.complexity = stoi(token);
break;
default:
break;
}
counter++;//increasing counter
}
partsVector.push_back(newChar);
partsVector.push_back(myPart.partName);
partsVector.push_back(stringMaximum);
partsVector.push_back(stringMinimum);
partsVector.push_back(stringComplexity);
}
}
double robotComplexity() {
double complexity;
string userChoice;
cout << "Enter a part code A ,B ,C ,D or E" << endl;
cin >> userChoice;
for(std::size_t i=0; i< partsVector.size(); ++i) {
if(partsVector[i] == userChoice) {
cout << userChoice << stoi(stringMaximum) << endl;
}
}
}
Thankyou for any help offered. If any furuther explaination is required please feel free to ask. PS I know global variables aren't the best to use, but once my functions operate correctly I will clean the code up with local variables.
There are several issues here, but your main problem is that
std::string stringMaximum = std::to_string(myPart.maximum);
std::string stringMinimum = std::to_string(myPart.minimum);
std::string stringComplexity = std::to_string(myPart.complexity);
are global variables, and not functions. They will be evaluated only once at the beginning of your program. So your readFile is already broken in the code that you gave us. The first thing I would do is remove the global state (i.e. remove all global variables) and fix the code that doesn't compile anymore.
You do
partsVector.push_back(stringMaximum);
partsVector.push_back(stringMinimum);
in readFile without ever setting those two variables so you always push the same value in your vector.
Next question is, why do you use a vector of string and not a vector of Part? You already parse the file to Part objects, so just use them. Furthermore, you want to access those parts via a user input that queries for Part.partCode, so we can use a lookup table with partCode as a key (std::unordered_map<char, Part> in this case).
All in all this would look like the following (the inner while loop in readFile was a nightmare too, so I removed that aswell):
#include <string>
#include <vector>
#include <sstream>
#include <fstream>
#include <iostream>
#include <unordered_map>
struct Part
{
char partCode = 0;
std::string partName;
int maximum = 0;
int minimum = 0;
int complexity = 0;
};
std::stringstream partsList(
R"(A:Head:1:2:15.
B:Torso:0:6:5.
C:Leg:0:4:6.
D:Arm:0:4:8.
E:Tail:0:6:2.)");
std::string outputFile = "output.txt";
std::string input;
std::unordered_map<char, Part> readFile() //function to read Builders, Customers and Parts text file
{
std::unordered_map<char, Part> parts;
std::string line;
while (std::getline(partsList, line))
{
line.pop_back(); //removing '.' at end of line
std::string token;
std::istringstream ss(line);
Part part;
std::getline(ss, token, ':');
part.partCode = token[0];
std::getline(ss, part.partName, ':');
std::getline(ss, token, ':');
part.maximum = std::stoi(token);
std::getline(ss, token, ':');
part.minimum = std::stoi(token);
std::getline(ss, token, ':');
part.complexity = std::stoi(token);
parts.emplace(part.partCode, std::move(part));
}
return parts;
}
double robotComplexity(std::unordered_map<char, Part> const& parts)
{
double complexity = 10;
char partCode;
std::cout << "Enter a part code A ,B ,C ,D or E" << std::endl;
std::cin >> partCode;
auto const& part = parts.at(partCode);
std::cout << part.maximum;
if (complexity > 100)
{
complexity = 100;
}
std::cout << "\nThe Robot Complexity is: " << complexity << std::endl;
return complexity;
}
void writeFile() //writes to a file output.txt the end calculations.
{
}
int main()
{
auto parts = readFile();
writeFile();
robotComplexity(parts);
return 0;
}

Simple string parsing without using boost [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Splitting a string in C++
I'm working on an assignment for my C++ class and I was hoping I could get some help. One of my biggest problems in coding with C++ is parsing strings. I have found longer more complicated ways to parse strings but I have a very simple program I need to write which only needs to parse a string into 2 sections: a command and a data section. For instance: Insert 25 which will split it into Insert and 25.
I was planning on using an array of strings to store the data since I know that it will only split the string into 2 sections. However I also need to be able to read in strings that require no parsing such as Quit
What is the simplest way to accomplish this without using an outside library such as boost?
The simplest may be like this:
string s;
int i;
cin >> s;
if (s == "Insert")
{
cin >> i;
... // do stuff
}
else if (s == "Quit")
{
exit(0);
}
else
{
cout << "No good\n";
}
The simplest way may be not so good if you need e.g. good handing of user errors, extensibility etc.
You can read strings from a stream using getline, and then to a split by finding the firs position of a space character ' ' within the string, and using the substr function twice (for the command to the left of the space and for the data to the right of space).
while (cin) {
string line;
getline(cin, line);
size_t pos = line.find(' ');
string cmd, data;
if (pos != string::npos) {
cmd = line.substr(0, pos-1);
data = line.substr(pos+1);
} else {
cmd = line;
}
cerr << "'" << cmd << "' - '" << data << "'" << endl;
}
Here is a link to a demo on ideone.
This is another way :
string s("Insert 25");
istringstream iss(s);
do
{
string command; int value;
iss >> command >> value;
cout << "Values: " << command << " " << values << endl;
} while (iss);
I like using streams for such things.
int main()
{
int Value;
std::string Identifier;
std::stringstream ss;
std::multimap<std::string, int> MyCollection;
ss << "Value 25\nValue 23\nValue 19";
while(ss.good())
{
ss >> Identifier;
ss >> Value;
MyCollection.insert(std::pair<std::string, int>(Identifier, Value));
}
for(std::multimap<std::string, int>::iterator it = MyCollection.begin(); it != MyCollection.end(); it++)
{
std::cout << it->first << std::endl;
std::cout << it->second << std::endl;
}
std::cin.get();
return 0;
}
This way you can allready convert your data into the needed format. And the stream automatically splits on whitespaces. It works the same way with std::fstream if your working with files.

how to manipulate the txt file using C++ STL HOmework

I have a txt file that contains name, id number, mobilenumber, and location in comma separated line.
example
Robby, 7890,7788992356, 123 westminister
tom, 8820, 77882345, 124 kingston road
My task is to retrieve
Look up all of an employee's information by name.
Look up all of an employee's information by ID.
Add the information of an employee.
Update the information of an employee.
SO far I have read the file and stored the information in a vector. Code is shown below.
For tasks
1)Look up all of an employee's information by name. I will iterate in the vector and prints information containing the name . I will be able to do that
2) simialry in text file I will look for id and prints information about that.
BUT I am clueless about point 3 & 4.
I am posting my code below
void filter_text( vector<string> *words, string name)
{
vector<string>::iterator startIt = words->begin();
vector<string>::iterator endIt = words->end();
if( !name.size() )
std::cout << " no word to found for empty string ";
while( startIt != endIt)
{
string::size_type pos = 0;
while( (pos = (*startIt).find_first_of(name, pos) ) != string::npos)
std:cout <<" the name is " << *startIt<< end;
startIt++;
}
}
int main()
{
// to read a text file
std::string file_name;
std::cout << " please enter the file name to parse" ;
std::cin >> file_name;
//open text file for input
ifstream infile(file_name.c_str(), ios::in) ;
if(! infile)
{
std::cerr <<" failed to open file\n";
exit(-1);
}
vector<string> *lines_of_text = new vector<string>;
string textline;
while(getline(infile, textline, '\n'))
{
std::cout <<" line text:" << textline <<std::endl;
lines_of_text->push_back(textline);
}
filter_text( lines_of_text, "tony");
return 0;
}
#include <string>
#include <iostream>
#include <vector>
#include <stdexcept>
#include <fstream>
struct bird {
std::string name;
int weight;
int height;
};
bird& find_bird_by_name(std::vector<bird>& birds, const std::string& name) {
for(unsigned int i=0; i<birds.size(); ++i) {
if (birds[i].name == name)
return birds[i];
}
throw std::runtime_error("BIRD NOT FOUND");
}
bird& find_bird_by_weight(std::vector<bird>& birds, int weight) {
for(unsigned int i=0; i<birds.size(); ++i) {
if (birds[i].weight< weight)
return birds[i];
}
throw std::runtime_error("BIRD NOT FOUND");
}
int main() {
std::ifstream infile("birds.txt");
char comma;
bird newbird;
std::vector<bird> birds;
//load in all the birds
while (infile >> newbird.name >> comma >> newbird.weight >> comma >> newbird.height)
birds.push_back(newbird);
//find bird by name
bird& namebird = find_bird_by_name(birds, "Crow");
std::cout << "found " << namebird.name << '\n';
//find bird by weight
bird& weightbird = find_bird_by_weight(birds, 10);
std::cout << "found " << weightbird.name << '\n';
//add a bird
std::cout << "Bird name: ";
std::cin >> newbird.name;
std::cout << "Bird weight: ";
std::cin >> newbird.weight;
std::cout << "Bird height: ";
std::cin >> newbird.height;
birds.push_back(newbird);
//update a bird
bird& editbird = find_bird_by_name(birds, "Raven");
editbird.weight = 1000000;
return 0;
}
Obviously not employees, because that would make your homework too easy.
So, first off, I don't think you should store the information in a vector of strings. This kind of task totally calls for the use of a
struct employee {
int id;
std::string name;
std::string address;
//... more info
};
And storing instances of employees in an
std::vector<employee>
You see, using your strategy of storing the lines, searching for "westminster" would net me Robbie, as his line of text does include this substring, but his name isn't westminster at all. Storing the data in a vector of employee structs would eliminate this problem, and it'd make the whole thing a lot more, well, structured.
Of course you'd need to actually parse the file to get the info into the vector. I'd suggest using a strategy like:
while(getline(infile, textline, '\n')) {
std::stringstream l(textline);
getline(l,oneEmp.name, ','); //extract his name using getline
l >> oneEmp.id; //extract his id
//extract other fields from the stringstream as neccessary
employees.push_back(oneEmp);
}
As for adding information: when the user enters the data, just store it in your employees vector; and when you should need to update the file, you may simply overwrite the original data file with a new one by opening it for writing & dumping the data there (this is obviously a rather wasteful strategy, but it's fine for a school assignment (I suppose it's school assignment)).
Start by splitting the CSV line into separate fields and then populate a struct with this data
eg:
struct Employee
{
std::string name;
std::string id_number;
std::string mobilenumber;
std::string location;
};
std::vector<Employee> employees; // Note you dont need a pointer
Look at string methods find_first_of, substr and friends.

Example for file input to structure members?

I have the following structure:
struct productInfo
{
int item;
string details;
double cost;
};
I have a file that will input 10 different products that each contain an item, details, and cost. I have tried to input it using inFile.getline but it just doesn't work. Can anyone give me an example of how to do this? I would appreciate it.
Edit
The file contains 10 lines that look like this:
570314,SanDisk Sansa Clip 8 GB MP3 Player Black,55.99
Can you provide an example please.
Edit
Sorry guys, I am new to C++ and I don't really understand the suggestions. This is what I have tried.
void readFile(ifstream & inFile, productInfo products[])
{
inFile.ignore(LINE_LEN,'\n'); // The first line is not needed
for (int index = 0; index < 10; index++)
{
inFile.getline(products[index].item,SIZE,DELIMETER);
inFile.getline(products[index].details,SIZE,DELIMETER);
inFile.getline(products[index].cost,SIZE,DELIMETER);
}
}
This is another approach that uses fstream to read the file and getline() to read each line on the file. The parsing of the line itself was left out on purpose since other posts have already done that.
After each line is read and parsed into a productInfo, the application stores it on a vector, so all products could be accessed in memory.
#include <iostream>
#include <fstream>
#include <vector>
#include <iterator>
#include <string>
using namespace std;
struct productInfo
{
int item;
string details;
double cost;
};
int main()
{
vector<productInfo> product_list;
ifstream InFile("list.txt");
if (!InFile)
{
cerr << "CouldnĀ“t open input file" << endl;
return -1;
}
string line;
while (getline(InFile, line))
{ // from here on, check the post: How to parse complex string with C++ ?
// https://stackoverflow.com/questions/2073054/how-to-parse-complex-string-with-c
// to know how to break the string using comma ',' as a token
cout << line << endl;
// productInfo new_product;
// new_product.item =
// new_product.details =
// new_product.cost =
// product_list.push_back(new_product);
}
// Loop the list printing each item
// for (int i = 0; i < product_list.size(); i++)
// cout << "Item #" << i << " number:" << product_list[i].item <<
// " details:" << product_list[i].details <<
// " cost:" << product_list[i].cost << endl;
}
EDIT: I decided to take a shot at parsing the line and wrote the code below. Some C++ folks might not like the strtok() method of handling things but there it is.
string line;
while (getline(InFile, line))
{
if (line.empty())
break;
//cout << "***** Parsing: " << line << " *****" << endl;
productInfo new_product;
// My favorite parsing method: strtok()
char *tmp = strtok(const_cast<char*>(line.c_str()), ",");
stringstream ss_item(tmp);
ss_item >> new_product.item;
//cout << "item: " << tmp << endl;
//cout << "item: " << new_product.item << endl;
tmp = strtok(NULL, ",");
new_product.details += tmp;
//cout << "details: " << tmp << endl;
//cout << "details: " << new_product.details << endl;
tmp = strtok(NULL, " ");
stringstream ss_cost(tmp);
ss_cost >> new_product.cost;
//cout << "cost: " << tmp << endl;
//cout << "cost: " << new_product.cost << endl;
product_list.push_back(new_product);
}
It depends on what's in the file? If it's text, you can use the redirect operator on a file input stream:
int i;
infile >> i;
If it's binary, you can just read it in to &your_struct.
You have to
0) Create a new instance of productInfo, pinfo;
1) read text (using getline) to the first comma (','), convert this string to an int, and put it into pinfo.item.
2) read text to the next comma and put it into pinfo.details;
3) read text to the endline, convert the string to a double, and put it into pinfo.cost.
Then just keep doing this until you reach the end of the file.
Here is how I would use getline. Note that I use it once to read from the input file, and then again to chop that line at ",".
ostream& operator>>(istream& is, productInfo& pi)
{
string line;
getline(is, line); // fetch one line of input
stringstream sline(line);
string item;
getline(sline, item, ',');
stringstream(item) >> pi.item; // convert string to int
getline(sline, item, ',');
pi.details = item; // string: no conversion necessary
getline(sline, item);
stringstream(item) >> pi.cost; // convert string to double
return is;
}
// usage:
// productInfo pi; ifstream inFile ("inputfile.txt"); inFile >> pi;
N.b.: This program is buggy if the input is
99999,"The Best Knife, Ever!",16.95