What's a robust way to insert another extension into a filename?

String manipulation like this is probably done best using regular expressions. If you're not familiar with these, you may want to have a look at the Wikipedia article or the Mathematica tutorial on them first.

The code given below defines a pattern regex that matches an extension of the type .abcDeF... at the end of a string (that's accomplished by the $ sign). If that pattern does not match, then the extension is simply added; if the pattern matches, the extension is put in between the rest of the string and the match. Here's the code for that:

appendExtension[filename_, extension_] := Block[{regex},
    regex = RegularExpression["\\.[a-z]+$"];
    If[Length@StringCases[filename, regex] == 0,
        filename <> "." <> extension,
        StringReplace[filename, regex -> ("." <> extension <> "$0")]
    ]
];
appendExtension["/home/me/dir/foo.txt", "123"]
appendExtension["xyz.csv", "123"]
appendExtension["foobar", "123"]

Output:

"/home/me/dir/foo.123.txt"
"xyz.123.csv"
"foobar.123"

A few remarks:

  • I'm not sure how you want filenames like foo.bar.baz to be handled. The code above replaces this by foo.bar.123.baz, i.e. takes only the last extension into account. If you want the new one inserted as the leftmost one, change the regular expression to (\\.[a-z]+)+$, and the replacement will yield foo.123.bar.baz.
  • The regex doesn't include upper case extensions or digits. To add these, change [a-z] inside the regex to [a-zA-Z0-9].

Here's my non-regex solution (but it does use a "String Pattern", which is equivalent to regex). I think it is robust.

insertExtension[fn_String, piece_String] := 
 Module[{split = FileNameSplit[fn], temp},
  temp = Insert[StringSplit[Last[split], "."], piece, 2];
  temp = StringJoin[Riffle[temp, "."]];
  FileNameJoin[Append[Most[split], temp]]]

Test:

In[]:= insertExtension["/home/me.em/dir.ab/nomnom.tar.gz", "123"]

Out[]= "/home/me.em/dir.ab/nomnom.123.tar.gz"