?
Solved

Renaming folders and subfolders with a pattern

Posted on 2006-12-01
5
Medium Priority
?
393 Views
Last Modified: 2016-08-02
Hi,
I could use some help with the thinking on this one.

Suppose I wanted to rename all the folders and subfolders from some root according to a pattern.

For example, suppose I wanted to replace "*fred*" in each of the paths below with ALFREDO.

(To make life a little simpler, only replace the first occurrence in any segment so
\xfred2\yfreddofred\fred.htm would become
\xALFREDO2\yALFREDOdofred\fred.htm )

Now, I cannot use repeated findfirst/findnext loops because the replacement pattern could contain the patten to match.

It would be nice to do it in one pass, with some sort of recursive follow down the tree algorithm. But again, I don't see how because the Tsearchrec contents (after a rename) would be wrong.

There should be a simple way .. just can't see it for the moment. Maybe something to do with PIDL's?

Assume for the purposes of exposition that the folders to be renamed are

c:\alfred\
c:\fred\
c:\fred\tom\fred\dick.htm
c:\fred\tom\fred\harry\freda\x.htm
c:\fred\x\y\freda\x.htm
c:\xfred2\
c:\xfred2\yfreddofred\

and these would become
c:\alALFREDO\
c:\ALFREDO\
c:\ALFREDO\tom\ALFREDO\dick.htm
c:\ALFREDO\tom\ALFREDO\harry\ALFREDOa\x.htm
c:\ALFREDO\x\y\ALFREDOa\x.htm
c:\xALFREDO2
c:\xALFREDO2\yALFREDOdofred\


If the solution requires PIDL's I could use some code, because I really don't understand them.

thanks


0
Comment
Question by:Mutley2003
  • 3
  • 2
5 Comments
 

Author Comment

by:Mutley2003
ID: 18058161

I found this


http://groups.google.com.au/group/microsoft.public.win32.programmer.ole/browse_thread/thread/b4f175cddd647516/7bb619fffb08100%237bb619fffb08100

using IshellFolder SetNameOf

but I don't quite understand what the suggested solution means "you are passing a complex pidl to SetNameOf.  SetNameOf requires a single-level pidl."

I still don't know if PIDL's are the way to go, though.
0
 
LVL 26

Accepted Solution

by:
Russell Libby earned 2000 total points
ID: 18058553
Actually, you can use the findfirst/next/close to get the path names, perform the string replace on just the relative portion (path), rename the path, then iterate down the new path.

Russell


Example:

function PathReplace(Root: String; OldPattern, NewPattern: String; Flags: TReplaceFlags; var FailedMove: String): Boolean;
var  listPaths:     TStringList;
     srFind:        TSearchRec;
     szNewPath:     String;
     szRoot:        String;
     dwFind:        Integer;
begin

  // Set default result
  result:=True;

  // Get root path
  szRoot:=ExcludeTrailingBackslash(Root);

  // Create list to hold the result
  listPaths:=TStringList.Create;

  // Resource protection
  try
     // Find first
     dwFind:=FindFirst(szRoot + '\*.*', faAnyFile, srFind);
     // Check find
     if (dwFind = 0) then
     begin
        // Resource protection
        try
           // Repeat
           repeat
              // Check for path
              if ((srFind.Attr and faDirectory) = faDirectory) and not((srFind.Name = '.') or (srFind.Name = '..')) then
              begin
                 // Add path to the list
                 listPaths.Add(srFind.Name);
              end;
           // Find next
           until not(FindNext(srFind) = 0);
        finally
           // Close the find
           FindClose(srFind);
        end;
     end;
     // Walk the list
     for dwFind:=0 to Pred(listPaths.Count) do
     begin
        // Perform string replace on existing path
        szNewPath:=StringReplace(listPaths[dwFind], OldPattern, NewPattern, Flags);
        // Check for change
        if not(CompareStr(szNewPath, listPaths[dwFind]) = 0) then
        begin
           // Move the old to new
           result:=MoveFileEx(PChar(szRoot + '\' + listPaths[dwFind]), PChar(szRoot + '\' + szNewPath), MOVEFILE_REPLACE_EXISTING);
        end;
        // Check result
        if result then
           // Iterate the new path
           result:=PathReplace(szRoot + '\' + szNewPath, OldPattern, NewPattern, Flags, FailedMove)
        else
           // Failed to move, set failed move path
           FailedMove:=szRoot + '\' + szNewPath;
        // Check result, break on failure
        if not(result) then break;
     end;
  finally
     // Free the list
     listPaths.Free;
  end;

end;

procedure TForm1.Button1Click(Sender: TObject);
var  szFail:        String;
begin

  if not(PathReplace('c:\temp', 'fred', 'ALFREDO', [rfIgnoreCase], szFail)) then
     ShowMessage(Format('Failed to move "%s"', [szFail]));

end;
0
 

Author Comment

by:Mutley2003
ID: 18058849
Thanks Russell ... lovely code, as usual.

Interestingly, MOVEFILE_REPLACE_EXISTING according to my copy of the SDK says "This value cannot be used if lpNewFileName or lpExistingFileName names a directory" but I will try it and see

For this application, where I am not trying to move files across drives, there does not seem to be any reason why I could not use MoveFile instead of MoveFileEx.

Anyway, I will do some quick tests but I am sure it will work beautifully
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 18059014

Thanks,
I tested it with the paths as defined above, and it was working ok. Didn't try any name collisions, but the code is written to pick up errors and return the path that caused the problem. As to the flag, all I have for it is this:

MOVEFILE_REPLACE_EXISTING      
If a file of the name specified by lpNewFileName already exists, the function replaces its contents with those specified by lpExistingFileName.

Like I said, didn't seem to cause any problems, though as you pointed out, might not allow a rename to a path that already exists.

Russell
0
 

Author Comment

by:Mutley2003
ID: 18062846
Hi Russell

I don't know if it is some sort of logical problem or not, or a Windows issue, or just my fuzzy thinking, but I can't get a rename going where the rename would result in addition of a subdirectory to an already existing directory. This is not imho a collision issue, but a consequence of the top down nature of the approach.

I'll post this in more detail in a new question.

regards

0

Featured Post

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
This video shows how to quickly and easily deploy an email signature for all users in Office 365 and prevent it from being added to replies and forwards. (the resulting signature is applied on the server level in Exchange Online) The email signat…
Is your OST file inaccessible, Need to transfer OST file from one computer to another? Want to convert OST file to PST? If the answer to any of the above question is yes, then look no further. With the help of Stellar OST to PST Converter, you can e…
Suggested Courses

850 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question