ifstream not working with dirent.h - c++

I'm testing optimizations for dijkstra algorithm and to make it easier to open files I used "dirent.h" to get all the test files in the running path and then ifstream to open this file.
the readDirec method reads all the files in the directory and ignores folder and puts those files names in a vector called files.
void selectDirec(){
files.clear();
DIR *dir;
struct dirent *ent;
if ((dir = opendir (".")) != NULL) {
while ((ent = readdir (dir)) != NULL) {
if(opendir(ent->d_name) == NULL){
files.push_back(ent->d_name);
}
}
closedir (dir);
} else {
cout<<"directory error"<<endl;
}
}
after that I uses a function called selectFile which assigns the name of the file the user chooses to a variable called fileName.
void selectFile(){
selectDirec();
for(int i = 0 ; i < files.size() ; i++){
cout<<i+1<<" : "<<files[i]<<endl;
}
int choice = 0;
do{
cout<<"enter file number"<<endl;
cin>>choice;
}while(choice > files.size());
choice--;
fileName = files[choice];
cout<<fileName<<":"<<endl;
}
after that I enter my readGraph function which opens the file and continue graph operations
void readGraph(){
ifstream ifile; ifile.open(fileName);
if(!ifile.is_open()){
cout<<"no file with the name specified"<<endl;
eflag = true;
return;
}
...
...
}
initialization:
vector<char *> files;
char * fileName ;
now I have those 5 files to test which I got from here http://algs4.cs.princeton.edu/44sp/:
tinyEWD.txt contains 8 vertices and 15 edges [140B]
mediumEWD.txt contains 250 vertices and 2,546 edges[40KB]
1000EWG.txt contains 1,000 vertices and 16,866 edges[313KB]
10000EWG.txt contains 10,000 vertices and 123,462 edges[2.4MB]
NYC.txt . contains 264346 vertices and 733846 edges[12.7MB].
but there's a weird problem with those 3 files:
'mediumEWD' , '10000EWD.txt' , 'NYC.txt'
when I choose any of them the code shows me "no file with the name specified" that in the else statement in readGraph.
but when I enter their name manually and comment selectDirec and selectFile the program opens them successfully.
P.S. I checked the file name and spacing and everything.
P.S.2 currently running this code on ubuntu 14.04 LTS.
thanks in advance.

if(opendir(ent->d_name) == NULL){
files.push_back(ent->d_name);
}
What is files? I suspect that you are using a std::vector<const char *>, or something along the same lines.
This won't work. d_name is a part of the dirent structure. Immediately afterwards, and certainly after the closedir(), that pointer is no longer valid, and points to deallocated memory.
Looks to me like you then proceed and attempt to use the no-longer valid pointer as the filename parameter to std::ifstream.
You should use a std::vector<std::string> to store the filenames, and use the c_str() member function to extract a pointer to a C-style string, for the open() call.
You can't be using a vector of std::strings here, this must be a vector of raw character pointers. That's because you're assigning one of its values to fileName, whatever it is, and then passing it directly to open() without using c_str(). So it can't be a vector of strings.

Related

C++: Reading from multiple files with spaces in their names

Essential to this problem is, that i am programming on Xcode. I wrote a function that reads in a given amount of text files into my sensor vector: To get the text file Paths i wrote also a function that gives me the filenames inclusive their paths and stores them in a list. The text files contain data that are delimited by a tab and will be stored in excel later on. The problem is they include spaces in their names and Mac has a problem with spaces in file names. I tried to replace the spaces with "\ ". This is what the terminal does with a space when i echo a file with a space in it. I cant open the files to read from them. I appreciate your help.
The path function:
void get_filelist(list<string>& list_in)
{
string full_path;
DIR *dir;
struct dirent *ent;
if((dir = opendir (dir_target.c_str()))!=NULL)
{
while((ent = readdir (dir)) != NULL)
{
if(strstr(ent->d_name, ".txt") && !strstr(ent->d_name, "Summary"))
{
full_path = dir_target;
full_path = full_path + ent->d_name;
list_in.push_back(full_path);
}
}
closedir(dir);
}
else{
printf("could not open directory");
perror("");
}
}
now here is the function that writes into my 3D vector
void fill_vector(list<string> list_in, data_vec& sensors)
{
ifstream myfile;
size_t found;
for(list<string>::iterator it = list_in.begin(); it!= list_in.end(); it++)
{
string tab = "";
vector<vector<string> > temp_matrix;
cout << *it << endl;
myfile.open(*it);
if(myfile.is_open())
{
vector<string> temp_row;
while(myfile.is_open())
{
getline(myfile, tab, '\t');
found = tab.find('\n');
if(found == string::npos) temp_row.push_back(tab);
else{
temp_row.push_back(tab.substr(0, found));
temp_matrix.push_back(temp_row);
temp_row.clear();
temp_row.push_back(tab.substr(found+1));
}
}
myfile.close();
}
else cout << "unable to open file" ;
sensors.push_back(temp_matrix);
}
}
Don't use backs;ash to denote spaces in filenames or you'll get into an even worse mess. Backslash is directory separator on MS DOS.
If
fopen("path/my file.txt", "w");
and
fopen("path/my filetxt", "r");
both work as expected (creating a file with a space in its name and opening it) you don't really have a problem. The rest of the system has the problem, but if you must have files with spaces in their names, you can read and write them.
Of course convert to hyphen, underscores or simple concatenation as soon as possible, spaces in filenames make for endless problems.
The function
std::string spacesToUnderscores(std:string const &nasty)
is easy enough to write.

readdir(): re-reading certain files

I got a function which task is to rename all files in a folder however, it re-rename certain files:
http://i.imgur.com/JjN8Qb2.png, the same kind of "error" keeps occurring for every tenth number onwards. What exactly is causing this "error"?
The two arguments to the function is the path for the folder and what start value the first file should have.
int lookup(std::string path, int *start){
int number_of_chars;
std::string old_s, file_format, new_s;
std::stringstream out;
DIR *dir;
struct dirent *ent;
dir = opendir (path.c_str());
if (dir != NULL) {
// Read pass "." and ".."
ent = readdir(dir);
ent = readdir(dir);
// Change name of all the files in the folder
while((ent = readdir (dir)) != NULL){
// Old string value
old_s = path;
old_s.append(ent->d_name);
// Get the format of the image
file_format = ent->d_name;
number_of_chars = file_format.rfind(".");
file_format.erase(0,number_of_chars);
// New string value
new_s = path;
out << *start;
new_s += out.str();
new_s.append(file_format);
std::cout << "Successfully changed name on " << ent->d_name << "\tto:\t" << *start << file_format << std::endl;
// Switch name on the file from old string to new string
rename(old_s.c_str(), new_s.c_str());
out.str("");
*start = *start+1;
}
closedir (dir);
}
// Couldn't open
else{
std::cerr << "\nCouldn't open folder, check admin privileges and/or provided file path\n" << std::endl;
return 1;
}
return 0;
}
You are renaming files to the same folder in which the original files were, resulting in an infinite loop. You renamed 04.png to 4.png but since you are iterating over all files in the folder, at some point you're going to iterate to the "new" 4.png file (in your smaple, on the 40th iteration) and rename that file to 40.png and so on...
The easiest way to resolve this with minimal changes to the existing code is to "rename" (move) the files to a temporary folder with their new names. Something like:
new_s = temp_path;
out << *start;
new_s += out.str();
new_s.append(file_format);
// Switch name on the file from old string to new string
rename(old_s.c_str(), new_s.c_str());
and when you are done renaming all the files in path (outside the while loop), delete the folder and "rename" (move) temp_path to `path:
closedir (dir);
deletedir(path);
rename(temp_path, path);
`
Possible problems I see:
Renaming files causes them to be fed to your algorithm twice.
Your algorithm for computing the new filename is wrong.
You should be able to write a test for this easily, which in turn should help you fix the problem or write a more specific question. Other than that, I don't see any grave issues, but it would help if you reduced the scope of variables a bit, which would make sure that different iterations don't influence each other.

Writing c++ output into xlsx file

I have a certain function in my c++ code which compares 2 .bmp files( a reference file is compared with almost 100 files in another directory one at a time), bit by bit and reports the bit errors properly when run a in terminal window. The function that does that is as follows :
void getBitErrors(char *filename, char *dirName, int height, int width){
DIR *dir;
struct dirent *ent;
//char *f = "";
dir = opendir (dirName);
if (dir != NULL) {
/* print all the files and directories within directory */
while ((ent = readdir (dir)) != NULL) {
if(strcmp(ent->d_name,".") && strcmp(ent->d_name,".."))
{
char f[255]="";
strcat(f,dirName);
strcat(f,ent->d_name);
printf ("reading image file %s\n", f);
cout<<"Bit Error "<<getBitError(filename,f,height,width)<<endl;
}
}
closedir (dir);
}
else {
perror ("");
}
}
I wish to have a function in my code that writes the 100 respective comparison values into an xlsx/obs file .(As opposed to having the output displayed by std::cout in a terminal window.) I have looked into 2 different options .
1) Self explanatory libXL which is a paid library and I dont really have a $199 to pay for this library.
2) SimpleXlsx which is slightly hazy.
I would be awfully obliged if someone were to explain to me how i could go about achieving my result.
OS : Linux Ubuntu 10.10 Maverick.
I suggest you look at the .csv (comma Separated Values) format. You can get a spreadsheet like result with that with much less complexity.

localtime alternative that won't overwrite the supplied struct

Essentially, what I'm trying to do is to check the last access time of a file and compare it with a string. Here's the relevant block:
struct stat file;
char timeStr[ 100 ];
stat(nodes.at(0), &file);
strftime(timeStr, 100, "%H:%M:%S-%m/%d/%y", localtime(&file.st_atime)); /* problem */
nodes is a vector of file paths; I'm not sure if it's relevant but I'll include the code that I'm using to set nodes:
vector<char*> nodes;
DIR *dir;
struct dirent *cur
if((dir = opendir(searchPath.c_str())) == NULL) {
cout << "Error opening search path. Are you sure '"
<< searchPath.c_str() << "' is a valid search path?" << endl;
return 0;
}
while((cur = readdir(dir)) != NULL) {
if(string(cur->d_name) == "." || string(cur->d_name) == "..") continue;
nodes.push_back(cur->d_name);
}
closedir(dir);
Where searchPath is a user-inputted string.
The problem: when the 'problem' line is run, from there on nodes is a vector of garbage. I'm wondering if I can accomplish this task without turning nodes into garbage.
Since this is homework, and as you can probably tell I'm not used to C++, a solid push in the right direction will be given the 'accept'.
Thank you.
It has nothing to do with your strftime call but with the fact that (from here):
The pointer returned by readdir() points to data which may be overwritten by another call to readdir() on the same directory stream.
Since you're simply pushing a character pointer that points to data that may be overwritten by subsequent calls to readdir, you may well end up with garbage.
You can probably fix it by using a copy of the C string with something like:
nodes.push_back (strdup (cur->d_name)); // plus error handling if need be.
And, if your implementation doesn't have a strdup (it's not part of the standard), you can use mine (found here).
nodes.push_back(cur->d_name);
You're storing pointers in the vector that immediately become invalid (cur is valid until the next readdir or closedir call). The best fix is to code what you want -- make nodes a vector of strings. The easiest fix:
nodes.push_back(strdup(cur->d_name));

read only .txt files from directory which has subfolders too

I am trying to read only .txt files from directory.
I am not using arrays.
I am using opendir() to open my directory.
d->d_name lists all my files and also subfolders.
I want to read only .txt but not the subfolders.
please help me.
Thanks
Can you not use FindFirstFile and FindNextFile for this?
Well, something like:
call opendir() to open the directory
in a loop, call readdir to read each entry
for each entry, examine the name to see if the last 4 characters are ".txt"
if they are, do something
at the end, call closedir to close the directory
You can use the stat() function to determine the type of file your struct dirent represents.
struct stat sb;
int rc = stat(filename, &sb);
// error handling if stat failed
if (S_ISREG(sb.st_mode)) {
// it's a regular file, process it
} else {
// it's not a regular file, skip it
}
Read the man pages for details. Also take care that the filename in d_name does not contain the directory part. If you're in a different directory than what you opendir'd, you'll need to prepend the directory name (and a directory separator if required).
For a C++ alternative, please see boost::filesystem.
You could try put the filenames into a simple structure (string array or vector for example), then pass a reference to that structure to a function that prunes names that don't use the .txt extension
in the function, look at each filename (a for loop would be handy), and use the find function in the String library to see if the last four characters are == to .txt. You can reset the position of to start searching the string to string_name.length - 4 so that you're only comparing the last few characters.
Cplusplus.com is a great reference for things like the String library: http://www.cplusplus.com/reference/string/string/find/
Assuming you are on a Linux/Posix system, you can use scandir(...). You can find the details on the manual page, but in short, you have to provide a filter function that takes a dirent pointer as argument, and returns non-zero if the entry is to be included (in your case, you would check for the name ending in .txt, and possibly the file type in the dirent struct).
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
int main(int argc, char *argv[])
{
DIR *dir;
struct dirent *entry;
int pos;
if (argc < 2)
{
printf("Usage: %s <directory>\n", argv[0]);
return 1;
}
if ((dir = opendir(argv[1])) == NULL)
{
perror("opendir");
return 1;
}
while ((entry = readdir(dir)) != NULL)
{
if (entry->d_type != DT_REG)
continue;
pos = strlen(entry->d_name) - 4;
if (! strcmp(&entry->d_name[pos], ".txt"))
{
printf("%s\n", entry->d_name);
}
}
if (closedir(dir) == -1)
{
perror("closedir");
return 1;
}
return 0;
}