Algorithm

파일 통계 프로그램

태인킴 2020. 9. 21. 17:35
반응형

1. 문제

music : mp3, aac, flac

image : jpg, bmp, gif

movie : mp4, aiv, mkv

other : 7z, txt, zip

위와 같은 파일 확장자가 존재 하고, music, image, movie, other 그룹으로 분리 할수 있다.

 

input :

"my.song.mp3 11b

greatSong.flac 1000b

not3.txt 5b

video.mp4 200b

game.exe 100b

mov!e.mkv 10000b"

위와 같은 input 이 주어 졌을 때, 아래와 같은 반환 값이 출력 되도록 solution 함수를 구현 하시오.

 

return :

"music 1011b

images 0b

movies 10200b

other 105b

"

 

파일이름, 확장자, 파일 크기 의 규칙은 아래와 같습니다.

 

파일 이름

1. 영어 대/소문자 만 허용

2. 특수문자 : ^&'@{}[],$=!-#()%.+~_ 만 허용

3. 숫자 (0 ~ 9)

 

확장자

1. 영어 소문자

2. 숫자 (0 ~ 9)

 

파일 크기

1. 양수 ~ 1,000,000b(백만 바이트)

 

 

2. 접근

1. 띄어쓰기를 기준으로, 파일이름과 크기를 분리

2, 파일 이름 중에서 끝에서 부터 탐색하여 (. dot) 을 찾아내고

3. dot 뒤의 문자열을 파일 타입으로 정의

4. 4가지 타입 중 하나로 분류

5. 해당 라인의 용량을 저장

6. 출력 포맷의 맞게 출력

위와 같이, 알고리즘 구현 접근을 하였습니다. 오늘 글 포스팅은 기능 확장 및 수정의 초점을 맞춰서 포스팅 하려고 합니다.

 

 

3. 1차 Java 소스 코드

import java.util.ArrayList;
import java.util.List;

public class Task2020_2 {
    public static void main(String[] args) {
        String input = "my.song.mp3 11b\n" +
                "greatSong.flac 1000b\n" +
                "not3.txt 5b\n" +
                "video.mp4 200b\n" +
                "game.exe 100b\n" +
                "mov!e.mkv 10000b";
        System.out.print(new Task2020_2().solution(input));
    }
   
    FileSizeGroup fileSizeGroup;

    public String solution(String S) {
        // 4. 4가지 타입 중 하나로 분류
        this.fileSizeGroup = new FileSizeGroup();

        // 각 라인을 분리
        String[] rows = S.split("\n");

        for (String row : rows) {
            // 1. 띄어쓰기를 기준으로, 파일이름과 크기를 분리
            String fileFullName = row.split(" ")[0];
            String sizeStr = row.split(" ")[1];
            int size = Integer.parseInt(sizeStr.substring(0, sizeStr.length() - 1));

            // 2. 파일 이름 중에서 끝에서 부터 탐색하여 (.(dot))을 찾아내고
            // 3. dot 뒤의 문자열을 파일 타입으로 정의
            for (int i = fileFullName.length() - 1; i >= 0; i--) {
                if (fileFullName.charAt(i) == '.') {
                    String fileType = fileFullName.substring(i + 1);
                    // 4. 4가지 타입 중 하나로 분류
                    insertFileSizeToFileGroup(fileType, size);
                    break;
                }
            }
        }

        StringBuilder sb = new StringBuilder();

        // 5. 해당 라인의 용량을 저장
        for (FileType value : FileType.values()) {
            String type = value.name;
            int size = fileSizeGroup.getSumSize(value);
            // 6. 출력 포맷의 맞게 출력
            sb.append(type).append(" ").append(size).append("b");
            sb.append(System.lineSeparator());
        }

        return sb.toString();
    }

    public void insertFileSizeToFileGroup(String fileType, int size) {
        switch (fileType) {
            //music
            case "mp3" :
            case "aac" :
            case "flac" : {
                this.fileSizeGroup.musicSizes.add(size);
                return;
            }
            //image
            case "jpg" :
            case "bmp" :
            case "gif" : {
                this.fileSizeGroup.imageSizes.add(size);
                return;
            }
            //movie
            case "mp4" :
            case "avi" :
            case "mkv" : {
                this.fileSizeGroup.movieSizes.add(size);
                return;
            }
            //other
            default : {
                this.fileSizeGroup.otherSizes.add(size);
                return;
            }
        }
    }

    class FileSizeGroup {
        List<Integer> musicSizes;
        List<Integer> imageSizes;
        List<Integer> movieSizes;
        List<Integer> otherSizes;

        public FileSizeGroup() {
            this.musicSizes = new ArrayList<>();
            this.imageSizes = new ArrayList<>();
            this.movieSizes = new ArrayList<>();
            this.otherSizes = new ArrayList<>();
        }

        int getSumSize(FileType fileType){
            int sum = 0;

            if (fileType == FileType.MUSIC)
                sum = getSumFrom(musicSizes);

            if (fileType == FileType.IMAGE)
                sum = getSumFrom(imageSizes);

            if (fileType == FileType.MOVIE)
                sum = getSumFrom(imageSizes);

            if (fileType == FileType.OTHER)
                sum = getSumFrom(otherSizes);

            return sum;
        }

        private int getSumFrom(List<Integer> list) {
            int sum = 0;
            for (Integer val : list) {
                sum += val;
            }
            return sum;
        }
    }

    enum FileType {
        MUSIC("music"), IMAGE("images"),
        MOVIE("movies"), OTHER("other");

        String name;

        FileType(String name) {
            this.name = name;
        }
    }
}

 

여기서 눈여겨 볼것은 insertFileSizeToFileGroup() 함수와 FileSizeGroup 클래스 입니다. insertFileSizeToFileGroup 함수에서는 파일 타입 그룹별switch 문을 이용해서 그룹별로 size를 저장 합니다. 그리고 해당 그룹의 파일 size 를 출력 하기 위해서는 FileSizeGroup.getSumSize()에서 그룹별로 저장해둔 size를 더해서 반환해 줍니다. 

 

만약, 'text' 라는 그룹의 'txt' 확장자가 추가 된다면, 위 소스에서 어디를 수정해 주어야 할까요? 

 

1. FileType enum

enum FileType {
        MUSIC("music"), IMAGE("images"),
        MOVIE("movies"), TEXT("text"),
        OTHER("other");

        String name;

        FileType(String name) {
            this.name = name;
        }
    }

'TEXT' 라는 파일 타입 그룹을 추가 시켜주면 됩니다.

 

 

2. FileSizeGroup class

class FileSizeGroup {
        List<Integer> musicSizes;
        List<Integer> imageSizes;
        List<Integer> movieSizes;
        List<Integer> textSizes;
        List<Integer> otherSizes;

        public FileSizeGroup() {
            this.musicSizes = new ArrayList<>();
            this.imageSizes = new ArrayList<>();
            this.movieSizes = new ArrayList<>();
            this.textSizes = new ArrayList<>();
            this.otherSizes = new ArrayList<>();
        }

        int getSumSize(FileType fileType){
            int sum = 0;

            if (fileType == FileType.MUSIC)
                sum = getSumFrom(musicSizes);

            if (fileType == FileType.IMAGE)
                sum = getSumFrom(imageSizes);

            if (fileType == FileType.MOVIE)
                sum = getSumFrom(imageSizes);

            if (fileType == FileType.TEXT)
                sum = getSumFrom(textSizes);

            if (fileType == FileType.OTHER)
                sum = getSumFrom(otherSizes);

            return sum;
        }
        ...
    }

'TEXT' 라는 파일 타입 그룹이 추가 될수록 getSumSize 함수의 if 문은 추가 될것 입니다. 또한 List<Integer> textSizes 변수도 추가 될것 입니다. 

 

 

3. insertFileSizeToFileGroup 함수

public void insertFileSizeToFileGroup(String fileType, int size) {
        switch (fileType) {
            //music
            case "mp3" :
            case "aac" :
            case "flac" : {
                this.fileSizeGroup.musicSizes.add(size);
                return;
            }
            //image
            case "jpg" :
            case "bmp" :
            case "gif" : {
                this.fileSizeGroup.imageSizes.add(size);
                return;
            }
            //movie
            case "mp4" :
            case "avi" :
            case "mkv" : {
                this.fileSizeGroup.movieSizes.add(size);
                return;
            }
            //movie
            case "txt" : {
                this.fileSizeGroup.textSizes.add(size);
                return;
            }
            //other
            default : {
                this.fileSizeGroup.otherSizes.add(size);
                return;
            }
        }
    }

'txt' 파일 확장자가 추가되면 switch 문의 case는 추가 될것 입니다. insertFileSizeToFileGroup 함수는 Task2020_2 클래스의 메서드로 정의 되어 있지만, 'txt', 'mp3' 등 파일 확장자의 데이터를 굳이 가지고 있을 필요가 없고, FileType enum 에서 관리를 해주도록 수정 할수 있을거 같습니다.

 

따라서, 파일 확장자 추가 및 파일 그룹 추가시 확장의 용이 하도록 아래와 같이 수정 하였습니다.

 

 

4. 2차 Java 소스 코드

import java.util.*;

public class Task2020_2_2 {
    public static void main(String[] args) {
        String input = "my.song.mp3 11b\n" +
                "greatSong.flac 1000b\n" +
                "not3.txt 5b\n" +
                "video.mp4 200b\n" +
                "game.exe 100b\n" +
                "mov!e.mkv 10000b";
        System.out.print(new Task2020_2_2().solution(input));
    }
   
    public String solution(String S) {
        // 각 라인을 분리
        String[] rows = S.split("\n");

        for (String row : rows) {
            // 1. 띄어쓰기를 기준으로, 파일이름과 크기를 분리
            String fileFullName = row.split(" ")[0];
            String sizeStr = row.split(" ")[1];
            int size = Integer.parseInt(sizeStr.substring(0, sizeStr.length() - 1));

            // 2. 파일 이름 중에서 끝에서 부터 탐색하여 (.(dot))을 찾아내고
            // 3. dot 뒤의 문자열을 파일 타입으로 정의
            for (int i = fileFullName.length() - 1; i >= 0; i--) {
                if (fileFullName.charAt(i) == '.') {
                    String fileTypeStr = fileFullName.substring(i + 1);
                    // 4. 4가지 타입 중 하나로 분류
                    FileType fileType = FileType.convertStrToFileType(fileTypeStr);
                    FileTypeGroup fileTypeGroup = FileTypeGroup.findByFileType(fileType);
                    fileTypeGroup.addSize(size);
                    break;
                }
            }
        }

        StringBuilder sb = new StringBuilder();

        // 5. 해당 라인의 용량을 저장
        for (FileTypeGroup value : FileTypeGroup.values()) {
            String type = value.title;
            int size = value.getTotal();
            // 6. 출력 포맷의 맞게 출력
            sb.append(type).append(" ").append(size).append("b");
            sb.append(System.lineSeparator());
        }

        return sb.toString();
    }

    enum FileTypeGroup {
        MUSIC("music", Arrays.asList(FileType.MP3, FileType.AAC, FileType.FLAC)),
        IMAGE("images", Arrays.asList(FileType.JPG, FileType.BMP, FileType.GIF)),
        MOVIE("movies", Arrays.asList(FileType.MP4, FileType.AVI, FileType.MKV)),
        OTHER("other", Arrays.asList(FileType.OTHER));

        String title;
        List<FileType> fileTypes;
        int total;

        FileTypeGroup(String title, List<FileType> fileTypes) {
            this.title = title;
            this.fileTypes = fileTypes;
        }

        void addSize(int size) {
            this.total += size;
        }

        int getTotal() {
            return total;
        }

        public static FileTypeGroup findByFileType(FileType fileType){
            return Arrays.stream(FileTypeGroup.values())
                    .filter(FileTypeGroup -> FileTypeGroup.hasFileType(fileType))
                    .findAny()
                    .orElse(OTHER);
        }

        public boolean hasFileType(FileType fileType) {
            return  fileTypes.stream()
                    .anyMatch(elem -> elem == fileType);
        }
    }

    enum FileType {
        MP3("mp3"), AAC("aac"), FLAC("flac"),
        JPG("jpg"), BMP("bmp"), GIF("gif"),
        MP4("mp4"), AVI("avi"), MKV("mkv"),
        OTHER("other");

        String title;

        FileType(String title) {
            this.title = title;
        }

        public static FileType convertStrToFileType(String title) {
            return  Arrays.stream(FileType.values())
                    .filter(elem -> elem.title.equals(title))
                    .findAny()
                    .orElse(OTHER);
        }
    }
}

Task2020_2 클래스의 insertFileSizeToFileGroup 함수가 사라지고, FileType 과 관련된 데이터가 사라지므로써, FileType의 의존하지 않습니다. FileTypeGroup 의 고수준 클래스는 FileType 의 저수준 클래스에도 의존하지 않고, TEXT 의 정의만 추가해 줄 수 있습니다.

 

그렇다면, 이 소스 코드에서 TEXT 그룹과 'txt' 확장자 추가시 아래의 소스 코드 처럼 수정 할수 있을거 같습니다. 

 

 

1. FileType enum의 TXT enum 추가

enum FileType {
        MP3("mp3"), AAC("aac"), FLAC("flac"),
        JPG("jpg"), BMP("bmp"), GIF("gif"),
        MP4("mp4"), AVI("avi"), MKV("mkv"),
        TXT("txt"),
        OTHER("other");

        String title;

        FileType(String title) {
            this.title = title;
        }

        public static FileType convertStrToFileType(String title) {
            return  Arrays.stream(FileType.values())
                    .filter(elem -> elem.title.equals(title))
                    .findAny()
                    .orElse(OTHER);
        }
    }

TXT("txt") 정의만 해주면 됩니다.

 

 

2. FileTypeGroup enum의 TEXT enum추가

enum FileTypeGroup {
        MUSIC("music", Arrays.asList(FileType.MP3, FileType.AAC, FileType.FLAC)),
        IMAGE("images", Arrays.asList(FileType.JPG, FileType.BMP, FileType.GIF)),
        MOVIE("movies", Arrays.asList(FileType.MP4, FileType.AVI, FileType.MKV)),
        TEXT("text", Arrays.asList(FileType.TXT)),
        OTHER("other", Arrays.asList(FileType.OTHER));

        String title;
        List<FileType> fileTypes;
        int total;

        FileTypeGroup(String title, List<FileType> fileTypes) {
            this.title = title;
            this.fileTypes = fileTypes;
        }

        void addSize(int size) {
            this.total += size;
        }

        int getTotal() {
            return total;
        }

        public static FileTypeGroup findByFileType(FileType fileType){
            return Arrays.stream(FileTypeGroup.values())
                    .filter(FileTypeGroup -> FileTypeGroup.hasFileType(fileType))
                    .findAny()
                    .orElse(OTHER);
        }

        public boolean hasFileType(FileType fileType) {
            return  fileTypes.stream()
                    .anyMatch(elem -> elem == fileType);
        }
    }

FileTypeGroup 의 TEXT("text", Arrays.asList(FileType.TXT)) 정의 만 추가해 주었습니다.

 

반응형