# Adding "_old" to a file name

This is a trivial problem. The reason I'm here is to get a better idea of how to use Java's numerous APIs for working with files, folders, and paths. I'm hoping this specific example will help.

Problem Statement

I want to rename a file like this:

/path/to/myFile.txt

to this:

/path/to/myFile_old.txt

Ugly Solution

I've been trying to find a concise way to do this, and this is the best I can come up with:

Files.move(
Paths.get(file),
Paths.get("/" + FilenameUtils.getPath(file) + FilenameUtils.getBaseName(file) + "_old" + FilenameUtils.getExtension(file)));


It works, which is why I'm not on Stack Overflow, but

1. It's not portable because of the hardcoded "/".
2. It's really cumbersome -- specifically I don't like how verbose org.apache.commons.io.FilenameUtils is, and how I have to convert my Strings to Paths with Paths.get.

I've looked at using java.io.File or java.nio.file.Paths.resolve... but I haven't found anything tidy.

• I'm on mobile, so I can't write an answer, but I can give a hint: Look at the  class. It's got all sorts of neat features for modifying file names.
– Nic
Nov 13 '15 at 23:42
• It kinda sounds like rather than renaming the files, you should use version control instead. Nov 14 '15 at 13:23
• ...Mobile decided to cut out Path. Really sorry about that.
– Nic
Nov 16 '15 at 0:40

I think that the approach of the question lacks only a better usage of the API of java.nio.file.Path.

Using the old java.io.File seems very ugly nowadays, when we have java.nio.*, where all the stuff like file separators is supported much better.

// ...
String originalFile = "/path/to/myFile.txt";
Path originalPath = Paths.get(originalFile);
String backupFileName = buildBackupFileName(originalPath.getFileName().toString());
Path backupPath = originalPath.getParent().resolve(backupFileName);
Files.move(originalPath, backupPath); // add options if necessary
// ...

private static String buildBackupFileName(String fileName) {
String backupSuffix = "_old"; // or move it in a static constant
int lastDotIndex = fileName.lastIndexOf(".");
if (lastDotIndex > -1) {
return fileName.substring(0, lastDotIndex) + backupSuffix + fileName.substring(lastDotIndex);
}
return fileName + backupSuffix;
}


Other ways to build the backup file name: use StringBuilder or String.format, with or without the above FilenameUtils.

1. Well, the first thing that's bad is the the fact that everything is crammed on one line. Max line character count is usually 80.

2. To handle the hardcoded /, you can use File.separator. (You don't need it anyways for my solution.)

3. You don't need to use move(); instead, assuming file is a File object, you can use renameTo().

4. The thing that makes this so hard is the fact that String does not have an insert() method. Instead, we use StringBuilder's insert() method, which is better than what you have up there.

5. Continuing from point 4, don't use FilenameUtils (I don't even know what that is). Instead, use File, and its methods.

After all the suggested edits:

private static final String _OLD = "_old";

private String getNewFilename(File file, String tag) {
String path = file.getPath();
String[] nameAndExtension = file.getName().split("\\."); // extension after .
if (nameAndExtension.length == 1) { // no extension
return path;
}
return new StringBuilder(path).insert(path.length() -
nameAndExtension[1].length(), tag).toString();
}

public void appendOldTagToFile(File file) {
file.renameTo(new File(getNewFilename(file, _OLD)));
}


Yes, it looks longer, but it is more understandable (IMO) than yours, and does not require as much String concatenation.

Since file is a String, you can call it like so:

appendOldTagToFile(new File(file));


Or you can change the method:

private static final String _OLD = "_old";

private String getNewFilename(String filePath) {
String result = filePath.replaceFirst("\\.", "." + _OLD);
if (filePath.length == result.length) { // no extension
return result + _OLD;
}
return result;
}


This method does not need anything from java.io or java.nio, as it only uses String.