Renaming a file

There is a problem with this rename() function. It, for some reason, doesn't think
the file I am telling it to rename exists! Here is my code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
#include <fstream>
#include <cstring>
#include <string>
#include <stdio.h>
#include <typeinfo>

using namespace 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?
Could you post the rename() function definition?
Did you know that it is a C function? http://www.cplusplus.com/reference/clibrary/cstdio/rename/
Last edited on
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)
Last edited on
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(const char *oldpath, const char *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?
Last edited on
No, the problem is not that it cannot get past line 34. Here's the updated code (it works):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <iostream>
#include <fstream>
#include <cstring>
#include <string>
#include <stdio.h>

using namespace 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.
1
2
3
4
5
6
    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])
Last edited on
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.
Last edited on
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.
Last edited on
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):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>
#include <cstring>
#include <string>
#include <vector>
#include <windows.h>
#include <stdio.h>

using namespace 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...)
I looked at other windows.h based solutions, but there is this argv thing that I don't understand.
#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

Last edited on
I found this immensely helpful: http://superuser.com/questions/177234/can-i-use-cmd-to-search-for-files-in-windows The only problem, is I don't know how to call cmd.exe commands in a C++ program (without using system()!!), so the program can be used by ANYONE.
Last edited on
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...)
Last edited on
all i know to rename is by using FILE *, why nobody using 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...
Last edited on
Now, the question I should have REALLY be asking is, does anyone know any good books on Windows.h functions for C++?

Oh, and shoutout to Duoas, andywestken, and ne555 for helping me get the basic thing working!
Last edited on
Topic archived. No new replies allowed.