Iteration into std::vector<string> - c++

I have a vector of string parameters...
|name1|value1|name2|value2|...
I wanna iterate and cache the name into a string and add it into a vector of names and make the same thing with the value. They are in a std::vector<string>.
I do it:
std::vector<string> names;
std::vector<string> values;
std::vector<string>::iterator pit = p.begin();
while(pit != p.end()){
string name = *pit;
pit++;
string value = *pit;
pit++;
names.push_back(name);
values.push_back(value);
}
But it returns an access violation in vector. It is accessing a bad location returning a <BadPtr>.
How to do this iteration?
Does it have a way of do it using for each?

Check this out:
std::vector<string> names;
std::vector<string> values;
std::vector<string>::iterator pit = p.begin();
while(pit != p.end()){
string name = *pit;
pit++;
if(pit == p.end())
break;
string value = *pit;
pit++;
names.push_back(name);
values.push_back(name);
}
As i said in my comment that problem could be, you didn't put any checking after incrementing pit second time.

Here is a demonstrative program that shows how it can be done using standard algorithm std::partition_copy
#include <iostream>
#include <vector>
#include <string>
#include <iterator>
#include <algorithm>
int main()
{
std::vector<std::string> p = { "name1", "value1", "name2", "value2" };
std::vector<std::string> names;
std::vector<std::string> values;
names.reserve( ( p.size() + 1 ) / 2 );
values.reserve( p.size() / 2 );
unsigned int i = 0;
std::partition_copy( p.begin(), p.end(),
std::back_inserter( names ),
std::back_inserter( values ),
[&]( const std::string & ) { return i ^= 1; } );
for ( const auto &s : p ) std::cout << s << ' ';
std::cout << std::endl;
for ( const auto &s : names ) std::cout << s << ' ';
std::cout << std::endl;
for ( const auto &s : values ) std::cout << s << ' ';
std::cout << std::endl;
return 0;
}
The program output is
name1 value1 name2 value2
name1 name2
value1 value2
The same can be done using the range based for statement
#include <iostream>
#include <vector>
#include <string>
int main()
{
std::vector<std::string> p = { "name1", "value1", "name2", "value2" };
std::vector<std::string> names;
std::vector<std::string> values;
names.reserve( ( p.size() + 1 ) / 2 );
values.reserve( p.size() / 2 );
unsigned int i = 0;
for ( const std::string &s : p )
{
if ( i ^= 1 ) names.push_back( s );
else values.push_back( s );
}
for ( const auto &s : p ) std::cout << s << ' ';
std::cout << std::endl;
for ( const auto &s : names ) std::cout << s << ' ';
std::cout << std::endl;
for ( const auto &s : values ) std::cout << s << ' ';
std::cout << std::endl;
return 0;
}
Thus your loop can look more simpler like
unsigned int i = 0;
for ( const std::string &s : p )
{
if ( i ^= 1 ) names.push_back( s );
else values.push_back( s );
}
As you see the body of the loop consists only from two statements instead of six or eight statements if to use your approach.

Related

map,vector in c++ [duplicate]

This question already has answers here:
How do I print out the contents of a vector?
(31 answers)
Closed 2 years ago.
error: no match for ‘operator<<’ (operand types are ‘std::basic_ostream’ and ‘std::pair<const std::__cxx11::basic_string, std::vector >’)
i want to same key and mutiple values, for example key is 10 values are 2,3,4
but "*iter" is wrong..
how to cout map,vector in c++?
In your code snippet the value of the expression *iter is an object of the type std::pair<std::string, std::vector<int>> for which the operator << is not defined.
And the error message
error: no match for ‘operator<<’ (operand types are ‘std::basic_ostream’ and
‘std::pair<const std::__cxx11::basic_string, std::vector >’)
says about this.
The simplest way is to use the range-based for loop.
Here is a demonstrative program.
#include <iostream>
#include <string>
#include <vector>
#include <map>
int main()
{
std::map<std::string, std::vector<int>> m;
m["10"].assign( { 2, 3, 4 } );
for ( const auto &p : m )
{
std::cout << p.first << ": ";
for ( const auto &item : p.second )
{
std::cout << item << ' ';
}
std::cout << '\n';
}
return 0;
}
The program output is
10: 2 3 4
If you want to write ordinary for-loops using iterators then the loops can look the following way.
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <iterator>
int main()
{
std::map<std::string, std::vector<int>> m;
m["10"].assign( { 2, 3, 4 } );
for ( auto outer_it = std::begin( m ); outer_it != std::end( m ); ++outer_it )
{
std::cout << outer_it->first << ": ";
for ( auto inner_it = std::begin( outer_it->second );
inner_it != std::end( outer_it->second );
++inner_it )
{
std::cout << *inner_it << ' ';
}
std::cout << '\n';
}
return 0;
}
Again the program output is
10: 2 3 4
I suggest using structured bindings and range-based for loops:
std::map<std::string,std::vector<int>> m;
for (auto&[str, vec] : m) { // bind str to "first" in the pair and vec to "second"
std::cout << str << ':';
for(auto lineno : vec) std::cout << ' ' << lineno;
std::cout << '\n';
}
You can define how to print things via std::ostream like this:
#include <iostream>
#include <map>
#include <vector>
#include <string>
// define how to print std::pair<std::string, std::vector<int>>
std::ostream& operator<<(std::ostream& stream, const std::pair<std::string, std::vector<int>>& pair) {
stream << "(" << pair.first << ", {";
bool first = true;
for (int e : pair.second) {
if (!first) stream << ", ";
stream << e;
first = false;
}
stream << "})";
return stream;
}
int main(void) {
std::string yytext = "hoge";
int lineno = 42;
// below is copied from the question
std::map<std::string,std::vector<int>> m;
m[yytext].push_back(lineno);
std::map<std::string,std::vector<int>>::iterator iter;
for (iter=m.begin(); iter!=m.end(); iter++){
std::cout<<iter->first<<":"<<*iter<<std::endl;}
}

How to search vector element and replace it? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
#include <iostream>
#include <string>
#include <vector>
#include <stdlib.h>
using namespace std;
void print(vector<string> v) {
for (unsigned int i = 0; i < v.size(); i++) {
cout << "[" << i << "] " << v[i] << "\n";
}
}
int main(){
vector<string> v(5);
v[0] = "Egg";
v[1] = "Milk";
v[2] = "Sugar";
v[3] = "Chocolate";
v[4] = "Flour";
print(v);
system("pause");
}
How do I make a loop that searches for the item, "sugar" and replace it with "honey."? Sry, im new to vectors
If you want to replace the first instance of the string (if it exists) you can use std::find then assign to the iterator that is returned.
std::vector<std::string> v {"Egg", "Milk", "Sugar", "Chocolate", "Flour"};
auto itMatch = std::find(v.begin(), v.end(), "Sugar");
if (itMatch != v.end())
*itMatch = "Honey";
If you'd like to replace all instances
std::replace(v.begin(), v.end(), "Sugar", "Honey");
You can use the standard alfgorithm std::find. For example
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
int main()
{
std::vector<std::string> v =
{
"Egg", "Milk", "Sugar", "Chocolate", "Flour"
};
const char *src = "Sugar";
const char *dsn = "Honey";
auto it = std::find( v.begin(), v.end(), src );
if ( it != v.end() ) *it = dsn;
for ( const auto &s : v ) std::cout << s << ' ';
std::cout << std::endl;
return 0;
}
The program output is
Egg Milk Honey Chocolate Flour
If you want to replace all occurences of "Sugar" then you can use the standard algorithm std::replace.
For example
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
int main()
{
std::vector<std::string> v =
{
"Egg", "Milk", "Sugar", "Chocolate", "Flour", "Sugar"
};
const char *src = "Sugar";
const char *dsn = "Honey";
std::replace( v.begin(), v.end(), src, dsn );
for ( const auto &s : v ) std::cout << s << ' ';
std::cout << std::endl;
return 0;
}
The program output is
Egg Milk Honey Chocolate Flour Honey
If you mean the substitution only in the function print within the loop then the function can look the following way
#include <iostream>
#include <vector>
#include <string>
void print( const std::vector<std::string> &v,
const std::string &src = "Sugar",
const std::string &dsn = "Honey" )
{
for ( std::vector<std::string>::size_type i = 0; i < v.size(); i++ )
{
std::cout << "[" << i << "] " << ( v[i] == src ? dsn : v[i] ) << "\n";
}
}
int main()
{
std::vector<std::string> v =
{
"Egg", "Milk", "Sugar", "Chocolate", "Flour"
};
print( v );
return 0;
}
Its output is
[0] Egg
[1] Milk
[2] Honey
[3] Chocolate
[4] Flour

Quickest Way to parse a string of numbers into a vector of ints

I'm wondering what the quickest way to parse a string of numbers into a vector of ints. My situation is that I will have millions of lines of data, formatted like this:
>Header-name
ID1 1 1 12
ID2 3 6 234
.
.
.
>Header-name
ID1 1 1 12
ID2 3 6 234
.
.
.
I would like to discard the "Header-name" field (or maybe use it for sorting later on), and then ignore the ID field and then place the remaining three ints into a vector.
I realize that I could just used boost split and then lexical cast in a couple of for loops with logic to ignore certain data, but I'm not sure if that will give me the quickest solution. I've looked at boost spirit but I don't really understand how to use it. Boost or STL are all ok.
Do you have to use boost?
I've used this function for a while. I believe I got it out of Accelerated C++ and have used it since. Your delimiter seems to be a tab, or multiple white spaces. If you pass the delimiter a " " it might work. I think it will depend on what's actually there though.
std::vector<std::string> split( const std::string& line, const std::string& del )
{
std::vector<std::string> ret;
size_t i = 0;
while ( i != line.size() ) {
while ( ( i != line.size() ) && ( line.substr(i, 1) == del ) ) {
++i;
}
size_t j = i;
while ( ( j != line.size() ) && ( line.substr(j, 1) != del ) ) {
++j;
}
if ( i != j ) {
ret.push_back( line.substr( i, j - i ) );
i = j;
}
}
return ret;
}
You can get each line with this:
int main() {
std::string line;
std::vector<std::string> lines;
while ( std::getline( std::cin, line ) ) {
lines.push_back( line );
}
for ( auto it = lines.begin(); it != lines.end(); it++ ) {
std::vector<string> vec = split( (*it) );
// Do something
}
}
You can get it to return std::vector with a quick modification.
Make each string an int with atoi( myString.c_str() )
Also you'll want to put a check in to skip the headers. Should be trivial.
Note that I've not compiled that above. ;)
On this specific problem, if you want the quickest, I would recommend manual parsing 1 char at a time. Boost Spirit would probably come as a close second and save you lots of ugly code.
Manual parsing one char at a time is key to high speed, as even well optimized converters like atoi and strtol have to deal with many different numeric representations while your example seems to imply that you are only interested in plain unsigned integers. Formatted IOs (scanf, operator<<, etc.) are very slow. Reading lines into intermediate strings will probably have a visible cost.
Your problem is simple enough to parse manually, assuming that the header lines do not contain any '\t' (and assuming that there aren't any IO or format errors):
#include <iostream>
#include <sstream>
#include <vector>
#include <string>
std::vector<unsigned> parse(std::istream &is)
{
bool skipField = true;
char c;
unsigned value = 0;
std::vector<unsigned> result;
while (is.get(c))
{
if (('\t' == c) || ('\n' == c))
{
if (!skipField)
{
result.push_back(value);
}
skipField = ('\n' == c);
value = 0;
}
else if (!skipField)
{
value *= 10;
value += (c - '0');
}
}
return result;
}
int main()
{
const std::string data = ">Header-name\nID1\t1\t1\t12\nID2\t3\t6\t234\n";
std::istringstream is(data);
const std::vector<unsigned> v = parse(is);
for (unsigned u: v)
{
std::cerr << u << std::endl;
}
}
As always, with delightfully underspecified questions like this, there's not a lot more than just showing "a way" to do "a thing". In this case, I used Boost Spirit (because you mentioned it):
Parsing into flat containers
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted.hpp>
#include <map>
std::string const input(
">Header - name1\n"
"ID1 1 1 12\n"
"ID2 3 6 234\n"
">Header - name2\n"
"ID3 3 3 14\n"
"ID4 5 8 345\n"
);
using Header = std::string;
using Container = std::vector<int>;
using Data = std::map<Header, Container>;
int main()
{
namespace qi = boost::spirit::qi;
auto f(input.begin()), l(input.end());
Data data;
bool ok = qi::phrase_parse(f, l,
*(
'>' >> qi::raw[*(qi::char_ - qi::eol)] >> qi::eol
>> *(!qi::char_('>') >> qi::omit[qi::lexeme[+qi::graph]] >> *qi::int_ >> qi::eol)
), qi::blank, data);
if (ok)
{
std::cout << "Parse success\n";
for (auto const& entry : data)
{
std::cout << "Integers read with header '" << entry.first << "':\n";
for (auto i : entry.second)
std::cout << i << " ";
std::cout << "\n";
}
}
else
{
std::cout << "Parse failed\n";
}
if (f != l)
std::cout << "Remaining input: '" << std::string(f, l) << "'\n";
}
Prints
Parse success
Integers read with header 'Header - name1':
1 1 12 3 6 234
Integers read with header 'Header - name2':
3 3 14 5 8 345
Parsing into nested containers
Of course, if you wanted separate vectors for each line (don't expect efficiency) then you can simply replace the typedef:
using Container = std::list<std::vector<int> >; // or any other nested container
// to make printing work without further change:
std::ostream& operator<<(std::ostream& os, std::vector<int> const& v)
{
os << "[";
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(os, " "));
return os << "]";
}
Prints
Parse success
Integers read with header 'Header - name1':
[1 1 12 ] [3 6 234 ]
Integers read with header 'Header - name2':
[3 3 14 ] [5 8 345 ]
You can use something like the following only instead of the string array I used you will get strings from a file
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <iterator>
int main()
{
std::string s[] = { "ID1 1 1 12", "ID2 3 6 234" };
std::vector<int> v;
for ( const std::string &t : s )
{
std::istringstream is( t );
std::string tmp;
is >> tmp;
v.insert( v.end(), std::istream_iterator<int>( is ),
std::istream_iterator<int>() );
}
for ( int x : v ) std::cout << x << ' ';
std::cout << std::endl;
return 0;
}
The output is
1 1 12 3 6 234
As for the header then you can check whether tmp is a header and if so you will skip this record.
Here is a simplified version
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <iterator>
int main()
{
std::string s[] =
{
"ID1 1 1 12",
">Header-name",
"ID2 3 6 234"
};
std::vector<int> v;
for ( const std::string &t : s )
{
std::istringstream is( t );
std::string tmp;
is >> tmp;
if ( tmp[0] == '>' ) continue;
v.insert( v.end(), std::istream_iterator<int>( is ),
std::istream_iterator<int>() );
}
for ( int x : v ) std::cout << x << ' ';
std::cout << std::endl;
return 0;
}
The output will be the same as above.

Taking lists as input till enter is pressed

I am having an integer N.and next N lines contain lists that can have distinct elements from 1-100.But i am not provided length of each list.How to handle this type of input.
If say i have vector > mylist;
I need to populate this list with those lists seperated by just next line.
Say if N=3
1 2 3
4
5 6
Then mylist[0]=[1,2,3] , mylist[1]=[4] , mylist[2]=[5,6].
How to do it in c++?
Mycode : Not correct but i tried.
int main(){
int t;
cin>>t;
cin.ignore();
while(t--){
int n;
cin>>n;
cin.ignore();
lists_t lists;
std::string record;
while ( std::getline( std::cin, record ) &&
record.find_first_not_of( ' ' ) != std::string::npos && lists.size()!=n)
{
std::istringstream is( record );
lists.push_back( std::vector<int>( std::istream_iterator<int>( is ),
std::istream_iterator<int>() ) );
}
for ( const auto &l : lists )
{
for ( int x : l ) std::cout << x << ' ';
std::cout << std::endl;
}
}
}
The problem is that if i enter t=1 and n=3 then instead of following n lines it takes 4 lines and then display the data.Why ?
You can use standard function std::getline ans string stream std::stringstream and of course the container std::list itself.
Here is an example
#include <iostream>
#include <iterator>
#include <list>
#include <sstream>
#include <string>
int main()
{
std::list<std::list<int>> lst;
std::string record;
while ( std::getline( std::cin, record ) &&
record.find_first_not_of( ' ' ) != std::string::npos )
{
std::istringstream is( record );
lst.push_back( std::list<int>( std::istream_iterator<int>( is ),
std::istream_iterator<int>() ) );
}
for ( const auto &l : lst )
{
for ( int x : l ) std::cout << x << ' ';
std::cout << std::endl;
}
return 0;
}
The output is
1 2 3
4
5 6
if the input was the same lines.
If you need to enter a given number of lines then the code could look the following way
#include <iostream>
#include <iterator>
#include <list>
#include <sstream>
#include <string>
int main()
{
std::list<std::list<int>> lst;
std::string record;
size_t n;
std::cin >> n;
std::cin.ignore();
while ( n-- &&
std::getline( std::cin, record ) &&
record.find_first_not_of( ' ' ) != std::string::npos )
{
std::istringstream is( record );
lst.push_back( std::list<int>( std::istream_iterator<int>( is ),
std::istream_iterator<int>() ) );
}
for ( const auto &l : lst )
{
for ( int x : l ) std::cout << x << ' ';
std::cout << std::endl;
}
return 0;
}
The input could look as
3
1 2 3
4
5 6

Counting the tokens when you tokenize the string in C++?

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);