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

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.

Related

ifstream not working with dirent.h

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.

Preventing line repeats in files in C++

I apologize before-hand if this question was already asked and my google skills failed me.
I'm making a simple console game with ncurses and I wanted to include this locked zip folder with extra lore, bonus material, etc...
I can write the codes to the file just fine but for whatever reason when I revisit the place that wrote the text to the file it repeats itself. I've tried looking for a solution and haven't found one so this is my last resort.
basic information: I use windows but I want the program to be cross-platform. If any more information is required I'd be glad to provide it.
EDIT 1:
std::ifstream checkFile("Unlocks.txt");
if(checkFile.is_open())
{
std::string data;
std::string fernox = "Unlock for Fernox Piraxis File";
while(std::getline(checkFile, data))
{
if(data.find(fernox) == std::string::npos)
{
std::ofstream myFile("Unlocks.txt", std::ios::app);
myFile << "Unlock for Fernox Piraxis File: ZWdOFMRmeE\n";
myFile.close();
break;
}
}
checkFile.close();
}
EDIT 2:
I'm not trying to overwrite any part of the other file. This code is "supposed" to check if the line above is already written in the file and if it isn't, write it. If the line already exists within the file I don't want it to write the same line again (and I'm using ios::app so that it doesn't overwrite anything already in the file.
Thanks in advance for the help.
EDIT 3:
working now thanks to twalberg.
Final Code:
std::ifstream checkFile ("Unlocks.txt");
if(checkFile.is_open())
{
bool found = false;
std::string data;
std::string fernox ("Unlock for Fernox Piraxis File");
while(std::getline(checkFile, data))
{
if(data.find(fernox) != std::string::npos)
{
found = true;
break;
}
}
if(!found)
{
std::ofstream myFile("Unlocks.txt", std::ios::app);
myFile << "Unlock for Fernox Piraxis File: ZWdOFMRmeE\n";
myFile.close();
}
checkFile.close();
}
Your current logic is a little off. You are reading the first line of the file, and if that line doesn't match, you append the string and break out of the loop. What you need is a structure more like this, checking each line of the file, and only then deciding whether to append your string:
// open file
bool found = false;
while (std::getline(checkFile, data))
{ if (data.find(fernox) != std::string::npos) // we found a match
{ found = true;
break;
}
}
if (!found)
{ // append string here
}
// close file

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.

list top 10 files by size in a unix directory

I am trying a to read a unix directory (including all subdirectories) using c++ and list the top 10 largest files.
I have read that I can use #include dirent.h and use struct dirent but I am having trouble passing the directory name as a variable to opendir/readdir.
Basically it doesn't recognise it and says file/directory not found.
Please can you help me with how I can do this in c++ and print out the top 10 largest files in the directory? Thanks
DIR *dir;
struct dirent *ent;
dir = opendir ("homedir");
if (dir != NULL) {
while ((ent = readdir (dir)) != NULL) {
cout << ent->d_name <<endl;
}
closedir (dir);
} else {
cout << "Can't open directory" << endl;
}
You don't really give enough details, but when you are reading
recursively, are you postfixing the names you read to the
previous names. Reading a directory doesn't change the current
directory, so your function should look more or less like:
std::vector
readDirectoriesRecursively( std::string const& path )
{
std::vector results;
for each name in path
if is directory
results.insert(
results.end(),
readDirectoriesRecursively( path + '/' + filename ) ) ;
else
results.push_back( FileInfo( path + '/' + filename ) );
return results;
}
In the constructor of FileInfo, use stat to obtain the size. Once you have the results, sort by size, and output the first 10.
You're almost there. You have all the filenames. With these, you can do a stat to obtain the filesize for each file. When you sort the filesizes descending, you have the ten largest files.
struct stat buf;
stat(ent->d_name, &buf);
See the detailed example in the man page.

Attaching text file to Visual Studio project...?

I'm almost done with a homework assignment but I'm having a hell of a time with one aspect of it. The purpose of the program is to read in a text file, and then do analysis. Now, if I'm on my computer, I can put in the full path of the file and it runs fine.
But it won't run ok if my professor tries to run it. I tried prompting the user to input a full path and that didn't work. I tried attaching the text file to the .exe but I don't think I did it right.
Anyone have any advice?
//int bookinput = 0;
//string whichbook;
//ifstream bookread;
//ifstream bookread(whichbook.c_str());
//cout << "Welcome to the book analysis program.\n";
//cout << "Please type in the full path of the book, remembering to double backslashes: ";
//cin >> whichbook;
//
//if(bookinput == 1){
// bookselect = "1984.txt";
//}
//else if(bookinput == 2){
// bookselect = "conneticutYankeeInKingArthursCourt.txt";
//}
//
//bookread.open(bookselect.c_str());
//bookread.open(whichbook.c_str());
bookread.open(whichbook.c_str());
if(bookread.is_open()){
std::cout << "opening book\n\n";
if(bookread.good()){
cout << "opening of book successful :D";
}
while(bookread.good()){ //reads to end of file
string input;
//getline(bookread, input);
bookread >> input;
//only add alphanumerical strings to the word list
if (isAlphaNumerical(input))
{
words.push_back(input);
}
}
}
This is the problem:
cout << "Please type in the full path of the book, remembering to double backslashes: ";
Double-backslashes are only meaningful to the C++ compiler. When you prompt the user for a path, the compiler isn't involved and double backslashes should NOT be used. (and string input cannot use \t to indicate a tab, etc., unless you implement special processing afterward)
You can keep the source file/exe file and text file in the same folder. Inform your professor to copy the entire folder and run it from there.
On another thought, the Prof. should have his own copy of the text file and he should be giving you instructions on how to locate the file within your program.
This bit of code I'm assuming is where the file path is being entered/decided.
.......
cin >> whichbook;
if(bookinput == 1){
bookselect = "1984.txt";
}
else if(bookinput == 2){
bookselect = "conneticutYankeeInKingArthursCourt.txt";
}
//should'nt this be either one line?
bookread.open(bookselect.c_str());
bookread.open(whichbook.c_str());
So if bookinput is 1 or 2, you're trying to open a file without specifying the path? You could keep the two text files in the same path as the executable and then you have to get the path of the executable and use it with the filename, instead, the easier/crappier route would be, you could hardcode it to a known path
Like:
bookselect = "C:\\Temp\\1984.txt";