I need to write to a bunch of files simultaneously, so I decided to use map <string, ofstream>.
map<string, ofstream> MyFileMap;
I take a vector<string> FileInd, which consists of, say "a" "b" "c", and try to open my files with:
for (vector<string>::iterator it = FileInd.begin(); iter != FileInd.end(); ++it){
...
MyFileMap[*it].open("/myhomefolder/"+(*it)+".");
}
I get the error
request for member 'open' in ..... , which is of non-class type 'std::ofstream*'
I've tried to switch to
map<string, ofstream*> MyFileMap;
But it didn't work either.
Could anyone help?
Thanks.
Clarification:
I've tried both
map<string, ofstream> MyFileMap;
map<string, ofstream*> MyFileMap;
with both
.open
->open
neither of 4 variants work.
Solution (suggested in Rob's code below):
Basically, I forgot "new", the following works for me:
map<string, ofstream*> MyFileMap;
MyFileMap[*it] = new ofstream("/myhomefolder/"+(*it)+".");
std::map<std::string, std::ofstream> can't possibly work, because std::map requires its data type to be Assignable, which std::ofstream isn't. In the alternative, the data type must be a pointer to ofstream -- either a raw pointer or a smart pointer.
Here is how I would do it, using C++11 features:
#include <string>
#include <map>
#include <fstream>
#include <iostream>
#include <vector>
int main (int ac, char **av)
{
// Convenient access to argument array
std::vector<std::string> fileNames(av+1, av+ac);
// If I were smart, this would be std::shared_ptr or something
std::map<std::string, std::ofstream*> fileMap;
// Open all of the files
for(auto& fileName : fileNames) {
fileMap[fileName] = new std::ofstream("/tmp/xxx/"+fileName+".txt");
if(!fileMap[fileName] || !*fileMap[fileName])
perror(fileName.c_str());
}
// Write some data to all of the files
for(auto& pair : fileMap) {
*pair.second << "Hello, world\n";
}
// Close all of the files
// If I had used std::shared_ptr, I could skip this step
for(auto& pair : fileMap) {
delete pair.second;
pair.second = 0;
}
}
and the 2nd verse, in C++03:
#include <string>
#include <map>
#include <fstream>
#include <iostream>
#include <vector>
int main (int ac, char **av)
{
typedef std::map<std::string, std::ofstream*> Map;
typedef Map::iterator Iterator;
Map fileMap;
// Open all of the files
std::string xxx("/tmp/xxx/");
while(av++,--ac) {
fileMap[*av] = new std::ofstream( (xxx+*av+".txt").c_str() );
if(!fileMap[*av] || !*fileMap[*av])
perror(*av);
}
// Write some data to all of the files
for(Iterator it = fileMap.begin(); it != fileMap.end(); ++it) {
*(it->second) << "Hello, world\n";
}
// Close all of the files
for(Iterator it = fileMap.begin(); it != fileMap.end(); ++it) {
delete it->second;
it->second = 0;
}
}
Related
In order to compare if two string contain a same char, I was trying to loop through a string a and put the chars into a map.
So this is what I did.
string a = "abc";
unordered_map<char,int> m;
for (auto i:a){
m.insert(i,1);
}
But then there is an error:
no matching function for call to ‘std::unordered_map<char, int>::insert(char&, int)’
I don't quite understand what can I do here. Hope someone can help!
The problem in your code is that you try to insert a which is a std::string into an std::unordered_map<char, int> - you should be inserting i which is a char (each char from std::string a).
Moreover, even if you correctly used
m.insert(a,1);
it wouldn't compile because std::unordered_map::insert accepts a std::pair not 2 arguments from the template type. So you would need:
std::unordered_map<char, int> char_map;
char_map.insert(std::make_pair(c, 1));
Want you want to achieve can be done with std::set (if you don't care about the order of objects - chars - stored inside it)
#include <iostream>
#include <string>
#include <unordered_set>
int main()
{
std::string a = "abc";
std::unordered_set<char> char_set;
for (auto c : a)
char_set.insert(c);
for (auto c : char_set)
std::cout << c << ' ';
}
http://cpp.sh/3zrgr
Unfortunately you need to call std::make_pair first:
#include <iostream>
#include <unordered_map>
int main()
{
std::string a = "abc";
std::unordered_map<char,int> m;
for (int i = 0; i < a.size(); ++i)
m.insert(std::make_pair(a[i],1));
}
I've created a game meant for younger audiences and am trying to filter out profanity and offensive names
#include <iostream>
#include <vector>
bool isBanned( std::string text ) {
std::vector bannedSent = {
"Profanity1",
"Profanity2",
"Profanity3",
"Profanity4"
};
for(auto &i : bannedSent) {
if(text == i) { return true; }
}
return false;
}
I'm getting a compiler error talking about "template arguments", on the line with std::vector, what does this mean?
You need to supply template arguments to your vector. Since you are holding strings, you need to declare it like this:
std::vector< std::string > bannedSent = {
"Gosh",
"Golly",
"Jeepers",
"Troll"
};
The easiest solution is actually not to specify the type. The compiler already has a decent idea, and you already knew the keyword:
auto bannedSent = {
"Profanity1",
"Profanity2",
"Profanity3",
"Profanity4"
};
for(auto i : bannedSent) { ...
Side benefit: This avoid constructing 4 std::string objects in each call.
Note that you used auto& i earlier. That's a mistake, you don't intend to change bannedSent.
If should be std::vector<std::string>:
bool isBanned( std::string text ) {
std::vector<std::string> bannedSent = {
...
}
}
Since you include the C++11 tag, you can also use any_of():
#include <vector>
#include <string>
#include <algorithm>
bool isBanned(const std::string & text)
{
const std::vector<std::string> bannedSent = {
"Profanity1",
"Profanity2",
"Profanity3",
"Profanity4",
};
return std::any_of(bannedSent.begin(), bannedSent.end(), [text](std::string &s){return s == text; });
}
I have a map<string, std::function<void(AgentMessage&)>> (AgentMessage is a struct with a few strings). When I try to access it using an iterator I get an access violation on the copy function of pair.
note: The std::function is pointing at a function in a different dll than the place where it is copied.
EDIT: I thought the explanation was good enough for a simple piece of code, but still - here it is.
for (map<string, std::function<void(AgentMessage&)>>::iterator it = mapRef.begin(); it != mapRef.end(); it++)
{
auto functionCopy = it->second; // IT CRASHES HERE
}
Can you show the code that inserts elements to the map?
I tried this and it works:
#include <functional>
#include <map>
#include <string>
using namespace std;
struct AgentMessage
{
};
void f(AgentMessage& am)
{
}
void g(AgentMessage& am)
{
}
int main()
{
AgentMessage am;
map<string, std::function<void(AgentMessage&)>> m;
m["f"] = f;
m["g"] = g;
for (map<string, std::function<void(AgentMessage&)>>::iterator it = m.begin(); it != m.end(); ++it)
{
auto func = it->second;
func(am);
}
}
I have following code:
#include <iostream>
#include <fstream>
#include <algorithm>
#include <iterator>
#include <vector>
#include <functional>
using namespace std;
typedef istream_iterator<string> is_it;
typedef vector<string>::iterator v_str_it;
int main()
{
int i = 4;
ifstream ifstr("1.txt");
is_it ifstrm(ifstr);
is_it eof;
vector<string> v_str(ifstrm, eof);
v_str_it vsit = v_str.begin();
while( (vsit = find_if(vsit, v_str.end(),
bind2nd(equal_to<string>(), i ))) != v_str.end())
{
cout << *vsit << endl;
++vsit;
}
return 0;
}
As far as I understand in find_if(vsit, v_str.end(), bind2nd(equal_to<string>(), i ) i should use const char like "sometext" instead of int i. But how can i find words with length equal to 4 e.g. ? I'm confused and need some advice.
find_if will only return the first item in the sequence that satisfies the predicate.
For this you really want a lambda and if you are using C++11. This will look something like:
[](std::string const& x) { return x.size() == i; }
(Not sure of the exact syntax).
To create a "functor" which is the simplest here you might do:
struct CompareStringLength
{
int len_;
explicit CompareStringLength( int len ) : len_(len)
{
}
bool operator()(std::string const& str ) const
{
return str.size() == len_;
}
};
Within your vector you would now use std::find_if( v.begin(), v.end(), CompareStringLength(i) );
to get the first element. To find all of them there is no std::copy_if to copy them into another vector so you'd actually have to create a different predicate that returns the opposite and use remove_copy_if which does exist or write your own copy_if algorithm.
I want to traverse an STL map. I don't want to use its key. I don't care about the ordering, I just look for a way to access all elements it contains. How can I do this?
Yes, you can traverse a Standard Library map. This is the basic method used to traverse a map, and serves as guidance to traverse any Standard Library collection:
C++03/C++11:
#include <cstdlib>
#include <map>
#include <string>
using namespace std;
int main()
{
typedef map<int,string> MyMap;
MyMap my_map;
// ... magic
for( MyMap::const_iterator it = my_map.begin(); it != my_map.end(); ++it )
{
int key = it->first;
string value = it->second;
}
}
If you need to modify the elements:
Use iterator rather than const_iterator.
Instead of copying the values out of the iterator, get a reference and modify the values through that.
for( MyMap::iterator it = my_map.begin(); it != my_map.end(); ++it )
{
int key = it->first;
string& value = it->second;
if( value == "foo" )
value = "bar";
}
This is how you typically traverse Standard Library containers by hand. The big difference is that for a map the type of *it is a pair rather than the element itself
C++11
If you have the benefit of a C++11 compiler (for example, latest GCC with --std=c++11 or MSVC), then you have other options as well.
First you can make use of the auto keyword to get rid of all that nasty verbosity:
#include <cstdlib>
#include <map>
#include <string>
using namespace std;
int main()
{
map<int,string> my_map;
// ... magic
for( auto it = my_map.begin(); it != my_map.end(); ++it )
{
int key = it->first;
string& value = it->second;
}
}
Second, you can also employ lambdas. In conjunction with decltype, this might result in cleaner code (though with tradeoffs):
#include <cstdlib>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
map<int,string> my_map;
// ... magic
for_each(my_map.begin(), my_map.end(), [](decltype(*my_map.begin()) val)
{
string& value = val.second;
int key = val.first;
});
}
C++11 also instroduces the concept of a range-bases for loop, which you may recognize as similar to other languages. However, some compilers do not fully support this yet -- notably, MSVC.
#include <cstdlib>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
map<int,string> my_map;
// ... magic
for(auto val : my_map )
{
string& value = val.second;
int key = val.first;
}
}
As with any STL container, the begin() and end() methods return iterators that you can use to iterate over the map. Dereferencing a map iterator yields a std::pair<const Key, Value>.
C++17
Since C++17 you can use range-based for loops together with structured bindings for iterating over a map. The resulting code, e.g. for printing all elements of a map, is short and well readable:
std::map<int, std::string> m{ {3, "a"}, {5, "b"}, {9, "c"} };
for (const auto &[k, v] : m)
std::cout << "m[" << k << "] = " << v << std::endl;
Output:
m[3] = a
m[5] = b
m[9] = c
Code on Coliru
You can traverse STL map in the same way as any other STL container: using iterators, e.g.
for (std::map<key, value>::const_iterator
i = myMap.begin(), end = myMap.end(); i != end; ++i)
{
// *i is a key-value pair
}
Using for with auto for C++11 and above usage
map<int,int> map_variable; //you can use any data type for keys, as well as value
for(auto &x:map_variable)
{
cout<<x.first ;// gives the key
cout<<x.second; //gives the value
}
The newer format of for using auto was introduced in C++11
To give it functionality like some higher level languages like python
Where there was already an implementation of such type of iteration
P.S. : map variable keeps values sorted, so when iterating you will get keys in sorted order
You can iterate map by using auto iterator.
Code Snippet:
#include<bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
map<string, int> mp;
mp["a"]=500;
mp["b"]=200;
mp["d"]=300;
mp["c"]=400;
for(auto it=mp.begin(); it != mp.end(); it++)
{
cout<<it->first <<" : "<<it->second<<endl;
}
return 0;
}