简体   繁体   中英

Split vector to multiple array/vector C++

I have problem to split a string vector to smaller integer vector\\array. My input vector data looks like:

std::vector<std::string> v(2);
v[0] = "0 14 150";
v[1] = "1 2 220";
//...

I know one solution, to make three arrays and to use sstream to convert data to integer. But i want to avoid making "spaghetti" code.

Thank you, Peter.

I found a split function at stackoverflow some time ago. Unfortunatly, I cannot post the link anymore.

void split(const std::string & str, std::vector<std::string>& cont, const std::string & delims)
{
    std::size_t current, previous = 0;
    current = str.find_first_of(delims);
    while (current != std::string::npos) 
    {
        cont.push_back(std::move(str.substr(previous, current - previous)));
        previous = current + 1;
        current = str.find_first_of(delims, previous);
    }
    cont.push_back(std::move(str.substr(previous, current - previous)));
}

I will need delimiter in your strings (seems to be backspace in your case) and call the function on each element of your string vector:

int main()
{
std::vector<std::string> vec{ "0 14 150","1 2 220" };
std::vector<std::vector<int>> intVec(3,std::vector<int>(vec.size()));
for (int i = 0; i < vec.size(); i++)
{
    std::vector<std::string> singleStr;
    split(vec[i], singleStr, " ");
    for (int j=0; j < singleStr.size();j++)
        intVec[j][i] = (std::stoi(singleStr[j]));
}

system("pause");
}

A more generic solution could look like this. You can add further types to BasicVariant

#include <string>
#include <vector>

class BasicVariant
{
private:
    std::string str;
public:
    BasicVariant(const std::string& _str) :str(_str) {}
    BasicVariant(int value) :str(std::to_string(value)) {}
    BasicVariant(double value) :str(std::to_string(value)) {}
    inline int toInt()const { return *this; }
    inline double toDouble()const { return *this; }
    inline std::string toString()const { return *this; }
    inline bool toBool()const { return toDouble(); }
    inline operator int()const { return std::stoi(str); }
    inline operator double()const { return std::stof(str); }
    inline operator std::string()const { return str; }
    inline operator bool()const { return toDouble(); }
};


template<typename T>
void split(const std::string& str, std::vector<T>& sink, const std::string& delims)
{
    std::size_t current, previous = 0;
    current = str.find_first_of(delims);
    while (current != std::string::npos)
    {
        sink.push_back(std::move(BasicVariant(str.substr(previous, current - previous))));
        previous = current + 1;
        current = str.find_first_of(delims, previous);
    }
    sink.push_back(std::move(BasicVariant(str.substr(previous, current - previous))));
}




int main()
{
    std::vector<std::string> vec{ "0 14 150","1 2 220" };
    std::vector<std::vector<int>> intVec(3, std::vector<int>(vec.size()));
    for (int i = 0; i < vec.size(); i++)
    {
        std::vector<int> row;
        split(vec[i], row, " ");
        for (int j = 0; j < row.size(); j++)
            intVec[j][i] = row[j];
    }

    system("pause");
}

Edit: I removed a verbose transposing function.


I assume that you want to convert std::vector<std::string> to a 2D matrix std::vector<std::vector<int>> . For instance, for your example, the desired result is assumed to be arr1 = {0,1,...} , arr2 = {14,2,...} and arr3 = {150,220,...} .

First,

  • We can use std::istream_iterator to extract integers from strings.

  • We can also apply the range constructor to create a std::vector<int> corresponding to each string.

So the following function would work for you and it does not seem to be a spaghetti code at least to me. First, this function extract two integer arrays {0,14,150,...} and {1,2,220,...} as matrices from a passed string vector v . Since a default constructed std::istream_iterator is an end-of-stream iterator, each range constructor reads each string until it fails to read the next value. And finally, transposed one is returned:

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

template <typename T>
auto extractNumbers(const std::vector<std::string>& v)
{
    std::vector<std::vector<T>> extracted;
    extracted.reserve(v.size());

    for(auto& s : v)
    {
        std::stringstream ss(s);
        std::istream_iterator<T> begin(ss), end; //defaulted end-of-stream iterator.

        extracted.emplace_back(begin, end);
    }

    // this also validates following access to extracted[0].
    if(extracted.empty()){
        return extracted;
    }

    decltype(extracted) transposed(extracted[0].size());
    for(std::size_t i=0; i<transposed.size(); ++i){
        for(std::size_t j=0; j<extracted.size(); ++j){
            transposed.at(i).push_back(std::move(extracted.at(j).at(i)));
        }
    }

    return transposed;
}

Then you can extract integers from a string vector as follows:

DEMO

std::vector<std::string> v(n);
v[0] = "0 14 150";
v[1] = "1 2 220";
...
v[n-1] = "...";

auto matrix = extractNumbers<int>(v);

where matrix[0] is arr1 , matrix[1] is arr2 , and so on. We can also quickly get internal pointers of them by auto arr1 = std::move(matrix[0]); .

We have here some misunderstands.
Output of my program should have three arrays/vectors.

The output looks like:

arr1| arr1| arr3
0   | 14  | 150
1   |  2  | 220
2   |  4  | 130  

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