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?
-
\$\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– drekbour2020年05月17日 21:47:47 +00:00Commented May 17, 2020 at 21:47
1 Answer 1
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.
-
\$\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– diedomkop2020年05月23日 19:42:02 +00:00Commented 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– iSR52020年05月24日 04:06:32 +00:00Commented May 24, 2020 at 4:06
-
\$\begingroup\$ There's no benefit in using
StringBuilder
in this code. Since you callToString
, the new string gets created anyway. \$\endgroup\$Roland Illig– Roland Illig2020年05月24日 07:56:28 +00:00Commented 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– iSR52020年05月24日 13:11:48 +00:00Commented 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– user739412020年05月24日 14:36:27 +00:00Commented May 24, 2020 at 14:36