Home > Enterprise >  How to convert string to vector in C
How to convert string to vector in C

Time:01-06

If I have a string (from user) of "{1, 2, 3, 4, 5}", how would I convert that to a vector of {1, 2, 3, 4, 5} in C ?

I tried to get a string from the user by

vector<int> input;
cin >> input;

but I got error:

./main.cpp:124:9: error: invalid operands to binary expression ('std::istream' (aka 'basic_istream<char>') and 'vector<int>')
    cin >> user_input;

CodePudding user response:

A suggestion: convert your string into an input stream.
Try something like this:

const std::string   input_data="{1, 2, 3, 4, 5}";
std::istringstream  input_stream(input_data);
char                c; // Used for ignoring characters.  
std::vector<int>    database;
int                 number;

// Ignore the first brace
input_stream >> c;

// Read in the first number
input_stream >> number;
database.push_back(number);

// Read in the separator;
input_stream >> c;


// Read in the next number
input_stream >> number;
database.push_back(number);

// ...

The test for the ending brace or end of input are left as an exercise for the OP. :-)

CodePudding user response:

I have this utility function I use to stream in specific characters like { and }

template<class e, class t>
std::basic_istream<e,t>& operator>>(std::basic_istream<e,t>& in, const e& cliteral) {
        e buffer;  //get buffer
        in >> buffer; //read data
        if (buffer != cliteral) //if it failed
                in.setstate(in.rdstate() | std::ios::failbit); //set the state
        return in;
}

And with that, you can use an istream_iterator to stream the ints that the user types directly into the vector:

std::cin >> '{';
std::vector<int> input(std::istream_iterator<int>(std::cin), {});
std::cin >> '}';

CodePudding user response:

So, this is where a library of useful functions comes in. I keep quite a few.

First, we’ll need something to range over a container (such as a string):

#include <utility>

template <typename Iterator>
struct ranger : public std::pair <Iterator, Iterator>
{
  ranger( Iterator begin, Iterator end = Iterator() ) : std::pair <Iterator, Iterator> { begin, end } { }
  Iterator begin() { return this->first;  }
  Iterator end  () { return this->second; }
};

Next we’ll want something to make iterating over a string with regular expressions easier:

#include <regex>
#include <string>

struct re_ranger : public std::regex, public ranger <std::sregex_iterator>
{
  template <typename RegEx>
  re_ranger( const RegEx& re, const std::string& s )
  : std::regex( re )
  , ranger( std::sregex_iterator( s.begin(), s.end(), *this ) )
  { }
};

And we will naturally want to have the ability to turn a string like "7" into an integer like 7:

#include <optional>
#include <sstream>
#include <string>

template <typename T>
auto string_to( const std::string & s )
{
  T value;
  std::istringstream ss( s );
  return ((ss >> value) and (ss >> std::ws).eof())
    ? value
    : std::optional<T> { };
}

This makes selecting and converting the numbers in a string to a vector of integers stupidly simple:

#include <iostream>
#include <vector>

int main()
{
  std::string input = "{1, 2, 3, 4, 5}";
  std::vector<int> xs;
  
  for (auto m : re_ranger( "[[:digit:]] ", input ))
    xs.emplace_back( *string_to<int>(m.str()) );

Since we are converting one way, we might as well be able to convert the other way. Here’s the freebie:

#include <sstream>
#include <string>

template <typename Iterator>
std::string join( Iterator begin, Iterator end, const std::string & separator = " " )
{
  std::ostringstream ss;
  if (begin != end)
  {
    ss << *begin  ;
    while (begin != end)
      ss << separator << *begin  ;
  }
  return ss.str();
}

template <typename Container>
std::string join( const Container & xs, const std::string & separator = " " )
{
  using std::begin;
  using std::end;
  return join( begin( xs ), end( xs ), separator );
}

Now we can finish off main():

#include <iostream>
#include <numeric>
#include <vector>

int main()
{
  std::string input = "{1, 2, 3, 4, 5}";
  std::vector<int> xs;
  
  for (auto s : re_ranger( "[[:digit:]] ", input ))
    xs.emplace_back( *string_to<int>( s.str() ) );
  
  std::cout << join( xs, " " ) 
    << " = " << std::accumulate( xs.begin(), xs.end(), 0 ) << "\n";
}

Output:

1 2 3 4 5 = 15

PS. You should get user input as a string:

int main()
{
  std::string input;
  std::cout << "input? ";
  getline( std::cin, input );

CodePudding user response:

Here is another way of parsing the input. It uses a helper type to expect a specific character in the input stream (and eat it).

#include <cctype>
#include <iostream>

struct expect
{
  char c;
  explicit expect( char c ) : c{c} { }
};

std::istream & operator >> ( std::istream & ins, const expect & c )
{
  if (!std::isspace( (unsigned char)c.c )) ins >> std::ws;
  if (ins.peek() == c.c) ins.get();
  else ins.setstate( std::ios::failbit );
  return ins;
}

Now we can write our input function. I’ll overload >> for a vector of integers:

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

std::istream & operator >> ( std::istream & ins, std::vector<int> & xs )
{
  int x;
  xs.clear();
  if (!(ins >> expect( '{' ))) return ins;
  while (ins >> x)
  {
    xs.emplace_back( x );
    ins >> expect( ',' );
  }
  ins.clear();
  ins >> expect( '}' );
  return ins;
}

Notice how the function works: it expects specific input. If that input fails at any given time the stream will be set to a fail state and the function will return. Now we can use it much like I think you had planned in your question:

int main()
{
  std::string input = "{1, 2, 3, 4, 5}";
  
  std::vector<int> xs;
  std::istringstream ss( input );
  ss >> xs;
  
  for (int x : xs)
    std::cout << x << " ";
  std::cout << "\n";
}

Helper functions for the win!


PS. There is a companion function/class named accept which does nearly the same thing as expect. It just doesn’t set the fail state if the desired character is not next in the input stream.

struct accept
{
  char c;
  explicit accept( char c ) : c{c} { }
};

std::istream & operator >> ( std::istream & ins, const accept & c )
{
  if (!std::isspace( (unsigned char)c.c )) ins >> std::ws;
  if (ins.peek() == c.c) ins.get();
  return ins;
}

These two functions are the basis for a lot of very useful parsing powers.

  • Related