简体   繁体   中英

How to take input from user ( which is a list ) and store values in array in c++?

Testcase 1: {1, 2, 3, 4, 5}

Testcase 2: {5, 8, 9}

Is there any better solution than this for storing the values in an array in c++??

Note: we dont know the size of the array to be created at compile time.

#include<iostream>
#include<sstream>
#include<vector>
using namespace std;

int main(){
  string s;
  cin.ignore();
  getline(cin,s);
  string t;
  stringstream x(s);
  vector<int>v;
  while(getline(x,t,' ')){
      if(t[0]!='{' && t[0]!='}'&& t[0]!=','){
           v.push_back(stoi(t));
        }
   }
 int size=v.size();
 int arr[size];
 for(int i=0;i<size;i++) arr[i]=v[i];
// for(int i=0;i<size;i++) cout<<arr[i]<<" ";
}

Yes, it is possibile take an unknown number of integers. We can use a std::vector

We could write a function for doing that. Here we will split the string and return a std::vector . In C++ we can make use of RVO, Return Value Optimization. You may read here about copy elision and RVO. Basically it says, that you will not have a problem with copying bytes when returning containers from a function by value. It is now even the recommended way to do so.

So, you can (and should) return a std::vector from a function. The compiler will take care that this works in an optimized way.

But first, please see the below example:

#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <iterator>

std::vector<int> parseInts(std::string str) {

    // Put the string into a stream
    std::istringstream iss{ str };

    // Return the vector of ints 
    return { std::istream_iterator<int>(iss), {} };
}

int main() {
    // Test data
    std::string test{ "1 2 3 4 5 6 7 8 9 10" };

    // Get the vector with the ints
    std::vector ints = parseInts(test);

    // Show output
    for (const int i : ints) 
        std::cout << i << "\n";
    return 0;
}

We have a function called parseInts consisting of just 2 lines of code.

First, we we put the string into an std::istringstream . With that, it would be possible to extract int from the new stream, like in

std::istringstream iss{ str };
int i1, i2;
iss >> i1 >> i2;

The extractor operator >> will "extract" space delimited integer values from the stream (the string).

But, if we look at it, we see, that then we need to know the number of integers in the string, and then repeatedly write ">>". For doing things repeatedly for the same number of items, like integers, we have iterators in C++. With iterators you can iterate over similar elements in a container. If you assume a string to be a container, you can of course iterate over the integers in it. There are iterators available that do this for us.

In the case of a stream, we have the std::istream_iterator . This thing will iterate over all space delimited items of a certain type in the input stream and return the values.

The many values , that this iterator return, can best be stored in a std::vector , because it is a dynamically growing container. Ideal for us, becuase we do not know the number of integers in the stream in advance.

If we look at the std::vector , we find out that it has a so called range constructor. Please see here: range constructor (number 5) .

With the range constructor we define a variable of type std::vector and initialize it with a "begin" and an "end" iterator. Values in this range, will be put into the vector.

If we define the variable of type std::vector in our example, we could write:

std::vector<int> intv{ std::istream_iterator<int>(iss), std::istream_iterator<int>() };

or

std::vector<int> intv( (std::istream_iterator<int>(iss)), std::istream_iterator<int>() );

Please note the braces for the first argument. This is necessary to protect against the most vexing parse problem. You do not want to define a function, but a variable.

But we will anyway not use it. Because, with uniform initalization and the {}-initializer as the default-initializer, we can use the default for the 2nd parameter. Now we could write:

std::vector<int> intv(std::istream_iterator<int>(iss), {});

So, next, we can make usage of CTAD, Class template argument deduction . Basically, the compiler knows from the parameters in the constructor, what datatype of the container it will construct. So, no need to write it.

Now:

std::vector intv(std::istream_iterator<int>(iss), {});

This looks already very nice and you could now return the variable intv.

But there is more. The compiler knows the type of the function and what it should return. So, you can use a braced initializer and return that and the compiler knows, that you want to return a std::vector<int>

So the final statement is:

return { std::istream_iterator<int>(iss), {} };

I hope it helps.

And sorry for the long description for the one line of code.

So, now, for reading more lines you can write:

int main() {

    // Her we will store the data
    std::vector<std::vector<int>> data{};

    // Read lines. Stop input with CRTL Z in an empty new line
    for (std::string line{}; std::getline(std::cin, line); ) {

        // Put the string into a stream
        std::istringstream iss{ line };

        // Split the values and add the values to the vector
        data.emplace_back(std::vector{ std::istream_iterator<int>(iss), {} });
    }

    // Show output
    for (const std::vector<int>& vi : data) {
        for (const int i : vi) std::cout << i << " ";
        std::cout << "\n";
    }
}

Stop input by typing CRTL Z in an empty line after the end.

Please try with inputting:

1 2 3 4
5 6 7 8
CTRL Z

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM