What's better solution to separate user input by space in C++? - c++

I'm reading C++ Primer and working on its exercise. It want me to get user's input and separate by space ' '. So I come up with 2 solution.
First solution:
vector<string> vector1;
string input;
string temp = ""; // temperary hold each word value in input.
string x1 = "";
char x2 = 'b';
x1 += x2;
cout << x1 << endl;
getline(cin, input);
input += " ";
for (string::size_type index = 0; index != input.size(); index++)
{
if (!isspace(input[index]))
{
temp += input[index];
}
else
{
if (temp.size() > 0)
{
vector1.push_back(temp);
temp = "";
}
}
}
Second solution
vector<string> vector1;
string input;
string temp = ""; // temperary hold each word value in input.
string x1 = "";
char x2 = 'b';
x1 += x2;
cout << x1 << endl;
getline(cin, input);
//input += " ";
for (string::size_type index = 0; index != input.size(); index++)
{
if (!isspace(input[index]))
{
temp += input[index];
}
else
{
if (temp.size() > 0)
{
vector1.push_back(temp);
temp = "";
}
}
}
if (!temp.empty())
{
vector1.push_back(temp);
}
The difference between them is first solution is add space to user input while second solution check that I don't add last word or not. I want to know which one is better solution for this problem?
If there're better solutions, please tell me.

I would write this:
std::vector<std::string> data;
std::copy(std::istream_iterator< std::string>(std::cin),
std::istream_iterator< std::string>(),
std::back_inserter(data));
It is almost same as #K-ballo's answer, except that I let std::copy read directly from input stream (i.e std::cin) rather than from std::stringstream.
Demo: http://www.ideone.com/f0Gtc
--
Or you could make use of vector's constructor, avoiding std::copy altogether:
std::vector<std::string> data(std::istream_iterator<std::string>(std::cin),
std::istream_iterator<std::string>());
And you're done! Demo : http://www.ideone.com/Szfes
If you find it difficult to read, then use this instead:
std::istream_iterator<std::string> begin(std::cin), end;
std::vector<std::string> data(begin, end);
Demo : http://www.ideone.com/PDcud

In C++, reading space-separated values is quite easy, and is built into the language. I may be wrong, but it looks like you are over-complicating things.
std::string line;
if (std::getline(std::cin, line)) {
std::stringstream ss(line);
std::vector<std::string> inputs_on_this_line(
std::istream_iterator<std::string>(ss)
, std::istream_iterator<std::string>() );
//do stuff with the strings on this line.
}

You can easily split the input from a stream into string separated values, and insert them into a vector with:
string input;
... get the input, perhaps with getline(cin, input); ...
stringstream input_stream( input );
vector<string> vector1;
std::copy(
(std::istream_iterator< std::string >( input_stream )), std::istream_iterator< std::string >()
, std::back_inserter( vector1 )
);
The std::istream_iterator< std::string > pair will iterate over the input_stream by extracting an std::string at a time (a string is read until a whitespace character is found). std::back_inserter will call push_back into your vector for each of those strings.

Related

Issues reading text from a file. Getting double reads

Hello friends at stackoverflow!
I have written a program that saves 3 strings to a textfile. The code to write is:
void my_class::save_file(const char* text) {
for(auto ad: ad_list) {
std::ofstream outputFile;
outputFile.open(text, std::ios::app);
if (outputFile.is_open()) {
outputFile << ad.string1 << "|" << ad.string2 << "|" << ad.string3 << "|" << std::endl;
outputFile.close();
}
}
}
This gives me the file with the content:
string1|string2|string3|
<- //extra line here
When i read from this file I do it with the following function:
void my_class::read_file(const char* text) {
// read file to stringsteam
std::stringstream ss;
std::ifstream fp (text);
if (fp.is_open()) {
ss << fp.rdbuf();
}
fp.close();
string file_contents = ss.str();
// split to lines
auto lines = splitString(file_contents, "\n");
// split to parts
for (auto ad_text: lines) { // <-- this is the possibly the issue
auto ad_parts = splitString(ad_text, "|");
string string1 = ad_parts[0];
string string2 = ad_parts[1];
string string3 = ad_parts[2];
auto new_class = MyClass(string1, string2, string3);
//Adds (push_back) the class to its vector
add_class(new_class);
}
}
vector<string> my_class::splitString(string text, string delimiter) {
vector<string> parts;
size_t start = 0;
size_t end = 0;
while((end = text.find(delimiter, start)) != std::string::npos) {
size_t length = end - start;
parts.push_back(text.substr(start, length));
start = end + delimiter.length();
}
start = end + delimiter.length();
size_t length = text.length() - start;
parts.push_back(text.substr(start, length));
return parts;
}
Result:
string1|string2|string3|
string1|string2|string3|
I'm pretty sure this copy is a result of the new line left behind by the write function. The read function splits the text into lines, and that would be 2 lines.
I am currently splitting the lines in this loop "for (auto ad_text: lines)", and what I want to do is basically ( lines - 1 ). I do not understand how I can do that with the way I am interating through the for loop.
Thanks in advance!
You can simplify your splitString function to look like below. Note the second parameter to splitString is a char now and not a std::string.
//note the second parameter is a char and not a string now
vector<string> splitString(string text, char delimiter) {
vector<string> parts;
std::string words;
std::istringstream ss(text);
while(std::getline(ss, words,delimiter ))
{
parts.push_back(words);
}
return parts;
}
For the above modification to work you have to make 2 additional changes:
Change 1: Replace auto lines = splitString(file_contents, "\n"); to:
auto lines = splitString(file_contents, '\n');
Change 2: Replace auto ad_parts = splitString(ad_text, "|"); to:
auto ad_parts = splitString(ad_text, '|');

Read String Till Flag (C++)

I'm trying to read a string until a ',' character is reached and store what's been read in a new string.
e.g. "5,6"
// Initialise variables.
string coorPair, xCoor, yCoor
// Ask to enter coordinates.
cout << "Input coordinates: ";
// Store coordinates.
cin >> coorPair
// Break coordinates into x and y variables and convert to integers.
// ?
I also need to store the y value in a separate variable.
What is the best way to do this in C++?
Also, is the best way to validate the input to convert to integers and test the values range?
If you have only one comma seperator in string, you can just find where comma first occurs in input and substring input with found position.
Try following:
std::size_t pos = coorPair.find_first_of(","); //Find position of ','
xCoor = coorPair.substr(0, pos); //Substring x position string
yCoor = coorPair.substr(pos + 1); //Substring y position string
int xCoorInt = std::stoi(xCoor); //Convert x pos string to int
int yCoorInt = std::stoi(yCoor); //Convert y pos string to int
The easiest way to do this is to just let operator>> do all the work for you:
int xCoor, yCoor;
char ch;
cout << "Input coordinates: ";
if (cin >> xCoor >> ch >> yCoor)
{
// use coordinates as needed ...
}
else
{
// bad input...
}
You can do this by specifying you delimiter and parsing the string
std::string delimiter = ",";
size_t pos = 0;
std::string token;
while ((pos = coorPair.find(delimiter)) != std::string::npos) {
token = coorPair.substr(0, pos);
std::cout << token << std::endl;
coorPair.erase(0, pos + delimiter.length());
}
std::cout << coorPair << endl;
The last token example in {5,6} the {6} would be in the coorPair.
Another way would be to use std::getline as pointed in the comments:
std::string token;
while (std::getline(coorPair, token, ','))
{
std::cout << token << std::endl;
}
http://www.cplusplus.com/reference/string/string/getline/
I would recommend using getline().
Below is a small example of how I use it. It takes the input from a stream, so you can either use an ifstream as an input, or do what I did below and convert the string to a stream.
// input data
std::string data("4,5,6,7,8,9");
// convert string "data" to a stream
std::istringstream d(data);
// output string of getline()
std::string val;
std::string x;
std::string y;
bool isX = true;
char delim = ',';
// this will read everything up to delim
while (getline(d, val, delim)) {
if (isX) {
x = val;
}
else {
y = val;
}
// alternate between assigning to X and assigning to Y
isX = !isX;
}

Extract and set variables from input file c++

So I want to get a specific part from every line of the input file.
So far I got this:
ifstream fin("text.txt");
string line;
while (getline(fin, line)) {
if (line.find("Set") == 0)
{
istringstream sin1(line.substr(line.find("path1=") + 1));
sin1 >> path1;
istringstream sin2(line.substr(line.find("path2=") + 1));
sin2 >> path2;
}
}
From what I understood the (line.substr(line.find("VAR_CAL_PATH2=") + 1)) part will take whatever it's after "path1=" and will put it into path1, I guess I got it wrong. My input file has two lines:
Set path1="somepath"
Set path2="someotherpath"
When the while loop ends I get path1="Set" and path2="someotherpath" but what I want is path1="somepath" and path2="someotherpath"
As #Jordfräs says, find() returns the position of the start of the string.
When you try to parse the path2 value, you overwrite the value of path1.
The code with these fixes:
const string path1field = "path1=";
const string path2field = "path2=";
string path1 = "", path2 = "";
ifstream fin("text.txt");
string line;
while (getline(fin, line))
{
if (line.find("Set") != string::npos)
{
size_t path1pos = line.find(path1field);
size_t path2pos = line.find(path2field);
if (path1pos != string::npos)
{
istringstream sin1(line.substr(path1pos + path1field.length()));
sin1 >> path1;
}
if (path2pos != string::npos)
{
istringstream sin2(line.substr(path2pos + path2field.length()));
sin2 >> path2;
}
}
}
cout << "path1: " << path1 << endl;
cout << "path2: " << path2 << endl;
The find() function in std::string returns the position of the start of the string. Adding 1 will point to the start + 1, not the position after the string you search for.
There is plenty of good documentation of the standard library available, e.g., http://en.cppreference.com/w/cpp/string/basic_string/find
Another (more general) solution for string variables could be:
ifstream fin("text.txt");
string line;
const vector<string> pathsNames={"path1=", "path2="};
vector<string> paths(pathsNames.size());
while (getline(fin, line)) {
if (line.find("Set") == 0)
{
for(std::vector<string>::size_type x=0; x<pathsNames.size(); x++){
if(line.find(pathsNames[x])!=string::npos){
paths[x]=line.substr(line.find(pathsNames[x]) + pathsNames[x].length());
break;
}
}
}
}
//print results
for(std::vector<string>::size_type x=0; x<pathsNames.size(); x++){
cout << pathsNames[x] << paths[x] << endl;
}

reading two vectors from file

Problem is described here.
I tried to solve it using code that's below, but it's not working.
const char* filename = "test.txt";
ifstream file1(filename);
vector<int> v1;
vector<int> v2;
vector<int> res;
int number;
char c;
while(1){
while(1){
v1.push_back(number);
file1.get(c);
if (c==';') break;
}
while(1){
v2.push_back(number);
file1.get(c);
if (c=='\n') break;
}
for (vector<int>::iterator it = v2.begin(); it!=v2.end(); it++)
cout << *it << ',';
cout << endl;
file1.get(c);
if (c==EOF) break;
file1.unget();
}
There is a problem with reading end of line. Is c=='\n' right?
To read a line, you should use:
istream& getline (istream& is, string& str, char delim);
In your case, with a delimiter of ';'
Then you can parse numbers in the same way, by using delimiter of ','
like this:
std::string line, temp;
std::getline(file1,line,';'); //get a line. (till ';')
std::istringstream s1 (line); //init stream with the whole line
while(std::getline(s1,temp,',')){//get a number as string from the line. (till ',')
int n;
std::istringstream s2(temp);
s2>>n; //convert string number to numeric value
//now you can push it into the vector...
}

Parsing string in C++

Currently,
I have this string us,muscoy,Muscoy,CA,,34.1541667,-117.3433333.
I need to parse US and CA. I was able to parse US correctly by this:
std::string line;
std::ifstream myfile ("/Users/settingj/Documents/Country-State Parse/worldcitiespop.txt");
while( std::getline( myfile, line ) )
{
while ( myfile.good() )
{
getline (myfile,line);
//std::cout << line << std::endl;
std::cout << "Country:";
for (int i = 0; i < 2/*line.length()*/; i++)
{
std::cout << line[i];
}
}
}
But i'm having an issue parsing to CA.
Heres some code I dug up to find the amount of ',' occurrences in a string but I am having issues saying "parse this string between the 3rd and 4th ',' occurrence.
// Counts the number of ',' occurrences
size_t n = std::count(line.begin(), line.end(), ',');
std::cout << n << std::endl;
You can use boost::split function (or boost::tokenizer) for this purpose. It will split string into vector<string>.
std::string line;
std::vector<std::string> results;
boost::split(results, line, boost::is_any_of(","));
std::string state = results[3];
It's not for a class... Haha... it seems like a class question though...
I have the solution:
int count = 0;
for (int i = 0; i < line.length(); i++)
{
if (line[i] == ',')
count++;
if (count == 3){
std::cout << line[i+1];
if (line[i+1] == ',')
break;
}
}
Just had to think about it more :P
This is STL version, works pretty well for simple comma separated input files.
#include<fstream>
#include <string>
#include <iostream>
#include<vector>
#include<sstream>
std::vector<std::string> getValues(std::istream& str)
{
std::vector<std::string> result;
std::string line;
std::getline(str,line);
std::stringstream lineS(line);
std::string cell;
while(std::getline(lineS,cell,','))
result.push_back(cell);
return result;
}
int main()
{
std::ifstream f("input.txt");
std::string s;
//Create a vector or vector of strings for rows and columns
std::vector< std::vector<std::string> > v;
while(!f.eof())
v.push_back(getValues(f));
for (auto i:v)
{
for(auto j:i)
std::cout<<j<< " ";
std::cout<<std::endl;
}
}