Click here to Skip to main content
15,886,689 members
Articles / Programming Languages / C#
Article

FileRen - Batch File Renamer

Rate me:
Please Sign up or sign in to vote.
3.40/5 (7 votes)
27 Feb 2007LGPL33 min read 48.4K   926   22   7
Batch file renaming utility using threading
Screenshot - fileren.gif

Introduction

I wonder why in this era of free open-source, there are still small utilities that are considered as proprietary software which otherwise should not be. One of the examples is Batch File Renaming utility. You can always find one freeware application on renaming file(s) in batch mode but the source code alongwith is less likely. So, I come forward to present how it can be done with this code snippet. I would like to mention that this is just a beginning to the approach and would likely be containing bugs, but my commitment is sincere and I will try to remove all those bugs as and when they are identified.

The file-renamer is yet another implementation of the batch file renaming utility. It uses multi-threading, first to collect file(s) and then to rename them so that the whole process consumes less resources of the system.

Background

The idea came to my mind when I studied the code of CoolFileSystem available here at coolfilesystemclass.asp which uses multi-threading to collect files in an ArrayList.

It uses the CoolFileSystem implementation by Vahe Karamian which is code present on CodeProject only.

Using the code

As proper comments have been added to the code to make it easy to understand, I am not giving any description of the code here. Still, let us analyze the basic structure of some useful classes in FileRen.

The FileRen uses CoolFileSystem to collect files in an ArrayList and then renames the file(s) based on the pattern selected by the user to rename the file(s). The pattern options are limited right now, but I am planning to include Regular Expressions based renaming options.

The first class is the Startup class, which associates all the exceptions in the Application to a locally defined procedure. The code is as follows:

C#
Application.ThreadException += new ThreadExceptionEventHandler(ShowErrorBox);

The Settings class is used to generate the new name of the file based on the pattern selected by the user. The code behind the generation of the new file name is:

C#
/// <summary>
/// Generates the new name based on the pattern properties
/// set.
/// </summary>
/// <param name="fileNameToBeProcessed">Original name</param>
/// <param name="newFileName">[Out] New file name</param>
/// <returns>true if successful, false otherwise</returns>
public bool GetNewFileName(string fileNameToBeProcessed, 
    out string newFileName)
{
    string fileName = "", tempFileName;
    int i;

    newFileName = "";

    switch( RenameWhichPart )
    {
        case RenamingOptions.RenameNameOnly:
            fileName = Path.GetFileName (fileNameToBeProcessed).Substring(0, 
                fileNameToBeProcessed.Length - 
                    Path.GetExtension FileNameToBeProcessed).Length);
            break;
        case RenamingOptions.RenameExtOnly:
            fileName = Path.GetExtension(fileNameToBeProcessed).Substring(1);
            break;
        case RenamingOptions.RenameWholeName:
            fileName = fileNameToBeProcessed;
            break;
        case RenamingOptions.RenameCustom:
            fileName = Path.GetFileName(fileNameToBeProcessed).Substring
            (_splitPositionToBeRenamed - 1);
            break;
    }

    switch( TextPatternToBeUsed )
    {
        case PatternOptions.PatternLowerCase: 
            fileName = fileName.ToLower();
        break;
        case PatternOptions.PatternUpperCase: 
            fileName = fileName.ToUpper();
        break;
        case PatternOptions.PatternInitCap: 
            fileName = StringFunctionality.Initcaps(fileName); 
        break;
        case PatternOptions.PatternRandomCase: 
            fileName = StringFunctionality.RandomCase(fileName); 
        break;
        case PatternOptions.PatternInvertCase: 
            fileName = StringFunctionality.InvertStringCase(fileName); 
        break;
    }

    if (_replaceStringWithExpression)
        fileName = fileName.Replace(_stringToFindForReplace, 
            _stringToReplace);

    if (_insertStringToFileName)
    {
        if (_renameWhichPart != RenamingOptions.RenameExtOnly)
        {
            tempFileName = fileNameToBeProcessed.Substring(0, 
                fileNameToBeProcessed.Length - 
                Path.GetExtension(fileNameToBeProcessed).Length);

            if(_positionStringToBeAppended < tempFileName.Length)
                fileName = tempFileName.Insert(_positionStringToBeAppended, 
                _stringToBeAppended);
            else
            {
                StringBuilder sbdr = new StringBuilder(tempFileName);
                for(i = tempFileName.Length; 
            i < _positionStringToBeAppended; i++)
                    sbdr.Append(" ");
                tempFileName = sbdr.ToString();
                fileName = tempFileName.Insert(_positionStringToBeAppended, 
            _stringToBeAppended);
            }
        }
    }

    if (_advancedControl)
    {
        if (_replaceSpaceWithUnderscore)
            fileName = fileName.Replace(" ", "_");

        if (_replaceUnderscoreWithSpace)
            fileName = fileName.Replace("_", " ");

        if (_killVowels)
            fileName = StringFunctionality.KillVowels(fileName);

        if (_killEndAndDoubleSpace)
        {
            fileName = fileName.Trim();
            fileName = fileName.Replace("  ", "");
        }
    }

    switch( RenameWhichPart )
    {
        case RenamingOptions.RenameWholeName: newFileName = fileName; 
    break;
        case RenamingOptions.RenameNameOnly: newFileName = fileName + 
                            Path.GetExtension(fileNameToBeProcessed); 
    break;
        case RenamingOptions.RenameExtOnly: 
            newFileName = Path.GetFileName(fileNameToBeProcessed).Substring(0, 
                    Path.GetFileName(fileNameToBeProcessed).Length 
            - fileName.Length) + fileName;
    break;
        case RenamingOptions.RenameCustom: newFileName = 
            fileNameToBeProcessed.Substring(0, _splitPositionToBeRenamed - 1) + fileName; 
    break;
    }

    return true;
}

As you might notice, the function generates a new name of the file, but this is based on some conditions which are encapsulated in if-else blocks. Let us analyze and drill down further into these conditions.

Let me first list the class members which are privately owned.

C#
// Private members of Settings class
private bool _replacePrevFiles = false;
private bool _ignoreIllegalFiles = false;
private PatternOptions _textPatternToBeUsed = PatternOptions.PatternNone;
private RenamingOptions _renameWhichPart = RenamingOptions.RenameWholeName;
private int _splitPositionToBeRenamed = 0;
private bool _replaceStringWithExpression = false;
private string _stringToFindForReplace = "";
private string _stringToReplace = "";
private bool _insertStringToFileName = false;
private string _stringToBeAppended = "";
private int _positionStringToBeAppended = 0;

private bool _advancedControl = false;

private bool _replaceSpaceWithUnderscore = false;
private bool _replaceUnderscoreWithSpace = false;
private bool _killEndAndDoubleSpace = false;
private bool _killVowels = false;

Each of the above members are exposed to the outside world as public properties which can be configured by the GUI designed to modify the pattern. There are two enum types associated, viz.,

C#
/// <summary>
/// Type for handling the renaming part options
/// </summary>
public enum RenamingOptions
{
    /// <summary>
    /// Renames the name part of file-name
    /// </summary>
    RenameNameOnly,
    /// <summary>
    /// Renames the extension only
    /// </summary>
    RenameExtOnly,
    /// <summary>
    /// Renames whole name
    /// </summary>
    RenameWholeName,
    /// <summary>
    /// Renaming starts from specified position
    /// </summary>
    RenameCustom
}

/// <summary>
/// Type for handling the pattern options
/// </summary>
public enum PatternOptions
{
    /// <summary>
    /// Lower-case pattern
    /// </summary>
    PatternLowerCase,
    /// <summary>
    /// Upper-case pattern
    /// </summary>
    PatternUpperCase,
    /// <summary>
    /// Init-cap pattern
    /// </summary>
    PatternInitCap,
    /// <summary>
    /// Random case pattern
    /// </summary>
    PatternRandomCase,
    /// <summary>
    /// Invert case pattern
    /// </summary>
    PatternInvertCase,
    /// <summary>
    /// No changes to the name
    /// </summary>
    PatternNone
}

The renaming options are used to specify which part of the file name is to be used for processing and the pattern options are used to perform some processing to the text of the file name in order to generate the new file name.

Settings screen

As might be clear from the above screenshot, each property of the Setting class is associated with one of the options displayed corresponding to it on the screen. Based on the selected options by the user, the corresponding variables are checked and a new file name is generated.

All string related processing is encapsulated in StringFunctionality class. All members of this class are static.

For a more recent download, you can visit my website [http://amitbhandari.co.nr].

Points of Interest

The whole process of string processing is done using the StringBuilder object, however, String objects are still used where there is no other option left.

In addition, the emphasis in all the further revisions of the code will be based on Regular Expressions since it will make the pattern for renaming to be more precise. Moreover, complex conversions/replacements in the original filename string can be carried out easily.

History

  • v0.1.010 First initial release
  • v0.2.030 Beta release

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Software Developer (Senior)
India India
I: working as software engineer.
My Blog: http://blog.abstractcomputing.in

Comments and Discussions

 
QuestionRealy a nice work。I wonder if i have any permission to translate it to Simple-Chinese Pin
xcf00715-Oct-12 15:13
xcf00715-Oct-12 15:13 
QuestionRenaming Pin
cometo_raj13-Jun-12 19:27
cometo_raj13-Jun-12 19:27 
GeneralSandbar Pin
David!26-Nov-07 16:29
David!26-Nov-07 16:29 
AnswerRe: Sandbar Pin
A. Bhandari a.k.a Amit Bhandari26-Nov-07 22:46
A. Bhandari a.k.a Amit Bhandari26-Nov-07 22:46 
GeneralThreading, resources, sleeping and polling Pin
Leo Davidson27-Feb-07 6:49
Leo Davidson27-Feb-07 6:49 
AnswerRe: Threading, resources, sleeping and polling Pin
A. Bhandari a.k.a Amit Bhandari1-Mar-07 18:30
A. Bhandari a.k.a Amit Bhandari1-Mar-07 18:30 
First of all, let me thank you for such a great post. I really appreciate the concepts you have talked about.

Now, straight away coming to the point. Before looking on to the justifications, we need to look on the domain-part also, i.e, what the application is upto, the objective of the application and the interface it presents to the user.

The application is to provide batch-file processing (renaming) to the user for which we need the list of file(s) to be processed before hand and then the pattern to be used for renaming all the file(s) in the list. Once the list and pattern is clear, the whole list will be processed and file(s) will be renamed accordingly.


Leo Davidson wrote:
This has no effect on resource usage. It is good because it means the user interface is not locked up while the directory is listed, but it does not decrease resource usage. (In fact, it increases it slightly, but the tiny increase is worth it for the benefit of a non-blocked UI thread.)


The first thread is put in timer so that it can be polled on regular intervals for its status whether it has finished its work or still working. Also, the timer makes sense since we are showing an indefinite progress-bar to the user and the two processes are working asynchronously to each other. That's where the first requirement is met, i.e, we can't allow the user to proceed until and unless we build up the list of file(s) to be processed.


Leo Davidson wrote:
The Sleep(10) calls in FormBase.cs worry me. You should never sleep in a UI thread and there are rarely good reasons to call Sleep anywhere at all. I couldn't see why these Sleep calls were there.


The sleep(10) calls are made to make UI thread to be in non-working state and all the changes done to the filesystem gets refreshed so that exceptions, if any, gets flushed in and be caught whenever the thread wakes up.


Leo Davidson wrote:
The Timer is also a bad idea since the UI thread is polling the filesystem thread to see if it has finished yet. Instead of polling you should add an event to the filesystem class which the form can add a handler to. When the filesystem class is done it calls the form's handler and the form is notified at the exact moment that the work is done, without wasting system resources by asking if the job is done every few seconds. C# makes events very easy to use and they are well worth learning about.


However, I really do appreciate the concept of adding the notifier event but again, the same process will be followed since we are required to show an indefinite progress-bar to the user which will somewhat similar resource consumer in the scenario required.


Regards,
Amit Bhandari a.k.a A. Bhandari
GeneralRe: Threading, resources, sleeping and polling Pin
Saltire5-Mar-07 23:00
Saltire5-Mar-07 23:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.