2

I have a vector of strings. Need help figuring out how to convert it into vector of integers in order to be able to work with it arithmetically. Thanks!

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

int main(int argc, char* argv[]) {

    vector<string> vectorOfStrings;
    vectorOfStrings.push_back("1");
    vectorOfStrings.push_back("2");
    vectorOfStrings.push_back("3");

    for (int i=0; i<vectorOfStrings.size(); i++)
    {
        cout<<vectorOfStrings.at(i)<<endl;
    }

    vector<int> vectorOfIntegers;

    //HELP NEEDED HERE
    //CONVERSION CODE from vector<string> to vector<int> 

    int sum;
    for (int i=0; i<vectorOfIntegers.size(); i++)
    {
        sum += vectorOfIntegers.at(i);
    }
    cout<<sum<<endl;
    cin.get();

    return 0;
}
masterial
  • 2,166
  • 9
  • 33
  • 48

7 Answers7

10

There are mulitple ways of converting a string to an int.

Solution 1: Using Legacy C functionality

int main()
{
    //char hello[5];     
    //hello = "12345";   --->This wont compile

    char hello[] = "12345";

    Printf("My number is: %d", atoi(hello)); 

    return 0;
}

Solution 2: Using lexical_cast(Most Appropriate & simplest)

int x = boost::lexical_cast<int>("12345"); 

Surround by try-catch to catch exceptions.

Solution 3: Using C++ Streams

std::string hello("123"); 
std::stringstream str(hello); 
int x;  
str >> x;  
if (!str) 
{      
   // The conversion failed.      
} 
Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • `std::stringstream` can still fail even if you check for `if(!str)` : http://ideone.com/YjNY2 . That is, `else` is still failed! – Nawaz Aug 21 '11 at 15:35
  • @Nawaz: That's not failure; there's merely some data left in the stream. – Lightness Races in Orbit Aug 21 '11 at 18:11
  • If the _entire_ input must be converted for the conversion to be deemed a success, check `str.str().empty()` (and `str` is a really bad name for a `stringstream` object!) – Lightness Races in Orbit Aug 21 '11 at 18:12
  • @Tomalak: What do you call it then if not a failure? `std::stringstream` failed to convert it or report it to you properly if there is some problem! – Nawaz Aug 21 '11 at 18:13
  • @Nawaz: In C++, formatted extraction is not required to consume the _entire_ string buffer in order to be considered successful. Otherwise, sequential parsing in general would be rather difficult! – Lightness Races in Orbit Aug 21 '11 at 18:13
  • @Tomalak: So? That means `std::stringstream` would fail if you use it to convert string into int, with that code which @Als have written. – Nawaz Aug 21 '11 at 18:15
  • @Nawaz: Huh? Streams are not designed for the formatted conversion of the entire buffer. In fact they are very explicitly _not_ containers, but flows of data. They are designed for sequential extraction of segments of that flow of data. That we can provide them with a fixed initial buffer value and leverage the built-in formatting to perform lexical conversions is, strictly, a side-benefit; within that side-benefit it is true that we often want to consider success only when that entire initial buffer has been consumed. We can do that by checking what's left in the buffer afterwards. – Lightness Races in Orbit Aug 21 '11 at 18:29
  • @Tomalak: Are you disagreeing with me? If yes, then prove that the code which Als has written using std::stringstream is awesome, I will be convinced. If no, then close discussion. – Nawaz Aug 21 '11 at 18:35
  • @Nawaz: You're the one who came onto this answer with a broken assertion that `stringstream` somehow "fails" without setting failure flags. I have explained how you're wrong, and explained how you can accomplish your sub-goal. And that's the end of it. – Lightness Races in Orbit Aug 21 '11 at 18:42
  • @Tomalak: And my comment is correct, because its specifically made on the solution Als has provided. – Nawaz Aug 21 '11 at 18:43
5

Use boost::lexical_cast. And surround it with try-catch block.

try
{
   for (size_t i=0; i<vectorOfStrings.size(); i++)
   {
      vectorOfIntegers.push_back(boost::lexical_cast<int>(vectorOfStrings[i]));
   }
}
catch(const boost::bad_lexical_cast &)
{
    //not an integer 
}

Or you can use Boost.Spirit parser (which someone claims is faster than even atoi()) as:

int get_int(const std::string & s)
{
    int value = 0;
    std::string::const_iterator first = s.begin();
    bool r = phrase_parse(first,s.end(),*int_[ref(value)=_1], space);
    if ( !r || first != s.end()) throw "error"; 
    return value;
}

//Usage
int value = get_int("17823");
std::cout << value << std::endl; //prints 17823

The full demo using your code : http://ideone.com/DddL7

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • I'm sure the OP assumes decimal, but for me, lexical_cast<> lack of hexadecimal support limits its usefulness. – srking Aug 21 '11 at 17:08
  • @srking: what do you mean by `lexical_cast` lacks hexadecimal suppport? – Nawaz Aug 21 '11 at 17:10
  • see this for example: http://stackoverflow.com/questions/1070497/c-convert-hex-string-to-signed-integer Indeed, lexical_cast("0x10") coughs a hairball. – srking Aug 21 '11 at 23:18
2
#include <string>
#include <vector>

#include <iterator>
#include <algorithm>
#include <boost/lexical_cast.hpp>

using namespace std;

int stringToInteger(const std::string& s)
{
    return boost::lexical_cast<int>(s);
}

int main(int /*argc*/, char* /*argv*/[])
{
    vector<string> vectorOfStrings;

    // ..

    vector<int> vectorOfIntegers;
    std::transform(vectorOfStrings.begin(), vectorOfStrings.end(), std::back_inserter(vectorOfIntegers), stringToInteger);

    // ..
}

You can replace the implementation of stringToInteger(..) with your preferred conversion function.

Simon
  • 1,496
  • 8
  • 11
1

What about:

#include <algorithm>
#include <boost/lexical_cast.hpp>

template<typename C1, typename C2>
void castContainer(const C1& source, C2& destination)
{
    typedef typename C1::value_type source_type;
    typedef typename C2::value_type destination_type;
    destination.resize(source.size());
    std::transform(source.begin(), source.end(), destination.begin(), boost::lexical_cast<destination_type, source_type>);
}

It can convert vector<string> into vector<int>, and also other container<T1> into container2<T2>, e.g.: list -> list.

Full code:

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <string>
#include <boost/lexical_cast.hpp>

template<typename C1, typename C2>
void castContainer(const C1& source, C2& destination)
{
    typedef typename C1::value_type source_type;
    typedef typename C2::value_type destination_type;
    destination.resize(source.size());
    std::transform(source.begin(), source.end(), destination.begin(), boost::lexical_cast<destination_type, source_type>);
}

template<typename T, typename T2>
std::vector<T>& operator<<(std::vector<T>& v, T2 t)
{
    v.push_back(T(t));
    return v;
}

main(int argc, char *argv[])
{   
    std::vector<std::string> v1;
    v1 << "11" << "22" << "33" << "44";
    std::cout << "vector<string>: ";
    std::copy(v1.begin(), v1.end(), std::ostream_iterator<std::string>(std::cout, ", "));
    std::cout << std::endl;

    std::vector<int> v2;
    castContainer(v1, v2);

    std::cout << "vector<int>: ";
    std::copy(v2.begin(), v2.end(), std::ostream_iterator<int>(std::cout, ", "));
    std::cout << std::endl;
}
baziorek
  • 2,502
  • 2
  • 29
  • 43
1

Here is the working version made up using the above comments.

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

using namespace std;

int main(int argc, char* argv[]) {

    vector<string> vectorOfStrings;
    vectorOfStrings.push_back("1");
    vectorOfStrings.push_back("2");
    vectorOfStrings.push_back("3");

    for (int i=0; i<vectorOfStrings.size(); i++)
    {
        cout<<vectorOfStrings.at(i)<<endl;
    }

    vector<int> vectorOfIntegers;
    int x;
    for (int i=0; i<vectorOfStrings.size(); i++)
    {
        stringstream str(vectorOfStrings.at(i));
        str >> x;
        vectorOfIntegers.push_back(x);
    }

    int sum = 0;
    for (int i=0; i<vectorOfIntegers.size(); i++)
    {
        sum += vectorOfIntegers.at(i);
    }
    cout<<sum<<endl;
    cin.get();

    return 0;
}
masterial
  • 2,166
  • 9
  • 33
  • 48
0

There are two independent tasks.

  1. Convert a single string to an integer
  2. Having something that can convert from A to B, convert std::vector<A> to std::vector<B>

I suggest you try to do them separately, and then combine the results. If one of these tasks proves difficult, you will be able to ask a more focused question.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
0

The most general way to convert strings to integers is with stringstream and a function template. You can optionally set the base for the conversion if you're dealing with hexadecimal. The boost library would also be helpful in your example.

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

#include <sstream>
#include <stdexcept>
#include <boost/static_assert.hpp>
#include <boost/foreach.hpp>

/******************************************************************************
 * Handy string to type conversion
 * First parameter is the string to convert
 * Second optional parameter is the number base, e.g. std::hex
 * 
 * Because this is a function template, the compiler will instantiate one
 * instance of the function per type
 *****************************************************************************/
// the std::dec thingy is actually a function, so extra glue required.
typedef std::ios_base& (*ios_base_fn)( std::ios_base& str );
template <class T>
T strtotype( const std::string& s, ios_base_fn base = std::dec )
{
    // C++ can't convert 8-bit values, they are *always* treated
    // as characters. :(  At least warn the user.
    // this gives a cryptic error message, but better than nothing.
    BOOST_STATIC_ASSERT( sizeof(T) > 1 );

    T val;
    std::istringstream iss(s);
    iss >> base >> val;
    if( iss.fail() )
        throw std::runtime_error( "Error: strtotype(): Can't convert string '" + s + "' to numeric value" );
    return val;
}

using namespace std;

int main(int argc, char* argv[]) {

    vector<string> vectorOfStrings;
    vectorOfStrings.push_back("1");
    vectorOfStrings.push_back("2");
    vectorOfStrings.push_back("3");

    for (int i=0; i<vectorOfStrings.size(); i++)
    {
        cout<<vectorOfStrings.at(i)<<endl;
    }

    vector<int> vectorOfIntegers;

    for( size_t i = 0; i < vectorOfStrings.size(); i++ )
        vectorOfIntegers.push_back( strtotype<int>( vectorOfStrings[i] ));

    // or better yet, use boost_foreach
    BOOST_FOREACH( const string& s, vectorOfStrings )
        vectorOfIntegers.push_back( strtotype<int>( s ));

    int sum;
    for (int i=0; i<vectorOfIntegers.size(); i++)
    {
        sum += vectorOfIntegers.at(i);
    }
    cout<<sum<<endl;
    cin.get();

    return 0;
}

If you don't want or can't use boost, you can remove the sizeof() check in strtotype. However, be careful never to try to convert to strings to individual bytes. Doing so will fail silently by only converting the first nibble of the byte.

If you're suing GNU tools, then compile like so:

 g++ -Wall -O3 -I /path/to/boost/include main.cpp

or, if you delete the boost related bits:

 g++ -Wall -O3 main.cpp 
srking
  • 4,512
  • 1
  • 30
  • 46