Ben Foster
Ben Foster

Reputation: 34800

c# change file name of relative or absolute uri

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

Answers (4)

Ian Boyd
Ian Boyd

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

Ben Foster
Ben Foster

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

Carvellis
Carvellis

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

Uwe Keim
Uwe Keim

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

Related Questions