Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I convert a std::string containing doubles to a vector of doubles?

Tags:

c++

c++11

I have two cases of input for which I want to use the same method. The first case is that the given parameter is a std::string containing three digits that I need to convert into int:

std::string pointLine = "1 1 1";

The second case is that the given parameter is a std::string containing three "not yet doubles" that I need to convert into doubles:

std::string pointLine = "1.23 23.456 3.4567"

I have written the following method:

std::vector<double> getVertexIndices(std::string pointLine) {


vector<int> vertVec;

vertVec.push_back((int) pointLine.at(0));
vertVec.push_back((int) pointLine.at(2));
vertVec.push_back((int) pointLine.at(4));

return vertVec;

}

This works fine for the first case, but not for having a line that is supposed to be converted to doubles.

So I tried the solution Double split in C . I get that my delimiter would be " ".

This is what I came up with as for now, but the program crashes after first call of the following method:

std::vector<double> getVertexIndices(std::string pointLine) { 

vector<double> vertVec;
char * result = std::strtok(const_cast<char*>(pointLine.c_str()), " "); 

while(result != NULL ) {
    double vert = atof (result);
    vertVec.push_back(vert);
    char * result = std::strtok(NULL, " ");
}
return vertVec;

}

like image 366
Reatus Avatar asked Aug 22 '14 10:08

Reatus


People also ask

How do you concatenate a vector in a string?

Method 1: Using paste() paste() function is used to combine strings present in vectors passed to it an argument. Parameter: vectors are the input vectors to be concatenate. sep is the separator symbol that separates the strings present in the vector.


3 Answers

Instead of copying, you can directly initialize your vector from iterators.

// include <string>, <vector>, <iterator> and <sstream> headers
std::vector<double> getVertexIndices(std::string const& pointLine)
{
  std::istringstream iss(pointLine);

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

This works exactly the same way for your ints. Your int-approach will not do what you intended for strings like "123 456 789"

like image 112
user2436830 Avatar answered Oct 26 '22 15:10

user2436830


The main problem with your attempt is that strtok() will modify the pointer passed as input. Doing so with a pointer obtained via std::string::c_str() will wreak havoc inside the string object.

All other answers provided here were pretty good. From what you've stated, you only need to read 3 doubles (an x,y,z vertex), so you could optimize the function to use some other fixed-size container that doesn't allocate memory (std::vector will allocate). In the following example, I've used an std::tuple to return the results. I've also used std::stod() which is a C++11 function that parses a double from a string. It will throw an exception if conversion fails, which might also be useful for your application.

#include <iostream> // std::cout
#include <string>   // std::stod
#include <tuple>    // std::tuple

std::tuple<double, double, double> getVertexIndices(const std::string & pointLine)
{
    size_t xEnd, yEnd;
    const double x = std::stod(pointLine, &xEnd);
    const double y = std::stod(pointLine.substr(xEnd), &yEnd);
    const double z = std::stod(pointLine.substr(xEnd + yEnd));
    return std::make_tuple(x, y, z);
}

// Test case:
int main()
{
    const std::string pointLine1 = "1 2 3";
    const std::string pointLine2 = "1.23 23.456 3.4567";

    std::tuple<double, double, double> v1 = getVertexIndices(pointLine1);
    std::cout << "x = " << std::get<0>(v1) << std::endl;
    std::cout << "y = " << std::get<1>(v1) << std::endl;
    std::cout << "z = " << std::get<2>(v1) << std::endl;

    std::tuple<double, double, double> v2 = getVertexIndices(pointLine2);
    std::cout << "x = " << std::get<0>(v2) << std::endl;
    std::cout << "y = " << std::get<1>(v2) << std::endl;
    std::cout << "z = " << std::get<2>(v2) << std::endl;

    return (0);
}
like image 28
glampert Avatar answered Oct 26 '22 16:10

glampert


Use std::istringstream:

std::vector<double> getVertexIndices(std::string pointLine)
{
  vector<double> vertVec;
  std::istringstream s(pointLine);
  double d;
  while (s >> d) {
    vertVec.push_back(d);
  }
  return vertVec;
}

It's quite simple, really. You construct a stream which will read from the string. Then just use normal stream extraction to fill the vector.

Of course, you can leverage the standard library iterator adaptors and similar, to produce something like this:

std::vector<double> getVertexIndices(std::string pointLine)
{
  std::vector<double> vec;
  std::istringstream s(pointLine);
  std::copy(
    std::istream_iterator<double>(s)  // start
    , std::istream_iterator<double>()  // end
    , std::back_inserter(vec)  // destination
  );
  return vec;
}

As a side note (thanks to @ikh), you might want to change the function to take a const std::string & - there's no need to take the string by value.

like image 40
Angew is no longer proud of SO Avatar answered Oct 26 '22 17:10

Angew is no longer proud of SO