3
\$\begingroup\$

I have over 2500 files in a directory tree- some of them are numbered, some of them aren't. eg.

1. That night it rained.epub
The sparrow returns at dawn.epub
001.  Tomorrow is a new day.epub
000.1. We are the children.epub

I created my C# code to iterate through all the directories and rename the numbered files to look neater, but at the same time should leave the unnumbered file unaltered like so:

001.    That night it rained.epub
The sparrow returns at dawn.epub
001.    Tomorrow is a new day.epub
000.1.  We are the children.epub

My coding looks like this:

class Program
{

static string path = @"C:\Users\Anja\Desktop\reekse";


static string name;
static string rpath;
static int numberperiod;
static string number;
static string newname;
static string movepath;

static void Main(string[] args)
    {


    foreach (var file in Directory.GetFiles(path, "*.epub", SearchOption.AllDirectories)) //searches for all the epub files even if there are 20 subdirectories between the file and the main directory
        {

        name = System.IO.Path.GetFileName(file);//gets the filename sans the path
        rpath = System.IO.Path.GetDirectoryName(file);//gets the path sans the  file

        numberperiod = name.IndexOf(".");//find the location of the 1st . (remember-zerobasing is used)

        if (numberperiod == 1)//first period - meaning no 0's is used in numbering
            {
            number = name.Substring(0, numberperiod + 1);//pick up the number as well as the . that follows the numbering


            name = name.TrimStart(number.ToCharArray());//convert the number to char and trim it
            name = name.TrimStart('.', ' ', ' ');// remove any potential blankspaces and . that may be at the start of the filename

            newname = "00" + number + "    " + name; // create a name that has the 0's, the number, adequite spacing, and the name
            }


        if (numberperiod == 2)//same as above but the numbering contains  two digits
            {
            number = name.Substring(0, numberperiod + 1);


            name = name.TrimStart(number.ToCharArray());
            name = name.TrimStart('.', ' ', ' ');


            newname = "0" + number + "    " + name;

            }

        if (numberperiod == 3)//files containing 3 digits
           {
            number = name.Substring(0, numberperiod + 1);

            if (number == "000.")// in case the file has odd numbering as  in the example
                {
                number = name.Substring(0, numberperiod + 3);


                name = name.TrimStart(number.ToCharArray());
                name = name.TrimStart('.', ' ', ' ');


                newname = number + "  " + name;
                }
            else
                {
                name = name.TrimStart(number.ToCharArray());
                name = name.TrimStart('.', ' ', ' ');


                newname = number + "    " + name;
                }

            }


        Console.WriteLine(newname.ToString());

        movepath = Path.Combine(rpath, newname);
        File.Move(file, movepath);
        }
   }
}

The coding works perfectly fine, but it just seems so tedious; I'm positive that there must be a simpler way of doing this like regex or Linq.

Can someone please help? I'm not any good when it comes to either Linq or regex. Can someone please help me rewrite this code to make it simpler?

\$\endgroup\$
1
  • \$\begingroup\$ I cannot address the code you've written directly but I think the gut feeling that brought you here is because files + renaming + regex are very much shell-scripting territory. A compiled language is simply not "the tool for the job". \$\endgroup\$
    – drekbour
    Commented May 17, 2020 at 21:47

1 Answer 1

2
\$\begingroup\$

using Regex.Replace with a help of PadLeft would do this in a single line. so your work can be simplified to :

foreach (var file in Directory.GetFiles(path, "*.epub", SearchOption.AllDirectories)) 
{
    var fileName = System.IO.Path.GetFileName(file);
    var filePath = System.IO.Path.GetDirectoryName(file);//gets the path sans the  file                 
    var strName  = Regex.Replace(fileName, @"^((\d+\.){1}(\d\.)?\s+)", m => m.Groups[0].Value.Trim().PadLeft(4, '0').PadRight(8));

    File.Move(file, Path.Combine(filePath, strName));
}

The regex should covers any string with the same pattern in your provided sample.

UPDATE :

To make it more readable for you, you can use the following :

var regex = new Regex(@"^([0-9]+\.?\.\s)");

var fileName = new StringBuilder();

foreach (var file in Directory.GetFiles(path, "*.epub", SearchOption.AllDirectories))
{
    fileName.Append(Path.GetFileName(file));
    var filePath = System.IO.Path.GetDirectoryName(file);

    var match = regex.Match(fileName.ToString());

    if (match.Success)
    {
        fileName.Clear();
        fileName
            .Append(match.Value.Trim().PadLeft(4, '0'))
            .Append("    ")
            .Append(file.Substring(match.Value.Length).Trim());
    }

    fileName.Replace(fileName.ToString(), Path.Combine(Path.GetDirectoryName(file), fileName.ToString()));

    File.Move(filePath, fileName.ToString());

    fileName.Clear(); 
}

Using StringBuilder is a must in your case, since you're dealing with a list of files name, and since string is immutable, using StringBuilder will avoid creating a new string for each filename which would save your memory and also optimize the performance.

\$\endgroup\$
7
  • \$\begingroup\$ Thank you so much for helping me renumber my files. It works like a charm! But can you please help me solve the Whitespace-issue? I want 4 whitespace-characters to be added between the number and the files actual name. ("001.----Each dash represents a whitespace-character) I want it to be look neat. \$\endgroup\$
    – diedomkop
    Commented May 23, 2020 at 19:42
  • \$\begingroup\$ @diedomkop I have updated my answer with another simplified code, I avoided using expressions to make it readable and easy to edit. \$\endgroup\$
    – iSR5
    Commented May 24, 2020 at 4:06
  • \$\begingroup\$ There's no benefit in using StringBuilder in this code. Since you call ToString, the new string gets created anyway. \$\endgroup\$ Commented May 24, 2020 at 7:56
  • \$\begingroup\$ @RolandIllig yes, it would create one string per iteration instead of multiple strings just for the name! \$\endgroup\$
    – iSR5
    Commented May 24, 2020 at 13:11
  • \$\begingroup\$ If you rewrite the replace line in the first version to var strName = Regex.Replace(fileName, @"^((\d+\.){1}(\d\.)?\s+)", m => m.Groups[0].Value.Trim().PadLeft(4, '0').PadRight(8)); it will work with regex only. \$\endgroup\$
    – user73941
    Commented May 24, 2020 at 14:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.