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;
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 );
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. :-)
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 >> '}';
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.
C++ has since over 10 years a dedicated functionality for that. It is the
std::sregex_token_iterator
And since it is existing, and easy to handle, it should be used. The result is very often a one liner.
Please look at one potential solution:
#include <iostream>
#include <vector>
#include <regex>
#include <string>
#include <algorithm>
const std::regex re{ R"(\d+)" };
int main() {
std::string s{ "{1, 2, 3, 4, 5}" };
std::vector<int> result{};
std::transform(std::sregex_token_iterator(s.begin(), s.end(), re), {}, std::back_inserter(result), [](const std::string& v) {return std::stoi(v); });
for (const int i : result) std::cout << i << ' ';
}
If you additionally want to validate the input, you can also use a regex.
Then the solution would look like this:
#include <iostream>
#include <vector>
#include <regex>
#include <string>
#include <algorithm>
const std::regex re{ R"(\d+)" };
const std::regex rv{ R"(\{(\d+\,)*\d+\})" };
int main() {
// Read string and check input
if (std::string s{}; std::getline(std::cin, s) and std::regex_match(s, std::regex{ R"(\{(\d+\,)*\d+\})" })) {
std::vector<int> result{};
// Extract intgers
std::transform(std::sregex_token_iterator(s.begin(), s.end(), re), {}, std::back_inserter(result), [](const std::string& v) {return std::stoi(v); });
// Show debug output
for (const int i : result) std::cout << i << ' ';
}
else std::cerr << "\n***Error: Invald input\n";
}
Input check can be relaxed with other regexes.
Related
How to take delimiter as -0 between array size and array value of integer in c++.
for example: 2 -0 1,2 -0
array-size '-0' 1st array values separated by "," '-0'
If I take input in a string using get line then how to separate size from the string and take input of array.
int main(){
string s,t;
getline(cin,s);
stringstream x(s);
while(getline(x,t,"-0")){
cout << t;
}
}
I cannot understand delimiter concept properly from google, can you explain it and take input in the: " array-size '-0' 1st array values separated by ',' '-0' " form.
Given the input, the easiest way is to check the string token as being the -0 delimiter.
#include <iostream>
#include <sstream>
#include <string>
using std::cin;
using std::cout;
using std::getline;
using std::string;
using std::stringstream;
int main(){
string s,t;
getline(cin, s);
stringstream x(s);
char const* sep = "";
while (x >> t) {
if (t == "-0") {
cout << sep << "<DELIMITER>";
} else {
cout << sep << t;
}
sep = " ";
}
cout << "\n";
}
That gives:
echo '2 -0 1,2 -0' | ./a.out
2 <DELIMITER> 1,2 <DELIMITER>
Update
Storing the values into a std::vector<int>.
#include <cstddef>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>
using std::cin;
using std::cout;
using std::getline;
using std::ostream;
using std::runtime_error;
using std::size_t;
using std::stoi;
using std::string;
using std::stringstream;
using std::vector;
static auto MakeVecInt(string line) -> vector<int> {
auto result = vector<int>();
auto ss = stringstream(line);
auto value = string{};
while(getline(ss, value, ',')) {
result.push_back(stoi(value));
}
return result;
}
static auto operator<<(ostream& out, vector<int> const& v) -> ostream& {
char const* sep = "";
for (auto i : v) {
out << sep << i;
sep = " ";
}
return out;
}
int main() {
auto line = string{};
getline(cin, line);
auto ss = stringstream(line);
auto count = size_t{};
auto delimiter1 = string{};
auto values = string{};
auto delimiter2 = string{};
if (!(ss >> count >> delimiter1 >> values >> delimiter2))
throw runtime_error("bad input");
if (delimiter1 != "-0" || delimiter2 != "-0")
throw runtime_error("bad input");
auto vec = MakeVecInt(values);
if (count != vec.size())
throw runtime_error("bad input");
cout << vec << "\n";
}
Update
Note in the above code the vector<int> output routine:
static auto operator<<(ostream& out, vector<int> const& v) -> ostream&
It outputs each element in the vector, with a space between each element. It does not output a space before the first element.
I have the following struct:
struct msg_latency{
double time;
string data;
};
I have a vector of strings which contains strings like:
"2344.5435345 hello world\n:"
I have to iterate the vector of the strings and convert each string to msg_latency struct.
vector<msg_latency> vec_msg
convert_str2struct(vector<string> str_vec, vector<msg_latency> msg_vec)
{
vector<string>::iterator msg_it;
for(msg_it=str_vec.begin(); msg_it!= str_vec.end(); ++msg_it)
{
///TODO
}
}
While in the todo I want to write something like:
msg_vec[i].time= *msg_it.substr(0, *msg_it.find(" "));
msg_vec[i].data= *msg_it;
How can I initialize the msg_vec as I describred above?
Can I do something like (in the TODO):
msg_vec.push_back({*msg_it.substr(0, *msg_it.find(" ")), *msg_it})?
As suggested in comments, this is an ideal application for std::transform and std::istringstream.
If I understand your string format you can use std::getline to read the remaining part of string after reading the double:
#include <string>
#include <vector>
#include <sstream>
#include <algorithm>
#include <iostream>
msg_latency
convert(const std::string& str) {
msg_latency msg;
std::istringstream ss(str);
ss >> msg.time >> std::ws; // read double and whitespace separator
getline(ss, msg.data); // read the rest of the string
return msg;
}
std::vector<msg_latency>
convert_str2struct(const std::vector<std::string>& str_vec) {
std::vector<msg_latency> ret(str_vec.size());
std::transform(str_vec.begin(), str_vec.end(), ret.begin(), convert);
return ret;
}
int main() {
auto vec_str = std::vector<std::string>{"2344.5435345 hello world\n", "42.0 foo\n"};
auto vec_msg = convert_str2struct(vec_str);
for (const auto& msg : vec_msg) {
std::cout << msg.time << "\n";
std::cout << msg.data << "\n";
std::cout << "\n";
}
}
Live demo.
I realize that this question may have been asked several times in the past, but I am going to continue regardless.
I have a program that is going to get a string of numbers from keyboard input. The numbers will always be in the form "66 33 9" Essentially, every number is separated with a space, and the user input will always contain a different amount of numbers.
I'm aware that using 'sscanf' would work if the amount of numbers in every user-entered string was constant, but this is not the case for me. Also, because I'm new to C++, I'd prefer dealing with 'string' variables rather than arrays of chars.
I assume you want to read an entire line, and parse that as input. So, first grab the line:
std::string input;
std::getline(std::cin, input);
Now put that in a stringstream:
std::stringstream stream(input);
and parse
while(1) {
int n;
stream >> n;
if(!stream)
break;
std::cout << "Found integer: " << n << "\n";
}
Remember to include
#include <string>
#include <sstream>
The C++ String Toolkit Library (Strtk) has the following solution to your problem:
#include <iostream>
#include <string>
#include <deque>
#include <algorithm>
#include <iterator>
#include "strtk.hpp"
int main()
{
std::string s = "1 23 456 7890";
std::deque<int> int_list;
strtk::parse(s," ",int_list);
std::copy(int_list.begin(),
int_list.end(),
std::ostream_iterator<int>(std::cout,"\t"));
return 0;
}
More examples can be found Here
#include <string>
#include <vector>
#include <iterator>
#include <sstream>
#include <iostream>
int main() {
std::string input;
while ( std::getline( std::cin, input ) )
{
std::vector<int> inputs;
std::istringstream in( input );
std::copy( std::istream_iterator<int>( in ), std::istream_iterator<int>(),
std::back_inserter( inputs ) );
// Log process:
std::cout << "Read " << inputs.size() << " integers from string '"
<< input << "'" << std::endl;
std::cout << "\tvalues: ";
std::copy( inputs.begin(), inputs.end(),
std::ostream_iterator<int>( std::cout, " " ) );
std::cout << std::endl;
}
}
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
using namespace std;
int ReadNumbers( const string & s, vector <int> & v ) {
istringstream is( s );
int n;
while( is >> n ) {
v.push_back( n );
}
return v.size();
}
int main() {
string s;
vector <int> v;
getline( cin, s );
ReadNumbers( s, v );
for ( int i = 0; i < v.size(); i++ ) {
cout << "number is " << v[i] << endl;
}
}
// get string
std::string input_str;
std::getline( std::cin, input_str );
// convert to a stream
std::stringstream in( input_str );
// convert to vector of ints
std::vector<int> ints;
copy( std::istream_iterator<int, char>(in), std::istream_iterator<int, char>(), back_inserter( ints ) );
Here is how to split your string into strings along the spaces. Then you can process them one-by-one.
Generic solution for unsigned values (dealing with prefix '-' takes an extra bool):
template<typename InIter, typename OutIter>
void ConvertNumbers(InIter begin, InIter end, OutIter out)
{
typename OutIter::value_type accum = 0;
for(; begin != end; ++begin)
{
typename InIter::value_type c = *begin;
if (c==' ') {
*out++ = accum; accum = 0; break;
} else if (c>='0' && c <='9') {
accum *= 10; accum += c-'0';
}
}
*out++ = accum;
// Dealing with the last number is slightly complicated because it
// could be considered wrong for "1 2 " (produces 1 2 0) but that's similar
// to "1 2" which produces 1 0 2. For either case, determine if that worries
// you. If so: Add an extra bool for state, which is set by the first digit,
// reset by space, and tested before doing *out++=accum.
}
Try strtoken to separate the string first, then you will deal with each string.
Java has this easy method to count the tokens that you tokenize:
import java.util.*;
public class Program
{
public static void main(String[] args)
{
String str =
"This is/some text/that I am/parsing/using StringTokenizer/.";
StringTokenizer strTok =
new StringTokenizer(str, "/", false);
System.out.println("Count...");
System.out.println(strTok.countTokens());
}
}
Output:Count...6
Is there any easy way to do in C++?
You could use std::istringstreamclass along with function std::getline. For example
#include <iostream>
#include <sstream>
#include <string>
int main()
{
char s[] = "This is/some text/that I am/parsing/using StringTokenizer/.";
std::istringstream is( s );
size_t count = 0;
std::string line;
while ( std::getline( is, line, '/' ) ) ++count;
std::cout << "There are " << count << " tokens" << std::endl;
}
The output is
There are 6 tokens
Or
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
int main()
{
char s[] = "This is/some text/that I am/parsing/using StringTokenizer/.";
std::istringstream is( s );
std::vector<std::string> v;
std::string line;
while ( std::getline( is, line, '/' ) ) v.push_back( line );
std::cout << "There are " << v.size() << " tokens" << std::endl;
}
To build again the string from the vector you could use for example the following code
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
int main()
{
char s[] = "This is/some text/that I am/parsing/using StringTokenizer/.";
std::istringstream is( s );
std::vector<std::string> v;
std::string line;
while ( std::getline( is, line, '/' ) ) v.push_back( line );
std::cout << "There are " << v.size() << " tokens" << std::endl;
std::string s1;
bool first = true;
for ( const std::string &t : v )
{
if ( first ) first = false;
else s1 += '/';
s1 += t;
}
std::cout << s1 << std::endl;
}
Or you could use standard algorithm std::replace declared in header <algorithm> to replace one delimeter to another in the original string.
If your compiler does not support the range based for loop then you can write instead
for ( std::vector<std::string>::size_type i = 0; i < v.size(); i++ )
{
if ( i != 0 ) s1 += '/';
s1 += v[i];
}
You could try this:
std::vector<std::string> v(std::istream_iterator<std::string>(std::cin), {});
std::cout << "Count..." << v.size() << "\n";
This will of course tokenize at spaces, not at arbitrary separators. To split on arbitary separators, we need std::getline, but now we don't have an easy istream_iterator. Thankfully, this is a solved problem. So we write:
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
namespace detail
{
template <char Sep = '\n'>
class Line : public std::string
{
friend std::istream & operator>>(std::istream & is, Line & line)
{
return std::getline(is, line, Sep);
}
};
}
int main()
{
std::vector<std::string> v(std::istream_iterator<detail::Line<'/'>>(std::cin), {});
std::cout << "Count..." << v.size() << "\n";
for (auto const & s : v) std::cout << s << "\n";
}
If you want to tokenize an existing string rather than the standard input, use a string stream, i.e. replace std::cin with iss, where we have:
#include <sstream>
std::istringstream iss(my_input_string);
I'm pretty new to C++ and was looking for a good way to pull the data out of this line.
A sample line that I might need to tokenise is
f 11/65/11 16/70/16 17/69/17
I have a tokenisation method that splits strings into a vector as delimited by a string which may be useful
static void Tokenise(const string& str, vector<string>& tokens, const string& delimiters = " ")
The only way I can think of doing it is to tokenise with " " as a delimiter, remove the first item from the resulting vector, then tokenise each part by itself. Is there a good way to do this all in one?
I see the question is tagged as C++ but the absolutely easiest way to do this is with scanf
int indices[3][3];
sscanf(buffer, "f %d/%d/%d %d/%d/%d %d/%d/%d", &indices[0][0], &indices[0][1],...);
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
class parse_error : public std::exception {};
template< typename Target >
inline Target convert_to(const std::string& value)
{
std::istringstream iss(value);
Target target;
iss >> target >> std::ws;
if(!iss || !iss.eof()) throw parse_error();
return target;
}
template< typename T >
inline T read_delimited_value(std::istream& is, char delim)
{
std::string value;
std::getline(is,value,delim);
if(!is) throw parse_error();
return convert_to<T>(value);
}
template< typename It >
inline void output(std::ostream& os, It begin, It end)
{
while(begin!=end)
os << *begin++ << ' ';
}
int main()
{
std::vector<int> values;
const std::string line = "f 11/65/11 16/70/16 17/69/17";
std::istringstream iss(line);
std::string value;
std::getline(iss,value,' ');
if(value!="f" || !iss) throw parse_error();
while(iss.good()) {
values.push_back( read_delimited_value<int>(iss,'/') );
values.push_back( read_delimited_value<int>(iss,'/') );
values.push_back( read_delimited_value<int>(iss,' ') );
}
if(!iss.eof()) throw parse_error();
output( std::cout, values.begin(), values.end() );
std::cout << '\n';
return 0;
}
You should take a look at Boost.Tokenizer and especially this:
// char_sep_example_1.cpp
#include <iostream>
#include <boost/tokenizer.hpp>
#include <string>
int main()
{
std::string str = ";;Hello|world||-foo--bar;yow;baz|";
typedef boost::tokenizer<boost::char_separator<char> >
tokenizer;
boost::char_separator<char> sep("-;|");
tokenizer tokens(str, sep);
for (tokenizer::iterator tok_iter = tokens.begin();
tok_iter != tokens.end(); ++tok_iter)
std::cout << "<" << *tok_iter << "> ";
std::cout << "\n";
return EXIT_SUCCESS;
}
Judging from the sample line you can use two delimiters ' ' and '/' and you will get all your numbers.
static void Tokenise(const string& str, vector<string>& tokens, const string& delimiters = " /")
You can remove easily the first part until the first blank or the just after the f ( you can get the rest after the first blank with
istringstream iss( line );
std::getline( iss, restStr ,' ' )
Then you can use your tokenize function first on blank space and then on '/', or just use a set of std::getline and istringstreams in one loop.
int main()
{
std::string s = "f 1/2/3 4/4/2";
std::istringstream issLine( s );
std::string result;
// remove the first "f"
std::getline( issLine, result, ' ' );
// parse blanks
while( std::getline( issLine, result, ' ' ) )
{
std::istringstream issToken( result );
std::string token;
//parse content
while( std::getline( issToken, token, '/' ))
{
std::cout << token << ',';
// add your data in whatever you want
}
std::cout << std::endl;
}
}