1

I would like to show all file extensions in a specific folder and give the total for each extension using DirectoryStream.

Now I'm only showing all the files in that folder, but how do I get their extensions only instead? I should also get the extensions of these files and count the total for each extension in that folder (see output below).

public static void main (String [] args) throws IOException {

    Path path = Paths.get(System.getProperty("user.dir"));

    if (Files.isDirectory(path)){
        DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path);

        for (Path p: directoryStream){
            System.out.println(p.getFileName());
        }
    } else {
        System.out.printf("Path was not found.");
    }
}

The output should look like this. I suppose the best way to get this output is using lambdas?

FILETYPE    TOTAL
------------------
CLASS    |  5
TXT      |  10
JAVA     |  30
EXE      |  27
Hülya
  • 3,353
  • 2
  • 12
  • 19
Sitjuh
  • 107
  • 8

2 Answers2

4

First check whether it is a file, if so extract the file name extension. Finally use the groupingBy collector to get the dictionary structure you want. Here's how it looks.

try (Stream<Path> stream = Files.list(Paths.get("path/to/your/file"))) {
    Map<String, Long> fileExtCountMap = stream.filter(Files::isRegularFile)
        .map(f -> f.getFileName().toString().toUpperCase())
        .map(n -> n.substring(n.lastIndexOf(".") + 1))
        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
}
Ravindra Ranwala
  • 20,744
  • 6
  • 45
  • 63
  • 1
    You can change `.getName()` to `.getName().toUpperCase()` or add `map(String::toUpperCase)` to avoid problems with `foo.exe` and `FOO.EXE` where collector would count `exe` and `EXE` as separate extensions. – Pshemo Mar 30 '19 at 17:27
  • Thanks for the suggestion. I have updated the answer. – Ravindra Ranwala Mar 30 '19 at 17:31
  • 1
    Instead of reverting to the obsolete java.io.File class, I would use `stream.filter(path -> Files.isRegularFile(path))`. Similarly, `map(s -> s.getFileName().toString().toUpperCase())`. – VGR Mar 30 '19 at 19:28
  • @VGR the former can be further improved by using a method reference. I have updated the answer. Thanks for the suggestion. Appreciate it. – Ravindra Ranwala Mar 31 '19 at 02:20
  • Thanks for the help! Now, in my case, how do I implement your solution using DirectoryStream? Methods as filter(), getFileName(), substring() don't work on directoryStream. – Sitjuh Mar 31 '19 at 04:22
  • Ok, I figured out it's impossible for DirectoryStream. I tried your solution and I'm able to get the output I need. Thanks a lot Ravindra, this helped me out, I'm getting closer to understanding. Sorry again for the duplicate question, I didn't realize I had confirmed this question this morning. – Sitjuh Mar 31 '19 at 04:40
0

You can try something like this:

public class FileCount {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get(System.getProperty("user.dir"));

        if (Files.isDirectory(path)) {

            Map<String, Long> result = Files.list(path).filter(f -> f.toFile().isFile()).map(FileCount::getExtension)
                    .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

            System.out.println(result);
        } else {
            System.out.printf("Path was not found.");
        }

    }

    public static String getExtension(Path path) {
        String parts[] = path.toString().split("\\.");
        if (1 < parts.length) {
            return parts[parts.length - 1];
        }

        return path.toString();
    }

You can even return the Map and arrange the results in the way you want.

Mihai
  • 9,526
  • 2
  • 18
  • 40