list directory files recursively with boost::filesystem - c++

I'm using new boost, v1.5.3, to carry out this task like following, thanks to the class recursive_directory_iterator (I don't have to write recursive code):
void ListDirRec(const char *Dir, vector<string>& DirFileList, const char* ext)
{
recursive_directory_iterator rdi(Dir);
recursive_directory_iterator end_rdi;
DirFileList.empty();
string ext_str0(ext);
for (; rdi != end_rdi; rdi++)
{
rdi++;
//cout << (*di).path().string() << endl;
cout << (*rdi).path().string() << endl;
//cout << " <----- " << (*rdi).path().extension() << endl;
//string ext_str1 = (*rdi).path().extension().string();
if (ext_str0.compare((*rdi).path().extension().string()) == 0)
{
DirFileList.push_back((*rdi).path().string());
}
}
the function list files with specific extension. This function works for cases but frequently return an "assertion fails error" like:
**** Internal program error - .... assertion (m_imp.get()) ... operations.hpp(952): dereference of end recursive_directory_iterator
I barely figure out the cause of this error. Can any try.. catch help?
thanks in advance for any help

You are incrementing rdi inside the loop as well as in the for declaration:
for (; rdi != end_rdi; rdi++)
{
rdi++;
That means that rdi might be end_rdi (the end-iterator, which means past the last element) within your loop. Is there any reason you're doing this? (If this is intentional, you should check to make sure rdi != end_rdi again after you increment it.)

You could try something like this:
recursive_directory_iterator dir(path(Dir));
for(auto&& i : dir) {
if (is_directory(i)) {
//Do whatever you want
cout << i << endl;
}
}

Related

why does the first 2 chars get skipped when executing? C++

while(!Info.eof()) {
std::getline(Info, line,'\r');
char a[line.length()];
char things[]= ":.\n\0";
for(int i=0;i<sizeof(a); i++) {
a[i]= line[i];
}
ptr = strtok(a, things);
ptr = strtok(nullptr,things);
while (ptr!= nullptr) {
ptr = strtok(nullptr,things);
std::cout << ptr << std::endl;
}
Info is the ifstream input file. line is a string. When I cout << line it displays everything no problem, the problem is i need to take away everything other than the needed strings and int, which I have done but the first 2 lines don't show. When I first executed this it displayed everything, yesterday it skipped the first line and today the first two lines. I guess this has something to do with memory or something unseen, I need help please, thanks.
Well, for starters, you are calling strtok() 3 times before your 1st cout print. So you are skipping the first few substrings.
Also, you have mistakes in your code, namely using eof() in a loop, and using non-standard variant-length arrays.
Try something more like this instead:
while (std::getline(Info, line))
{
const char *things = ":.";
ptr = strtok(line.data()/* or: &line[0]*/, things);
while (ptr)
{
std::cout << ptr << std::endl;
ptr = strtok(nullptr, things);
}
...
}
Or, as a for loop:
while (std::getline(Info, line))
{
const char *things = ":.";
for(ptr = strtok(line.data()/* or: &line[0]*/, things);
ptr != nullptr;
ptr = strtok(nullptr, things))
{
std::cout << ptr << std::endl;
}
...
}
Although, you really shouldn't be using strtok() in C++ at all. std::string has its own find_first_of() and substr() methods that you can use instead, eg:
while (std::getline(Info, line))
{
std::string::size_type start = 0, end;
while (start < line.size())
{
end = line.find_first_of(":.", start);
if (end == std::string::npos)
{
std::cout << line.substr(start) << std::endl;
break;
}
std::cout << line.substr(start, end-start) << std::endl;
start = end + 1;
}
...
}
One thing you're missing -- C-style strings are terminated with a zero on the end. You're not doing that.
Secondly, you did two strtoks before your while loop, which is why you're losing a few things.

For loop iterator issues c++

I having a problem getting my code to throw runtime errors as well getting these loops to work properly, and I'm not sure why.
If the user were to enter a name and balance, for example: josh 100, the program should add 100 to the account named josh in listAccounts vector, and then print the new balance.
something about the line: for ( ; iter != listAccounts.end(); iter++) {
For some reason it's causing the if statements to not work, if I comment out that line the function works but only with the last element in listAccounts. Is something wrong with the for loop that is causing it to not step through each element?
void Account::change_balance(string name, int balance, int i) {
try {
auto iter = listAccounts.begin();
for ( ; iter != listAccounts.end(); iter++) {
if ((*iter).account_name == name) {
if (((*iter).account_type == 0) && (((*iter).account_balance +
balance) < 0)) {
throw runtime_error("account cannot hold negative
balance");
(*iter).account_balance = (((*iter).account_balance) -
(balance));
cout << (*iter).account_balance;
} else {
(*iter).account_balance = balance +
(*iter).account_balance;
cout << (*iter).account_balance;
}
}
}
}
catch (runtime_error& e) {
cout << "error on line " << i << ": " << e.what() << "\n";
}
}
I can't figure out where I have gone wrong, any indication would be much appreciated. Thanks.
Check your inner for-loop. You never increase the iterator, all you do is comparing listAccounts.size() times the same element of the vector with the function argument. As the loops ends when k == listAccounts.size(), the condition n == listAccounts.size() can only be fulfilled when the outer loop goes for at least another run, because you did not clear n (or defined it locally). But now, it may fire and say "Account not found" even though it is there, because n == listAccounts.size(). This is not the behaviour your intended.
You should rethink your algorithm. Why do you traverse all the accounts each time you traverse the list of accounts? It would be totally sufficient to do it once. Consider doing something like this:
for(auto& account : listAccounts) {
if(account.account_name == name) {
// do your logic stuff and eventually:
return;
}
}
// if your program gets here, no account with such a name was found

why this function is not returning value expected

below is a searchVal function for searching a value in a binary tree. nodes of binary tree are stored in a vector nodeVec with first object in vector as root.
structure of nodes
class bst{
public:
//other functions..//
int searchVal(int)
private:
bst *lLink;
int info;
bst *rLink;
}
calling part in main
cout << "Enter Value to search ";
int val;
cin >> val;
int ret = nodeVec[0].searchVal(val);
if (ret == 2)
cout << "Value Not Found" << endl << endl;
else
cout << "Value Found" << endl << endl;
Function
int bst::searchVal(int val)
{
if (info != val)
{
if (info > val)
{
if (lLink != NULL)
lLink->searchVal(val);
else
return 2;
}
if (info < val)
{
if (rLink != NULL)
rLink->searchVal(val);
else
return 2;
}
}
if (info == val)
return 1;
}
while debugging (using codeBlocks) i observed that after any condition is met for example if the condition info==val is met the execution pointer (arrow in the IDE pointing to line being processed) goes to end of searchVal and after that it go to nearest if(from the end) however it does not go into the block of that condition. It always returns the info stored in root node in ret not 1 or 2
Should be
if(lLink!=NULL)
return lLink->searchVal(val);
^^^^^^
I think the major problem in your code is that you're not returning the result of the sub-tree searches; i.e. you should be doing this:
if(lLink!=NULL)
return lLink->searchVal(val);
You're missing the return keyword. That means that some execution paths go all the way through to the end of the function, and never hit a return, which is potentially very bad. The compiler should be issuing an error or warning about that though.
The searchVal function needs a return value on every code path - everything else has undefined behaviour (which means that anything could happen).
Add a return to your calls to lLink->searchVal and rLink->searchVal.
(And if your compiler didn't warn you about this, you need to enable that warning.)

c++ map<string,string>::find seems to return garbage iterator

map<string,string>::find seems to be returning garbage iterator, since i can access neither my_it->first nor second (NB: my_it != my_map.end() is verified). VC2010 reports a debug error, and looking deeper reveals
my_it is (Bad Ptr, Bad Ptr).
The 'offending' map is a class attribute, _match, shown below in context:
class NicePCREMatch
{
private:
map<string, string, less<string> > _match;
public:
void addGroup(const string& group_name, const string& value);
string group(const string& group_name);
};
Here is the code that returns elements by key (the commented-out code works fine):
string NicePCREMatch::group(const string& group_name)
{
/*for (map<string, string, less<string> >::iterator j = _match.begin(); j != _match.end(); j++)
{
if(!strcmp(j->first.c_str(), group_name.c_str()))
{
return j->second;
}
}
throw runtime_error("runtime_error: no such group");*/
map<string, string, less<string> >::iterator i = _match.find(group_name);
if (i == _match.end())
{
throw runtime_error("runtime_error: no such group");
}
return i->second;
}
And Here is the code that inserts new elements in the map:
void NicePCREMatch::addGroup(const string& group_name, const string& value)
{
_match.insert(pair<string, string>(group_name, value));
}
Another class uses NicePCREMatch as follows:
template<class Match_t>
vector<Match_t> NicePCRE<Match_t>::match(const string& buf)
{
[snip]
Match_t m;
[snip]
m.addGroup(std::string((const char *)tabptr + 2, name_entry_size - 3), \
buf.substr(ovector[2*n], ovector[2*n+1] - ovector[2*n]));
[snip]
addMatch(m);
[snip]
return _matches;
}
Where,
template<class Match_t>
void NicePCRE<Match_t>::addMatch(const Match_t& m)
{
_matches.push_back(m);
}
Finally, client code uses NicePCRE class as follows:
void test_NicePCRE_email_match(void)
{
NicePCRE<> npcre;
npcre.compile("(?P<username>[a-zA-Z]+?)(?:%40|#)(?P<domain>[a-zA-Z]+\.[a-zA-Z]{2,6})");
vector<NicePCREMatch> matches = npcre.match("toto#yahoo.com");
assert(!matches.empty());
assert(!strcmp(matches.begin()->group("username").c_str(), "toto"));
cout << matches.begin()->group("domain").c_str() << endl;
assert(!strcmp(matches.begin()->group("domain").c_str(), "yahoo.com"));
}
BTW, this --is pretty much-- my main (the oddest TDD ever :) ):
int main()
{
int test_cnt = 0;
cout << "Running test #" << test_cnt << " .." << endl;
test_NicePCRE_email_match();
cout << "OK." << endl << endl;
test_cnt++;
SleepEx(5000, 1);
return 0;
}
What am I doing wrong here?
EDIT:
The following modification (compare with the version above) solved my problem. Viz,
void NicePCREMatch::addGroup(const string& group_name, const string& value)
{
_match.insert(pair<string, string>(group_name.c_str(), value.c_str()));
}
Client code (slightly modified) now looks like this:
void test_NicePCRE_email_match(void)
{
NicePCRE<> npcre;
npcre.compile("(?P<username>[a-zA-Z]+?)(?:%40|#)(?P<domain>[a-zA-Z]+\.[a-zA-Z]{2,6})");
vector<NicePCREMatch> matches = npcre.match("toto#yahoo.com");
assert(!matches.empty());
try
{
assert(!strcmp(matches.begin()->group("username").c_str(), "toto"));
assert(!strcmp(matches.begin()->group("domain").c_str(), "yahoo.com"));
cout << "username = " << matches.begin()->group("username") << endl;
cout << "domain = " << matches.begin()->group("domain") << endl;
}
catch (const runtime_error& e)
{
cout << "Caught: " << e.what() << endl;
assert(0x0);
}
}
This is quite bizarre. Can someone please explain. However, I consider my problem solved already.
Thanks every one.
Your issue is here
if (i == _match.end())
{
throw runtime_error("runtime_error: no such group");
}
return i->second;
Your find failed for some reason. I can't say why because I don't have the full code. But, after the failure, you are throwing an error, but there is nobody to catch outside. Please add a try catch at the point where you call the method group() and implement the logic if the match is not found.
I tried with your sample snippets (+ some changes to get the stuff compiled) and it looks like visual studio continues with the next line in the function even after a throw statement. I don't know the theory behind it. I was bit surprised at seeing such a behavior.
[To make sure that your class structure is not causing the problem, I tried with a simple global method and even the method also gave me the same behavior. If there are somebody who can explain this please feel free.]
This might be caused by three things - either you modify the map in some way after the execution of find or you have a memory coruption somewhere in your program or the debugger is simply not showing the correct values for the iterator.
Try using debug output - if the code crashes when you try to output the values, then probably the iterator is really broken.
Also make sure you do not modify the map after the execution of find. If you do, this may make the iterator invalid and so you need to move the find call immedietly before using the iterator.
If both of the above options don't help you probably have memory corruption somewhere and you need to find it. Maybe use valgrind for that. Please note this should be your last resort only when the two other options are proved impossible.

boost filesystem version 3 issue?

the example code on the boost website is not working. http://www.boost.org/doc/libs/1_46_1/libs/filesystem/v3/doc/tutorial.html#Using-path-decomposition
int main(int argc, char* argv[])
{
path p (argv[1]); // p reads clearer than argv[1] in the following code
try
{
if (exists(p)) // does p actually exist?
{
if (is_regular_file(p)) // is p a regular file?
cout << p << " size is " << file_size(p) << '\n';
else if (is_directory(p)) // is p a directory?
{
cout << p << " is a directory containing:\n";
typedef vector<path> vec; // store paths,
vec v; // so we can sort them later
copy(directory_iterator(p), directory_iterator(), back_inserter(v));
sort(v.begin(), v.end()); // sort, since directory iteration
// is not ordered on some file systems
for (vec::const_iterator it (v.begin()); it != v.end(); ++it)
{
path fn = it->path().filename(); // extract the filename from the path
v.push_back(fn); // push into vector for later sorting
}
}
else
cout << p << " exists, but is neither a regular file nor a directory\n";
}
else
cout << p << " does not exist\n";
}
catch (const filesystem_error& ex)
{
cout << ex.what() << '\n';
}
return 0;
}
two error messages when compiled in visual studio 2010 for the line path fn = it->path().filename();
The first error is: 'function-style cast' : illegal as right side of '->' operator and the second error is: left of '.filename' must have class/struct/union
Also, when I mouse over path() it says: class boost::filesystem3::path Error: typename not allowed
This section (for's body) is problematic:
path fn = it->path().filename(); // extract the filename from the path
v.push_back(fn); // push into vector for later sorting
it points to path objects, so I don't see why path() is called. Seems that it should be replaced with it->filename()
you push filenames at the end of the same vector, so after the loop you will have the vector containing the list of files twice, first with pathnames then just filenames.
EDIT: Looking at the original example I see that these are your modification. If you want to store filenames instead of printing them, define another vector of string or path and store filenames in it, don't reuse the first one. And removing the call to path() should solve the compilation problem.
EDIT 2: as a cute BTW, you can achieve both directory traversal and filename extraction in one pass using std::transform instead of std::copy:
struct fnameExtractor { // functor
string operator() (path& p) { return p.filename().string(); }
};
vector<string> vs;
vector<path> vp;
transform(directory_iterator(p), directory_iterator(), back_inserter(vs),
fnameExtractor());
Same using mem_fun_ref instead of fnameExtractor functor:
transform(directory_iterator(p), directory_iterator(), back_inserter(vp),
mem_fun_ref(&path::filename));
After you posted the link it seems that the code posted is working fine. But problem is introduced in the modified code in for loop. 1st problem is that, path() is a constructor. 2nd problem, I am not sure about boost::path contains any method as filename(). You can try,
path fn = (*it);