4

In my code, I would like to transform a path of the form

/a/path/to/a/file/image.jpg

to

/a/path/to/a/file/image_resized.jpg

Currently, I am using the the following code which uses FilenameUtils from apache commons IO.

public Path resize(Path original) {
    String baseName = FilenameUtils.getBaseName(original.toString());
    String extension = FilenameUtils.getExtension(original.toString());
    return Paths.get(original.getParent().toString(), baseName + "_resized." + extension);
}

I was wondering if some of this code could be enhanced using java 8 features, specifically:

  • is there a java-8 way for extracting the extension and basename, without using a dependency on Apache Commons IO (FilenameUtils), and without regex (I prefer dependency on apache commons IO over using a regex here)
  • joining to Paths without toString() in Paths.get(existingPath.toString(), "path/to/append");

The second part of the question is answered in Combine paths in Java

Stijn Haezebrouck
  • 387
  • 1
  • 2
  • 14
  • See also https://stackoverflow.com/questions/30913799/is-there-a-new-java-8-way-of-retrieving-the-file-extension – janos Aug 07 '17 at 20:43
  • It seems there is no new way to extract extension in Java 8 (https://stackoverflow.com/questions/30913799/is-there-a-new-java-8-way-of-retrieving-the-file-extension) – jeanr Aug 07 '17 at 20:44
  • indeed, that pretty much answers it – Stijn Haezebrouck Aug 08 '17 at 04:35
  • And avoiding the .toString() in my question above, or better said, joining two Paths, can be done using `existingPath.resolve("path/to/append");` - https://stackoverflow.com/questions/412380/combine-paths-in-java – Stijn Haezebrouck Aug 08 '17 at 04:44

5 Answers5

5

You don't need a library for such a small and simple task IMO (and no java-8 does not add support for that); and I also can't tell why a regex is out of the question

    int where = input.lastIndexOf(".");

    String result = input.substring(0, where) + "_resized" + input.substring(where);
    System.out.println(result);
Eugene
  • 117,005
  • 15
  • 201
  • 306
3

How about this? you can use Path#resolveSibling(String) to get the path relative to the original path. and use the Path#getFileName to get the filename in path.

thanks to @Holger point out that I can use the regex "(.*?)(\\.[^.]+)?$" instead.

public Path resize(Path original) {
    return original.resolveSibling(original.getFileName().toString()
            //               v--- baseName 
            .replaceFirst("(.*?)(\\.[^.]+)?$", "$1_resized$2"));
            //                      ^--- extension            
}

Output

//            v---for printing, I used the `String` here,actually is Paths.get(...)
resize("/a/b/image.jpg") =>  "/a/b/image_resized.jpg";

resize("/a/b/image.1.jpg") => "/a/b/image.1_resized.jpg";

resize("/a/b/image1") => "/a/b/image1_resized";
holi-java
  • 29,655
  • 7
  • 72
  • 83
  • I like the resolveSibling, but find the regex a bit heavy to read. I combined your resolveSibling with a solution using lastIndexOf instead, as a new answer. – Stijn Haezebrouck Aug 08 '17 at 05:09
  • @StijnHaezebrouck you shouldn't worry about the regex, because the filename is shorter and it is closed to use `lastIndexOf`. And use the regex can make the rest of work easily. – holi-java Aug 08 '17 at 05:32
  • 2
    What does `(?=.*?)` mean? A positive look-ahead that accepts anything? Looks entirely obsolete to me. Besides that, why so complicated? Why not just `.replaceFirst("(\\.[^.]+)?$", "_resized$1")`? Or, if you want to care for even the smallest bits of performance, `.replaceFirst("(?:\\.[^.]++)?$", "_resized$0")` – Holger Aug 08 '17 at 12:14
  • @Holger sir, thanks for the feedback. :), my intention is makes the 2 parts `baseName` & `.extension` clearly by use 2 look-aheads. can I do it like that? – holi-java Aug 08 '17 at 14:04
  • 2
    No, these lookaheads are called [zero-width lookahead](https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html#special), because they do not advance the text position, in other words, if you place one lookahead after another, both are applied to the same position. If you want to match both parts, just don’t use lookaheads `.replaceFirst("(.*?)(\\.[^.]+)?$", "$1_resized$2")` – Holger Aug 08 '17 at 14:45
  • @Holger thanks very much sir. " *if you place one lookahead after another, both are applied to the same position.* " - could you give me a min example? – holi-java Aug 08 '17 at 15:06
  • 2
    When you search for `(?=[0-5])(?=[5-9]).`, both lookaheads and the dot are applied to the same position, so it will match occurrences of `5` and nothing else. – Holger Aug 08 '17 at 15:10
  • @Holger yeah. I can produce the problem of the `(?=[0-5])(?=[5-9])`, but I can't produce the problem of my regex, I think the trick thing is it has a `\\.` between them, Am I right? – holi-java Aug 08 '17 at 15:17
  • 2
    As said in my first comment, `(?=.*?)` matches *anything*, therefore, is entirely obsolete. It doesn’t matter that it doesn’t match from start to the dot, as you thought, but from the dot to the end instead. `.*?` will always be fulfilled, regardless of where it is applied. And it doesn’t advance the match position. In other words, it has no effect. You can remove it, and the result doesn’t change. `(\\.[^.]+)?$` is already sufficient to match an extension (or the end position if there is no dot). Using a lookahead like your `(?=\\.[^.]+)?$` will match the position before the extension. – Holger Aug 08 '17 at 15:20
  • @Holger I'm sorry, sir. I have forgot the previous context, :(. thanks for your advice. – holi-java Aug 08 '17 at 15:24
  • @Holger After read your comments again and again, I found I misread the word **obsolete** ago. Eventually, I found you mean that it is **redundant**, :( ... thanks very much, sir. let me learn more and more. – holi-java Aug 08 '17 at 16:02
  • Using named regex makes the regex perhaps easier to read, but is little more verbose: `.replaceFirst("(?.*?)(?\\.[^.]+)?$", "${baseName}_resized${extension}"));` – Stijn Haezebrouck Aug 08 '17 at 21:16
2

I'm sure that Java8 doesn't have anything to deal with this. Maybe you can checkout the Files API from Guava.Files.getFileExtension(fileName)

Jeeppp
  • 1,553
  • 3
  • 17
  • 39
0

This problem can be solved by using simple string operations, you don't need libraries for stuff like this. One of the solution to this problem is -

class Solution {
    public static void main(String[] args) {
     String path = "/a/path/to/a/file/image.jpg";
     String[] temp = path.split("/");
     String[] tmp = temp[temp.length - 1].split("\\.");
     tmp[0] = tmp[0] + "-resized";
     String fullname = String.join(".", tmp);
     temp[temp.length - 1] = fullname;
     String newPath =  String.join("/", temp);
     System.out.print(newPath);

  }
}
kinny94
  • 376
  • 1
  • 5
  • 22
0

So, I combine your answers into this one:

First, there seem to be no way of java build in way of extracting the file name extension in java (other stackoverflow question)

Then using the String.lastIndexOf() is a simple reliable manner to get the extension (Eugene). Finally using Path.resolveSibling() (holi-java) joins the new filename with the parent path.

resulting code:

public Path resize(Path original) {
    String fileName = original.getFileName().toString();
    int extIndex = fileName.lastIndexOf(".");
    String baseName = fileName.substring(0,extIndex);
    String extension = fileName.substring(extIndex);
    return original.resolveSibling(baseName+"_resized"+extension);
}
Stijn Haezebrouck
  • 387
  • 1
  • 2
  • 14