#include <iostream>
#include <fstream>
#include <cstring>
#include <string>
#include <stdio.h>
#include <typeinfo>
usingnamespace std;
int main()
{
bool keepgoing = false;
string filename, extension, line;
//char * oldfilename, * newfilename;
int result = 0;
cout << "Welcome to the file renamer!" << endl;
while (keepgoing == false)
{
cout << "\nPlease enter the filename to be changed (no extension AND must be in the same folder as this program) : ";
getline(cin,filename);
cout << "Now enter the extension of the file: ";
getline(cin,extension);
filename = filename + '.' + extension;
ifstream myfile;
myfile.open(filename.c_str());
if (myfile != 0)
{
keepgoing = true;
} //end if
else
{
cout << "File does not exist in this directory. Please try again."<<endl;
} //end else
} //end while
//This is where we rename the files. However, both arguments MUST be character arrays!
char * oldfilename = (char*)filename.c_str();
for (size_t pos = filename.find_first_of('+'); pos != filename.npos;pos = filename.find('+',pos+1))
{
filename.at(pos) = ' ';
} //end if
char * newfilename = (char*)filename.c_str();
result = rename(oldfilename,newfilename);
if (result == 0)
{
cout<<"\nFile successfully renamed!"<<endl;
} //end if
else
{
perror("This error occurred");
} //end else
delete[] oldfilename;
delete[] newfilename;
return 0;
} //end main
It was working fine until the rename() function got called. I was thinking the oldfilename and newfilename are NOT null-terminated. Could that be the problem?
I was right. They have to be null-terminated. I couldn't use memcpy() because that means calling string.h and thus overloading what the program thinks memcpy() is. My question is now this: would the algorithmic complexity be the same either way?
Oh, and I will now extend this program to replace the '+' in ALL files to ' ' (to undo the changes the internet/machine's downloader does to the file)
I am looking for the cheapest way to do this. No, this is NOT a school project, this is a personal project to make .docx files easier to deal with. Will I have to import any additional header files?
Let me ask the question this way. Suppose you were going to use the windows.h file to accomplish the goal of locating and modifying all the .docx files that have a '+' in them. Would the algorithmic complexity FOR ANY CASE be more than the number of files in the target directory?
You are rambling.
A lot of times the functions are quite intuitive, in doubt RTFM
There is a problem with this rename() function
No, you need to realize that your program does not go beyond line 34.
It is not rename() fault, that's in line 42.
Your problem is with
1 2 3 4 5 6 7 8
if (myfile != 0)
{
keepgoing = true;
}
else
{
cout << "File does not exist in this directory. Please try again."<<endl;
}
If you're curious I could try to explain your mistake, for now if you want to test if a file is open then you could ask that myfile.is_open()
1 2
//This is where we rename the files. However, both arguments MUST be character arrays!
char * oldfilename = (char*)filename.c_str();
sigh, this is the prototype int rename(constchar *oldpath, constchar *newpath);
as you can see, it asks for constant strings, and that's precisely what std::string::c_str() returns.
So you ought to write rename( old_name.c_str(), new_name.c_str() );
delete[] oldfilename; ¿why are you doing that? ¿where did you use new[] to allocate that memory?
#include <iostream>
#include <fstream>
#include <cstring>
#include <string>
#include <stdio.h>
usingnamespace std;
int main()
{
bool keepgoing = false;
string filename, extension, line;
char * oldfilename, * newfilename;
int result = 0;
cout << "Welcome to the file renamer!" << endl;
while (keepgoing == false)
{
cout << "\nPlease enter the filename to be changed (no extension AND must be in the same folder as this program) : ";
getline(cin,filename);
cout << "Now enter the extension of the file: ";
getline(cin,extension);
filename = filename + '.' + extension;
ifstream myfile;
myfile.open(filename.c_str());
if (myfile != 0)
{
keepgoing = true;
} //end if
else
{
cout << "File does not exist in this directory. Please try again."<<endl;
} //end else
} //end while
//This is where we rename the files. However, both arguments MUST be character arrays!
oldfilename = new(nothrow) char [filename.size()+1];
oldfilename[filename.size()] = 0;
for (size_t x = 0; x<filename.size(); x++)
{
oldfilename[x] = filename.at(x);
} //end for
for (size_t pos = filename.find_first_of('+'); pos != filename.npos;pos = filename.find('+',pos+1))
{
filename.at(pos) = ' ';
} //end if
newfilename = new(nothrow) char [filename.size()+1];
newfilename[filename.size()] = 0;
for (size_t x = 0; x<filename.size(); x++)
{
newfilename[x] = filename.at(x);
} //end for
result = rename(oldfilename,newfilename);
if (result == 0)
{
cout<<"\nFile successfully renamed!"<<endl;
} //end if
else
{
perror("This error occurred");
} //end else
delete[] oldfilename;
delete[] newfilename;
return 0;
} //end main
I was going to one-up this by making this work iteratively on EVERY .docx containing a '+' in the filename, that happens to be in the same directory as this program. The only problem(s) are:
1.) I have no idea as to what is going on with windows.h functions (I might after I finish this systems programming class...)
2.) The algorithmic complexity might be TOO DAMN HIGH!! I hope that it won't be bigger than the number of files in the directory.
newfilename = new(nothrow) char [filename.size()+1];
newfilename[filename.size()] = 0;
for (size_t x = 0; x<filename.size(); x++)
{
newfilename[x] = filename.at(x);
}
¿Did you read what I wrote? There is no need for that copy.
I was going to one-up this by making this work iteratively on EVERY .docx containing a '+' in the filename, that happens to be in the same directory as this program
The complexity will be O(kn) where `n' is the number of files and `k' is the length of the names
There is no need to use windows.h you may take the names of the files as arguments of your program int main(int argc, char **argv)
So when you call
$ ./program.bin *.docx
you will have in argc the number of files (plus 1 for the program itself) and in argv the file names (starting in argv[1])
ne555, you are such a help! Thanks! Oh, and I assume that $ ./program.bin *.docx is if you are running Linux/UNIX. Is this right? Also, the program should not receive input from the user.
I was going to one-up this by making this work iteratively on EVERY .docx containing a '+' in the filename, that happens to be in the same directory as this program
Well, I have to admit that for a little Windows app like this, I would use FindFirstFile/etc to solve this problem. Which would mean dragging in windows.h. But then I'm used to being able to just use
C:\Documents and Settings\andy_westken\My Documents>renamer *.docx
rather than
C:\Documents and Settings\andy_westken\My Documents>renamer example1.docx example2.docx example3.docx
I like the program to do the work for me, not vice versa!
Andy
PS Boost does, of course, provide a cross-platform soution.
Thanks, Andy! I have it, but it only works on the folder it's in (which is typical of C++ programs). Is there a way to make it work on ALL .docx files that are in ALL the subfolders (say, if I have this program in C:\Users I want it to effectively work on ALL .docx files the user would interact with on the C: drive.
Here's my code so far (which works, btw):
#include <iostream>
#include <cstring>
#include <string>
#include <vector>
#include <windows.h>
#include <stdio.h>
usingnamespace std;
void PressEnterToContinue()
{
int c;
printf( "Press ENTER to continue... " );
fflush( stdout );
do
c = getchar();
while ((c != '\n') && (c != EOF));
} //end PressEnterToContinue
int main()
{
//This program works by usage of a do-while loop. Before that, it will use a conditional based on the value of the FindFirstFile(filename,FindFileData)
//It is worth noting that the second item is a pointer to the WIN32_FIND_DATA structure that returns information about files being searched.
//That function should NOT return INVALID_HANDLE_VALUE. If it doesn't, proceed to the loop, which loops until there are no more files found of the
//target type.
//Remember that the function should be the value of something called a handle.
vector <string> files, oldfiles; //Declare a vector to store the filenames
cout<<"Vector size before the file search operation:"<< files.size()<<endl; //writing test line...
WIN32_FIND_DATA fd;
HANDLE h = FindFirstFile("*.docx", &fd);
if (h != INVALID_HANDLE_VALUE)
{
do
{
files.push_back(fd.cFileName); //write found file to vector...
} //end do
while (FindNextFile(h, &fd)); //...while there are more files of the desired type to find.
FindClose(h); //Close the handle when you are done.
} //end if
else
{
cout<<GetLastError()<<endl;
} //end else
cout<<"Amount of files found: "<<files.size()<<endl;
//cout<<"Handle == "<<h;
for (int x=0; x<files.size(); x++) //for every file in the vector
{
oldfiles.push_back(files.at(x));
for (size_t s = (files.at(x)).find_first_of('+'); s!=(files.at(x)).npos ;s = (files.at(x)).find('+', s+1))
//locate every '+' in the vector entries....
{
(files.at(x)).at(s) = ' '; //...and replace it with a ' '!
} //end inner for
rename((oldfiles.at(x)).c_str(), (files.at(x)).c_str()); //try the rename just right after the xth new name is being generated...
} //end outer for
PressEnterToContinue();
return 0;
} //end main
PS: I am hoping to sell this (or give this) to my school's Writing Center. They would LOVE this (some of their tutors showed interest...)
#1 To handle subfolders, you need to factor your search routine out of main so it's a function that's passed the starting path, etc. You then call it recursively when the dwFileAttributes member of the WIN32_FIND_DATA struct tells you it's a directory (i.e. has the FILE_ATTRIBUTE_DIRECTORY bit set)
#2 argc + argv are the command line arguments: argc is the count, argv an array of char* pointers to the values.
Calling example.exe using the following command line
example.exe 1 "hello world" c:\forgot the quotes\path\temp.txt
means that that argument would arrive as
argc = 6
argv[0] = example.exe (sometimes this is converted to a fully qualified path)
argv[1] = 1
argv[2] = hello world
argv[3] = c:\forgot
argv[4] = the
argv[5] = quotes\path\temp.txt
I know that in the Command Prompt, you have something like dir /b /s "c:\*+*.docx", but the problem is that using a command line argument in a C++ program is, as far as I know, dangerous. (The only two ways I have heard of doing such are system() (EVIL!!) and _exec(). I remember being warned that _exec() could also allow for malevolent system manipulation, so I shied away from that. In fact, I was told that using FindFirstFile(), FindNextFile() was the best way to go. The only problem with this approach is the fact that I can't seem to use my command line syntax. How would I go about this?? (I can't even find the right manual, so how can I read it...)
rename() is a more direct way. See the other parts of this conversation above. However, the problem is in finding the files on the client's computer and removing the '+' and the other space-substitute strings from the filenames (for user-friendliness).
Oh, and I think I might have answered my own question: the FindFirstFile has to know EXACT DIRECTORIES with the file in them. So, to do what this client is asking for, I would have to manually search ALL the folders that would have the .docx files in them. There is some good news:
1.) I might only have to search the Desktop, My Documents, and Downloads (My Documents == Documents)
2.) You can specify to return only the .docx files that have a string in them like this C:/Users/yourfolder/subfolder/*string*.docx
In conclusion, my hopes of calling something similar to the command-line argument are crushed in the sense that I would have to REALLY know what I am looking for. It also seems that my dreams of doing this and keeping the algorithmic complexity to O(kn), where k:=max((files[j]).size()),j∈Z∩[1,n], and n:=number of files found (that meet our search criteria!!), are just that, dreams :( I posted this, instead of deleting my other comment so anyone else with the same hopes can receive the Red Pill here...