Suppose I have a string of numbers
"1 2 3 4 5 6"
I want to split this string and place every number into a different slot in my vector. What is the best way to go about this
Use istringstream to refer the string as a stream and >> operator to take the numbers. It will work also if the string contains newlines and tabs. Here is an example:
#include <vector>
#include <sstream> // for istringstream
#include <iostream> // for cout
using namespace std; // I like using vector instead of std::vector
int main()
{
char *s = "1 2 3 4 5";
istringstream s2(s);
vector<int> v;
int tmp;
while (s2 >> tmp) {
v.push_back(tmp);
}
// print the vector
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << endl;
}
}
#include <iostream>
#include <string>
#include <algorithm>
#include <cstdlib>
std::vector<std::string> StringToVector(std::string const& str, char const delimiter);
int main(){
std::string str{"1 2 3 4 5 6 "};
std::vector<std::string> vec{StringToVector(str, ' ')};
//print the vector
for(std::string const& item : vec){
std::cout << "[" << item << "]";
}
return EXIT_SUCCESS;
}
std::vector<std::string> StringToVector(std::string const& str, char const delimiter){
std::vector<std::string> vec;
std::string element;
//we are going to loop through each character of the string slowly building an element string.
//whenever we hit a delimiter, we will push the element into the vector, and clear it to get ready for the next element
for_each(begin(str),end(str),[&](char const ch){
if(ch!=delimiter){
element+=ch;
}
else{
if (element.length()>0){
vec.push_back(element);
element.clear();
}
}
});
//push in the last element if the string does not end with the delimiter
if (element.length()>0){
vec.push_back(element);
}
return vec;
}
g++ -std=c++0x -o main main.cpp
this has the advantage of never pushing an empty string into the vector.
you can also choose what you want the separator to be.
maybe you could write some others: one for a vector of characters or maybe the delimiter could be a string? :)
good luck!
#include <vector>
#include <string>
#include <sstream>
int str_to_int(const string& str){
stringstream io;
int out;
io<<str;
io>>out;
return out;
};
vector<int> Tokenize(string str, string delimiters = " ")
{
vector<int> tokens;
string::size_type nwpos; //position of first non white space, which means it is first real char
nwpos = str.find_first_not_of(delimiters, 0); //ignore the whitespace before the first word
string::size_type pos = str.find_first_of(delimiters, nwpos);
while (string::npos != pos || string::npos != nwpos)
{
// Found a token, add it to the vector.
tokens.push_back(str_to_int(str.substr(nwpos, pos - nwpos)));
// Skip delimiters. Note the "not_of"
nwpos = str.find_first_not_of(delimiters, pos);
// Find next "non-delimiter"
pos = str.find_first_of(delimiters, nwpos);
}
return tokens;
};
try:
#include <sstream>
#include <string>
#include <algorithm>
#include <iterator>
#include <vector>
int main()
{
// The data
std::string data = "1 2 3 4 5 6";
// data in a stream (this could be a file)
std::stringstream datastream(data);
// Copy the data from the stream into a vector.
std::vector<int> vec;
std::copy(std::istream_iterator<int>(datastream), std::istream_iterator<int>(),
std::back_inserter(vec)
);
// We can also copy the vector to the output (or any other stream).
std::copy(vec.begin(), vec.end(),
std::ostream_iterator<int>(std::cout, "\n")
);
}
Related
I am having trouble splitting words of a string into a vector. I am trying to keep track of each word with a first and last integer. I believe my main issue has to do with how I am iterating over the string.
What would be ways to improve this function?
Input: "hello there how are you"
Actual Output: "hello", "there", "how", "are"
Expected Output: "hello", "there", "how", "are", "you"
std::vector <std::string> wordChopper(std::string& s)
{
std::vector<std::string> words;
int first = 0;
int last;
std::string word;
for(unsigned int i = 0; i < s.size(); i++)
{
if(s[i] != ' ')
{
last++;
}
else
{
word = s.substr(first,last);
words.push_back(word);
first = i+1;
last = 0;
}
}
return words;
}
I suggest:
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <iterator>
using namespace std;
vector<string> wordChopper(const string & s)
{
istringstream iss(s);
return vector<string>(istream_iterator<string>{iss}, istream_iterator<string>());
}
int main()
{
for (auto & iter : wordChopper("hello there how are you"))
{
cout << iter << endl;
}
return 0;
}
I have given the widely used method in the comment. However, if you want to write your own function, I would suggest trying out something on the lines of:
std::vector<std::string> stringTokeniser(std::string originalString, char delimiter = ' ') {
std::vector<std::string> tokensVector;
std::string word;
for (const auto character : originalString) {
if (character != delimiter) {
word.push_back(character);
}
else {
tokensVector.push_back(word);
word.clear();
}
}
tokensVector.push_back(word);
return tokensVector;
}
I am having trouble splitting words of a string into a vector
There is no need for indexing, start and first positions, etc. if the words are separated by spaces.
Usage of std::stringstream accomplishes this:
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
std::vector <std::string> wordChopper(std::string s)
{
std::vector<std::string> words;
std::stringstream strm(s);
std::string word;
while (strm >> word)
words.push_back(word);
return words;
}
int main()
{
auto v = wordChopper("hello there how are you");
for (auto s : v)
std::cout << s << "\n";
}
Output:
hello
there
how
are
you
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
using namespace std;
vector<string> split_string(string s)
{
string buf;
stringstream ss(s);
vector<string> tokens;
while (ss >> buf)
tokens.push_back(buf);
return tokens;
}
int main()
{
cout << split_string("Alpha Beta Gamma");
}
when i try to split a string into a vector by using whitespaces i am not able to print out my solution.
i doesnt let me use std::cout but in my function the return value is given
why cant i use it like that? how do i fix this?
std::cout cannot take a vector, you need to iterate through the container and print each element separately, try using something like this:
int main()
{
string originalString = "Alpha Beta Gamma";
for (const auto& str : split_string(originalString))
cout << str << '\n';
return 0;
}
I am trying to make a C++ program that receives user input, and extracts the individual words in the string, e.g. "Hello to Bob" would get "Hello", "to", "Bob". Eventually, I will be pushing these into a string vector. This is the format I tried to use when designing the code:
//string libraries and all other appropriate libraries have been included above here
string UserInput;
getline(cin,UserInput)
vector<string> words;
string temp=UserInput;
string pushBackVar;//this will eventually be used to pushback words into a vector
for (int i=0;i<UserInput.length();i++)
{
if(UserInput[i]==32)
{
pushBackVar=temp.erase(i,UserInput.length()-i);
//something like words.pushback(pushBackVar) will go here;
}
}
However, this only works for the first space encountered in the string.It does not work if there are any spaces before the word (e.g. if we have "Hello my World", pushBackVar will be "Hello" after the first loop, and then "Hello my" after the second loop, when I want "Hello" and "my".) How do I fix this? Is there any other better way to extract individual words from a string? I hope I haven't confused anyone.
See Split a string in C++?
#include <string>
#include <sstream>
#include <vector>
using namespace std;
void split(const string &s, char delim, vector<string> &elems) {
stringstream ss(s);
string item;
while (getline(ss, item, delim)) {
elems.push_back(item);
}
}
vector<string> split(const string &s, char delim) {
vector<string> elems;
split(s, delim, elems);
return elems;
}
So in your case just do:
words = split(temp,' ');
You can use the operator >> direct to a microbuffer (string) to extract the word. (getline is not needed). Take a look at the function below:
vector<string> Extract(const string& Text) {
vector<string> Words;
stringstream ss(Text);
string Buf;
while (ss >> Buf)
Words.push_back(Buf);
return Words;
}
#include <algorithm> // std::(copy)
#include <iostream> // std::(cin, cout)
#include <iterator> // std::(istream_iterator, back_inserter)
#include <sstream> // std::(istringstream)
#include <string> // std::(string)
#include <vector> // std::(vector)
using namespace std;
auto main()
-> int
{
string user_input;
getline( cin, user_input );
vector<string> words;
{
istringstream input_as_stream( user_input );
copy(
istream_iterator<string>( input_as_stream ),
istream_iterator<string>(),
back_inserter( words )
);
}
for( string const& word : words )
{
cout << word << '\n';
}
}
Here I have created a vector of words from the sentence.
#include<bits/stdc++.h>
using namespace std;
int main(){
string s = "the devil in the s";
string word;
vector<string> v;
for(int i=0; i<s.length(); i++){
if(s[i]!=' '){
word+=s[i];
}
else{
v.push_back(word);
if(i<s.length()+1)
word.clear();
}
}
v.push_back(word);
for(auto i:v){
cout<<i<<endl;
}
}
I have few strings, each one contains one word and several integer numbers (One string is whole line):
Adam 2 5 1 5 3 4
John 1 4 2 5 22 7
Kate 7 3 4 2 1 15
Bill 2222 2 22 11 111
As you can see, each word/number is separated with space. Now, I want to load these data into a map, where word (name) would be the key and the value would be vector of the numbers in line. I already have key values in separated temporary stl container, so the task is to load only the integer numbers from each line to 2D vector and then merge these two into map.
The question is, is there any C++ function, which would avoid words and white spaces and get only integers from a string, or I have to search strings char-by-char like
here ?
I found only partial solution, which is not able to get more than one digit number:
vector<int> out;
for (int i = 0; i < line.length(); i++) {
if (isdigit(line.at(i))) {
stringstream const_char;
int intValue;
const_char << line.at(i);
const_char >> intValue;
out.push_back(intValue);
}
}
If every line has the format "word number number number ...", use a stringstream and skip the word by reading it.
If the current line is in line:
vector<int> out;
istringstream in(line);
string word;
in >> word;
int x = 0;
while (in >> x)
{
out.push_back(x);
}
split the string on spaces since that seems to be your delimiter. Then check that each substring contains an int with strol.
Then use stoi to convert the integer substrings to int.
If no stoi conversion can be performed (the string does not contain a number), an invalid_argument exception is thrown, so don't try to convert the name substring.
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <cstdlib>
std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim)) {
elems.push_back(item);
}
return elems;
}
std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
split(s, delim, elems);
return elems;
}
inline bool isInteger(const std::string & s)
{
if(s.empty() || ((!isdigit(s[0])) && (s[0] != '-') && (s[0] != '+'))) return false ;
char * p ;
strtol(s.c_str(), &p, 10) ;
return (*p == 0) ;
}
int main()
{
std::cout << "Hello World" << std::endl;
std::string example="Adam 2 5 1 5 3 4";
std::vector<std::string> subStrings;
subStrings = split(example, ' ');
std::string sItem;
for(std::vector<std::string>::iterator it = subStrings.begin(); it != subStrings.end(); ++it) {
sItem = *it;
if( isInteger(sItem) ){
int nItem = std::stoi (sItem);
std::cout << nItem << '\n';
}
}
return 0;
}
use find() and substr() of string Class to find the name if it is always at the beginning of the string.
std::string s = "Adam 2 5 1 5 3 4";
std::string delimiter = " ";
s.substr(0, s.find(delimiter)); //To get the name
s.erase(0, s.find(delimiter)); //To delete the name
//Repeat the mechanism with a for or a while for the numbers
I do not test this solution but I use something similar with always the label in first place.
If the name could be anywhere, I do not see how test it without check for every character.
Here is a program that demonstrates an approach to the task that can be used.
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <sstream>
#include <iterator>
int main()
{
std::string s( "Adam 2 5 1 5 3 4" );
std::map<std::string, std::vector<int>> m;
std::string key;
std::istringstream is( s );
if ( is >> key )
{
m[key] = std::vector<int>( std::istream_iterator<int>( is ),
std::istream_iterator<int>() );
}
for ( const auto &p : m )
{
std::cout << p.first << ": ";
for ( int x : p.second ) std::cout << x << ' ';
std::cout << std::endl;
}
return 0;
}
The output is
Adam: 2 5 1 5 3 4
Assuming that the name comes first, here is a function that will read the string and add to the map.
#include <map>
#include <vector>
#include <sstream>
#include <string>
#include <algorithm>
using namespace std;
typedef std::map<std::string, std::vector<int> > StringMap;
void AddToMap(StringMap& sMap, const std::string& line)
{
// copy string to stream and get the name
istringstream strm(line);
string name;
strm >> name;
// iterate through the ints and populate the vector
StringMap::iterator it = sMap.insert(make_pair(name, std::vector<int>())).first;
int num;
while (strm >> num)
it->second.push_back(num);
}
The function above adds a new entry to the map with the first read, and on subsequent reads, populates the vector.
Note that the map::insert function returns a std::pair, where the first of that pair is the iterator to the map entry that was created. So we just get the iterator, and from there, push_back the entries.
Here is a test program:
int main()
{
vector<std::string> data = { "Adam 2 5 1 5 3 4", "John 1 4 2 5 22 7",
"Kate 7 3 4 2 1 15", "Bill 2222 2 22 11 111" };
StringMap vectMap;
// Add results to map
for_each(data.begin(), data.end(),
[&](const std::string& s){AddToMap(vectMap, s); });
// Output the results
for_each(vectMap.begin(), vectMap.end(),
[](const StringMap::value_type& vt)
{cout << vt.first << " "; copy(vt.second.begin(), vt.second.end(),
ostream_iterator<int>(cout, " ")); cout << "\n"; });
}
Live example: http://ideone.com/8UlnX2
I'm having a problem with this code. After compiling with g++, I run a.out and I get a segmentation fault and no "here" displayed. The code is pretty short:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sstream>
using namespace std;
bool inWords(vector<string> words, string str);
int main()
{
cout << "here";
vector<string> words;
string str;
istringstream iss;
ifstream file("data.txt", ifstream::in);
// read in words
for(int i = 0; file >> str; /*no i++*/)
{
if(str[str.length() - 1] == '.')
str.erase( str.length()-1);
// if word has a period at the end, erase it
if(!inWords(words, str))
{
// if word is not in vector words, add it
words.push_back(str);
i++;
}
}
// output each word
for (vector<string>::size_type i = 0; i < words.size(); i++)
cout << words[i];
// return to beginning of file
file.clear();
file.seekg(0, ios::beg);
// read in sentences
// to be implemented
file.close();
return 0;
}
bool inWords(vector<string> words, string str)
{
for(int i = 0; !words[i].empty(); i++)
if(words[i] == str) { return true; }
return false;
}
As far as I know, nothing should be a problem. data.txt is definitely in the same directory as the file and I receive no arguments from the command line. Can anyone help?
It won't be before main. Try using a debugger to see where it happens (eg GDB) which are incredibly handy tool. The reason you don't see "here" is because the buffer isn't flushed. Put a << std::endl after it so that it forces output at that point.
A technicality: You can segfault before main but that will happen in a constructor. I see you have no custom objects defined/instantiated in global scope.
The technique for iterating the vector in inWords is wrong and interacts with elements past the end of the vector causing the segmentation fault.
This program immediately accesses words[0] in inWords when the words vector is empty and words[0] (the first element of the vector) does not exist yet because the size of the vector is still zero but the loop does not do anything to avoid this condition.
I think inWords could be better implemented with std::find, perhaps like this:
bool inWords(const vector<string>& words, const string& str)
{
return std::find(words.begin(), words.end(), str) != words.end();
}
Remember to #include <algorithm> to make use of std::find. I also changed the parameters to pass by const reference, so you'll need to change the forward declaration of that function. I also added an endl to the output to make it readable.
Full text of repair:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sstream>
#include <algorithm>
using namespace std;
bool inWords(const vector<string>& words, const string& str);
int main()
{
vector<string> words;
string str;
istringstream iss;
ifstream file("data.txt", ifstream::in);
// read in words
for(int i = 0; file >> str; /*no i++*/)
{
if(str[str.length() - 1] == '.')
str.erase( str.length()-1);
// if word has a period at the end, erase it
if(!inWords(words, str))
{
// if word is not in vector words, add it
words.push_back(str);
i++;
}
}
// output each word
for (vector<string>::size_type i = 0; i < words.size(); i++)
cout << words[i] << std::endl;
// return to beginning of file
file.clear();
file.seekg(0, ios::beg);
// read in sentences
// to be implemented
file.close();
return 0;
}
bool inWords(const vector<string>& words, const string& str)
{
return std::find(words.begin(), words.end(), str) != words.end();
}