How can I get and then store a space delimited input in C++ from a user into an array?
Most important part is that length is unknown and input should be by user during runtime
#include<iostream>
using namespace std;
int main() {
int n=0;
string names[3];
int i = 0;
while ( getline( cin, names[i]))
{
cout<<"i = "<<i<<"\n";
cout<<names[i]<<"\n";
i++;
cout<<"i = "<<i<<"\n";
}
for(i=0;i<3;i++)
{
cout<<names[i];
}
return 0;
}
The first problem I can spot in your code is, that you provide a fixed sized array of string names[3]; to store an unknown number of inputs from the user. That certainly won't work.
To fix this use a container that can use an "arbitrary" number of std::strings;
std::vector<std::string> names;
Having this you can just use something like
std::vector<std::string> names;
std::string name;
while(cin >> name) {
names.push_back(name);
}
The problem still left is how you could end this loop.
There are several options:
Let the user use one of CTRL-D or CTRL-Z (<- subtle link)
Ask them to enter a special value like quit, exit, -1 to end inputting
Track it down to a second level to process ENTER. Use std::getline() parse all the words using a std::istringstream or break, when getline() reads an empty result.
Let's analyze these:
Is a bad choice, because it's operating system dependent and asks the user to do relatively complicated actions.
Restricts the possible inputs, and there's a method needed to escape inputs of quit, exit or alike.
Empty input like retrieved from a simple additional hit of the ENTER key is probably the most intuitive you can offer to the user.
As a solution following my proposal in 3.:
std::string line;
std::vector<std::string> names;
while ( getline( cin, line)) {
if(line.empty()) {
// end input condition
break;
}
std::istringstream iss(line);
std::string name;
while(iss >> name) {
names.push_back(name);
}
}
Related
I have a text file that stores the index, student name and student ID and I am trying to read them into an array of integers index, arrays of strings studentName and studentID. I'm having problems storing the student's names because they could be more than a single word. I could separate the items in the text file by commas and use getline but that would mean the index array will have to be a string type. Is there a workaround for this without changing the original text file?
Original file:
1 James Smith E2831
2 Mohammad bin Rahman M3814
3 MJ J4790
const int SIZE = 3;
int index[SIZE];
string studentName[SIZE], studentID[SIZE];
fstream infile("students.txt");
if(infile.is_open()){
int i = 0;
while(i < 3){
infile >> index[i] >> studentName[i] >> studentID[i];
i++;
}
}
Changed file:
1,James Smith,E2831
2,Mohammad bin Rahman,M3814
3,MJ,J4790
const int SIZE = 3;
string index[SIZE];
string studentName[SIZE], studentID[SIZE];
fstream infile("students.txt");
if(infile.is_open()){
int i = 0;
while(i < 3){
getline(infile, index[i],','); //index array is string
getline(infile, studentName[i],',');
getline(infile, studentID[i],'\n');
i++;
}
}
It's an error to read one line into one student property with the given input format. You need to read one line and then split the information in this line into the 3 properties.
std::stoi can be used to convert to convert the first part of the line read to an int. Futhermore it's simpler to handle the data, if you create a custom type storing all 3 properties of a student instead of storing the information in 3 arrays.
Note that the following code requires the addition of logic to skip whitespace directly after (or perhaps even before) the ',' chars. Currently it simply includes the whitespace in the name/id. I'll leave that task to you.
struct Student
{
int m_index;
std::string m_name;
std::string m_id;
};
std::vector<Student> readStudents(std::istream& input)
{
std::vector<Student> result;
std::string line;
while (std::getline(input, line))
{
size_t endIndex = 0;
auto index = std::stoi(line, &endIndex);
if (line[endIndex] != ',')
{
throw std::runtime_error("invalid input formatting");
}
++endIndex;
auto endName = line.find(',', endIndex);
if (endName == std::string::npos)
{
throw std::runtime_error("invalid input formatting");
}
result.push_back(Student{ index, line.substr(endIndex, endName - endIndex), line.substr(endName + 1) });
}
return result;
}
int main() {
std::istringstream ss(
"1,James Smith,E2831\n"
"2, Mohammad bin Rahman, M3814\n"
"3, MJ, J4790\n");
auto students = readStudents(ss);
for (auto& student : students)
{
std::cout << "Index=" << student.m_index << "; Name=" << student.m_name << ";Id=" << student.m_id << '\n';
}
}
There are so many possible solutions that it is hard to tell, what should be used. Depends a little bit on your personal style and how good you know the language.
In nearly all solutions you would (for safety reasons) read a complete line with std::getline then put either split the line manually or use a std::istringstream for further extraction.
A csv input would be preferred, because it is more clear what belongs together.
So, what are the main possibilities? First the space separated names
You could use a std::regexand then search or match for example "(\d) ([\w ]+) (\w+)"
You cou create substrings, by searching the first space from the right side of the string, which would be the "studentID", then the getting the rest is simple
You could use a while loop and read all parts of the string and put it in a std::vector. The first element in the std::vector is then the index, the last the ID and the rest would be the name.
You could parse the string with formatted input functions. First read the index as number, then the rest as stringm the split of the last part and get the id.
And many more
For csv, you could use
the std::sregex_token_iterator looking for the comma as separator
Use also a std::vector and later pick the needed values.
Use a mixture of formatted and unformatted input
Example:
std::getline(std::getline(infile >> index >> Comma >> std::ws, name, ','), id);
It is up to you, what you would like to implement.
Write your preference in the comment, then I will add some code.
I am a beginner and I'm trying to limit the user to input a single character only, I do aware of using cin.get(char) and it will only read one character from the input, but I don't want the other characters be left in buffer. Here is a sample of my code using EOF, but it doesn't seem to work.
#include <iostream>
#include <sstream>
using namespace std;
string line;
char category;
int main()
{
while (getline (cin, line))
{
if (line.size() == 1)
{
stringstream str(line);
if (str >> category)
{
if (str.eof())
break;
}
}
cout << "Please enter single character only\n";
}
}
I have used this for digit inputs and the eof works fine.
But for the char category the str.eof() seems to be false.
Can someone explain? Thanks in advance.
The eof flag is only set if you read try to read past the end of the stream. If str >> category read past the end of the stream, if (str >> category) would have evaluated false and not entered the loop to test (str.eof()). If there was one character on the line you would have to attempt to read two characters to trigger eof. Reading two characters is far more effort than testing the length of line to see how long it is.
while (getline (cin, line)) got the whole line from the console. If you don't consume it in the stringstream it doesn't matter, that stuff is gone is gone from cin when you loop back around in the while.
In fact, the stringstream isn't doing you any favours. Once you've confirmed the length of the line that was read, you can just use line[0].
#include <iostream>
using namespace std;
int main()
{
string line; // no point to these being global.
char category;
while (getline(cin, line))
{
if (line.size() == 1)
{
//do stuff with line[0];
}
else // need to put the fail case in an else or it runs every time.
// Not very helpful, an error message that prints when the error
// didn't happen.
{
cout << "Please enter single character only\n";
}
}
}
This function keeps getting called in another function inside a while-loop while valid_office_num is false. The problem is that if the input begins with a digit but is followed by other invalid characters (e.g. 5t) it takes the digit part and accepts that as a valid input. I want it to consider the whole input and reject it so it can ask for another one. I thought I could use getline() but then I cannot use cin.fail(). How could I implement this behavior?
I forgot to mention I am very new to C++, I have only learnt the basics so far.
(To be clear the desired behavior is to reject anything that contains anything other than digits. This is not an integer range check question. If it is NOT an integer, discard it and request another one)
//Function to read a valid office number, entered by user
int read_office_num()
{
//Declaration of a local variable
int office_num;
//Read input
cin >> office_num;
//Check if input was valid
if (cin.fail())
{
//Print error message
cout << "\nInvalid office number, it should only consist of digits!! Enter another:\n";
//Clear error flags
cin.clear();
//Ignore any whitespace left on input stream by cin
cin.ignore(256, '\n');
}
else
{
//Office number entered is valid
valid_office_num = true;
}
return office_num;
}
From what I gather you want the whole line to be read as a number and fail otherwise?
Well, you can use std::getline(), but you have to follow the algorithm below (I will leave the implementation to you..)
use std::getline(cin, str) to read a line, and if this returns true
use std::stoi(str, &pos) to convert to integer and get the position of the last integer
if pos != str.size() then the whole line in not an integer (or if the above throws an exception), then it's not a valid integer, else return the value...
Read a line of input as a std::string using std::getline().
Examine the string and check if it contains any characters that are not digits.
If the string only contains digits, use a std::istringstream to read an integer from the string. Otherwise report a failure, or take whatever other recovery action is needed (e.g. discard the whole string and return to read another one).
You could use a stringstream
int read_office_num()
{
//Declaration of a local variable
int office_num;
string input = "";
while (true) {
getline(cin, input);
stringstream myStream(input);
if (myStream >> office_num)
break;
cout << "\nInvalid office number, it should only consist of digits!! Enter another:\n" << endl;
}
return office_num;
}
If you want to reject input like 123 xxx you could add an additional check to verify that the received string is indeed an integer:
bool is_number(const string& s)
{
string::const_iterator itr = s.begin();
while (itr != s.end() && isdigit(*itr)) ++itr;
return !s.empty() && itr == s.end();
}
int read_office_num()
{
//Declaration of a local variable
int office_num;
string input = "";
while (true) {
getline(cin, input);
stringstream myStream(input);
if (is_number(input) && myStream >> office_num)
break;
cout << "\nInvalid office number, it should only consist of digits!! Enter another:\n" << endl;
}
return office_num;
}
You should probably just look at the number of input characters that are left in cin. You can do that with in_avail
Your function will probably end up having a body something like this:
//Declaration of a local variable
int office_num;
//Read input and check if input was valid
for (cin >> office_num; cin.rdbuf()->in_avail() > 1; cin >> office_num){
//Print error message
cout << "\nInvalid office number, it should only consist of digits!! Enter another:\n";
//Ignore any whitespace left on input stream by cin
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
//Office number entered is valid
valid_office_num = true;
return office_num;
Points of interest:
There is always at least 1 character in cin otherwise the cin would be marked as bad and that would not be good
You don't need valid_office_num if read_office_num is implemented this way, cause valid_office_num will always be set to true before returning
Hm. I may be missing something, but why not read a line, trim it, use regular expressions to validate a number and then exploit strstream's facilities or just atoi if you must? In all reality I'd probably just let users get away with extraneous input (but discard it if I'm sure I'm always running interactively). Following the motto "be lenient in what you accept."
The "interactive" caveat is important though. One can generally not assume that cin is a terminal. Somebody may get cocky and let your program run on a text file or in a pipeline, and then it would fail. A robust approach would separate data processing (portable) from means of input (possibly machine specific and therefore also more powerful and helpful than stdin/stdout via a console).
Here's how to do it using Boost Lexical Cast:
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <vector>
#include <string>
int read_office_num()
{
using boost::lexical_cast;
using boost::bad_lexical_cast;
using namespace std;
int office_num;
while (true)
{
try
{
string input = cin.getline();
office_num = lexical_cast<int>(*argv));
break;
}
catch(const& bad_lexical_cast)
{
cout << "\nInvalid office number, it should only consist of digits!! Enter another:\n";
}
}
return office_num;
}
I am using the following loop to read an unknown number of lines from the console, but it is not working. After I have fed the input I keeping pressing enter but the loop does not stop.
vector<string> file;
string line;
while(getline(cin,line)
file.push_back(line);
Because getline will evaluate to true even if you push only enter.
You need to compare the read string to the empty string and break if true.
vector<string> file;
string line;
while(getline(cin,line))
{
if (line.empty())
break;
file.push_back(line);
}
Try:
vector<string> file;
string line;
while( getline(cin,line))
{
if( line.empty())
break;
file.push_back(line);
}
For getline is easy, as it is suggested by other answers:
string line;
while(getline(cin,line))
{
if (line.empty())
break;
file.push_back(line);
}
But to cin objects, I found a way to without the need for any breaking character. You have to use the same variable to cin all of the objects. After usage, you need to set it to a default exit value. Then check if your variable is the same after the next cin. Example:
string o;
while(true){
cin>>o;
if (o.compare("tmp")==0)
break;
// your normal code
o="tmp";
}
You should signal the end of file to your application. On Linux it is Ctrl-D, and it might be Ctrl-Z on some Microsoft systems
And your application should test of end of file condition using eof()
I'm trying to do a simple task of reading space separated numbers from console into a vector<int>, but I'm not getting how to do this properly.
This is what I have done till now:
int n = 0;
vector<int> steps;
while(cin>>n)
{
steps.push_back(n);
}
However, this requires the user to press an invalid character (such as a) to break the while loop. I don't want it.
As soon as user enters numbers like 0 2 3 4 5 and presses Enter I want the loop to be broken. I tried using istream_iterator and cin.getline also, but I couldn't get it working.
I don't know how many elements user will enter, hence I'm using vector.
Please suggest the correct way to do this.
Use a getline combined with an istringstream to extract the numbers.
std::string input;
getline(cin, input);
std::istringstream iss(input);
int temp;
while(iss >> temp)
{
yourvector.push_back(temp);
}
To elaborate on jonsca's answer, here is one possibility, assuming that the user faithfully enters valid integers:
string input;
getline(cin, input);
istringstream parser(input);
vector<int> numbers;
numbers.insert(numbers.begin(),
istream_iterator<int>(parser), istream_iterator<int>());
This will correctly read and parse a valid line of integers from cin. Note that this is using the free function getline, which works with std::strings, and not istream::getline, which works with C-style strings.
This code should help you out, it reads a line to a string and then iterates over it getting out all numbers.
#include <iostream>
#include <sstream>
#include <string>
int main() {
std::string line;
std::getline(std::cin, line);
std::istringstream in(line, std::istringstream::in);
int n;
vector<int> v;
while (in >> n) {
v.push_back(n);
}
return 0;
}
Also, might be helpful to know that you can stimulate an EOF - Press 'ctrl-z' (windows only, unix-like systems use ctrl-d) in the command line, after you have finished with your inputs. Should help you when you're testing little programs like this - without having to type in an invalid character.
Prompt user after each number or take number count in advance and loop accordingly.
Not a great idea but i saw this in many applications.