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
- It's not portable because of the hardcoded "/".
- It's really cumbersome -- specifically I don't like how verbose
org.apache.commons.io.FilenameUtils
is, and how I have to convert myString
s toPath
s withPaths.get
.
I've looked at using java.io.File
or java.nio.file.Paths.resolve...
but I haven't found anything tidy.
2 Answers 2
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
.
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.
To handle the hardcoded
/
, you can useFile.separator
. (You don't need it anyways for my solution.)You don't need to use
move()
; instead, assumingfile
is aFile
object, you can userenameTo()
.The thing that makes this so hard is the fact that
String
does not have aninsert()
method. Instead, we useStringBuilder
'sinsert()
method, which is better than what you have up there.Continuing from point 4, don't use
FilenameUtils
(I don't even know what that is). Instead, useFile
, 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
.
-
\$\begingroup\$ According to the Javadocs of
Paths
andFilenameUtils
,file
is aString
. \$\endgroup\$h.j.k.– h.j.k.2015年11月14日 04:21:17 +00:00Commented Nov 14, 2015 at 4:21
Path
. Really sorry about that. \$\endgroup\$