Reputation: 34800
I'm looking for a clean method of changing the filename of an absolute or relative uri, that may contain multiple segments. We don't know if its absolute or relative in advance, so we need to detect that:
Here's the test case:
[Test]
public void Can_change_path_file_name()
{
Fix("file.txt").ShouldEqual("file_fixed.txt");
Fix("/file.txt").ShouldEqual("/file_fixed.txt");
Fix("directory/file.txt").ShouldEqual("directory/file_fixed.txt");
Fix("/directory/file.txt").ShouldEqual("/directory/file_fixed.txt");
Fix("/directory/subdirectory/file.txt").ShouldEqual("/directory/subdirectory/file_fixed.txt");
Fix("http://www.test.com/directory/subdirectory/file.txt").ShouldEqual("http://www.test.com/directory/subdirectory/file_fixed.txt");
Fix("/directory/file/file.txt").ShouldEqual("/directory/file/file_fixed.txt");
}
private string Fix(string uri)
{
// fill me with goodness
}
My solution works but seems a bit involved. I've posted it below in case you want to better it:
private string Fix(string uri)
{
var fileName = Path.GetFileNameWithoutExtension(uri);
var extension = Path.GetExtension(uri);
fileName += "_fixed" + extension;
var path = uri.Contains('/') ? uri.Substring(0, uri.LastIndexOf('/') + 1) : "";
return Combine(path, fileName);
}
private string Combine(string path1, string path2)
{
if (path1 == null)
{
throw new ArgumentNullException("path1");
}
if (path2 == null)
{
throw new ArgumentNullException("path2");
}
if (String.IsNullOrEmpty(path2))
{
return path1;
}
if (String.IsNullOrEmpty(path1))
{
return path2;
}
if (path2.StartsWith("http://") || path2.StartsWith("https://"))
{
return path2;
}
var ch = path1[path1.Length - 1];
if (ch != '/')
{
return (path1.TrimEnd('/') + '/' + path2.TrimStart('/'));
}
return (path1 + path2);
}
Upvotes: 3
Views: 7325
Reputation: 256671
No regular expressions, no string indexes.
Just using .NET Path
methods to retreive, and reassemble, a new filename:
/// <summary>
/// Partner to Path.ChangeExtension. This function changes the base filename portion
/// </summary>
/// <param name="path"></param>
/// <param name="newFilename"></param>
/// <returns></returns>
public static String ChangeFilename(String path, String newFilename)
{
String directoryName = Path.GetDirectoryName(path); //e.g. "C:\Temp\foo.dat" ==> "C:\Temp"
//String oldFilename = Path.GetFileName(path); //e.g. "C:\Temp\foo.dat" ==> "foo.dat"
//String filenameWithoutExtension = Path.GetFileNameWithoutExtension(path); //e.g. "C:\Temp\foo.dat" ==> "foo"
String extension = Path.GetExtension(path); //e.g. "C:\Temp\foo.dat" ==> ".dat"
//Reassemble as
// directoryName \ newFilename dotExtension
return String.Format("{0}{1}{2}{3}",
directoryName,
Path.DirectorySeparatorChar,
newFilename,
extension);
}
Note: Any code is released into the public domain. No attribution required.
Upvotes: 4
Reputation: 34800
I've wrapped the working solution into a utility method. Should be some help to others.
/// <summary>
/// Changes the file name of the <paramref name="uri"/> using the given <paramref name="modifier"/>
/// </summary>
/// <param name="uri">A relative or absolute uri</param>
/// <param name="modifier">A function to apply to the filename</param>
/// <returns>The modified uri</returns>
public static string ModifyUriFileName(string uri, Func<string, string> modifier)
{
if (string.IsNullOrEmpty(uri))
{
throw new ArgumentNullException("uri");
}
var fileName = Path.GetFileNameWithoutExtension(uri);
var extension = Path.GetExtension(uri);
var path = uri.Substring(0, uri.LastIndexOf('/') + 1);
return string.Format("{0}{1}{2}", path, modifier(fileName), extension);
}
Usage:
return CommonUtils.ModifyUriFileName(uri, s => s + "_fixed");
Upvotes: 1
Reputation: 4042
Why not a simple string replace?
private string Fix(string uri)
{
var fileName = Path.GetFileNameWithoutExtension(uri);
var extension = Path.GetExtension(uri);
return uri.Replace(string.Format("{0}{1}", fileName, extension), string.Format("{0}_fixed{1}", fileName, extension));
}
This would only go wrong in case your filename will also occur in some other segment of the uri.
Another option that does not have this drawback is:
private string Fix(string uri)
{
var fileName = Path.GetFileNameWithoutExtension(uri);
var extension = Path.GetExtension(uri);
var slashIndex = uri.LastIndexOf("/");
return string.Format("{0}{1}_fixed{2}", uri.Substring(0, slashIndex + 1), fileName, extension);
}
Upvotes: 3
Reputation: 40736
How about some Regular Expression like
\b[a-zA-Z0-9]+\.[a-zA-Z0-9]+$
This should find all endings of file
followed by an extension.
Next, if you capture the file
...
\b([a-zA-Z0-9]+)(\.[a-zA-Z0-9]+)$
...you then can replace it:
var result = Regex.Replace(
input,
@"\b([a-zA-Z0-9]+)(\.[a-zA-Z0-9]+)$",
"$1_fixed$2" );
and you're done. You could use an online Regular Expression tester like this one to test before you code.
Upvotes: 1