Solved

Renaming folders and subfolders with a pattern

Posted on 2006-12-01
5
373 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 500 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

Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Suggested Solutions

Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
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…
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, Just open a new email message.  In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…
Here's a very brief overview of the methods PRTG Network Monitor (https://www.paessler.com/prtg) offers for monitoring bandwidth, to help you decide which methods you´d like to investigate in more detail.  The methods are covered in more detail in o…

708 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

Need Help in Real-Time?

Connect with top rated Experts

15 Experts available now in Live!

Get 1:1 Help Now