본문 바로가기
개발/Unity

유니티 C# - 하위 폴더의 모든 파일 통합하기 (Recursive Folder Merger)

by 피로물든딸기 2022. 8. 25.
반응형

Unity 전체 링크

 

Window Visual Studio에서 폴더의 모든 파일 통합하기는 C, C++을 이용하여 하위 파일을 통합하였다.

 

여기서는 유니티 C#을 활용하여 만들어보자.

 

C, C++에서 사용된 함수는 C#에서는 아래와 같이 사용된다.

isDirectory - 폴더 / 파일 체크 함수 → C# 불필요

getAllFilePath - 해당 경로를 모두 읽어오는 함수 → C# Directory.GetFiles() / GetDirectories() 제공

deleteDirectoryFiles - 폴더, 파일 삭제 함수 FileInfo.Delete() 제공

deleteAllDirectoryFiles - 모든 폴더, 파일 삭제 함수

fileCopy - 파일 복사 함수  File.Copy() 제공

numOfDigits - 자릿수를 세는 함수

padStart - 자릿수의 빈칸을 앞부분부터 특정 문자로 채우는 함수

split - 특정 문자열 기준으로 분해하는 함수  string.Delete() 제공

getExtension - 파일의 확장자를 얻는 함수 → getLastSplit(string, delimeter)로 변형해서 사용

 

Ctrl + Shift + N으로 빈 오브젝트를 만든 후, RecursiveFolderMeger.cs를 추가하자.

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;

public class RecursiveFolderMerger : MonoBehaviour
{
    const string PATH = "C:\\Users\\username\\Desktop\\MyFiles";
    const string RESULT_PATH = "C:\\Users\\username\\Desktop\\Result";

    string getLastSplit(string str, char delimeter)
    {
        string[] split = str.Split(delimeter);
        int length = split.Length;

        return split[length - 1];
    }

    void deleteAllDirectoryFiles(string path)
    {
        DirectoryInfo di = new DirectoryInfo(path);

        if (di.Exists == false) return;

        FileInfo[] files = di.GetFiles();
        foreach (FileInfo file in files) file.Delete();

        DirectoryInfo[] subDirectories = di.GetDirectories();
        foreach (DirectoryInfo subDirectory in subDirectories) subDirectory.Delete();

        Directory.Delete(path);
    }

    void makeDirectory(string path)
    {
        DirectoryInfo di = new DirectoryInfo(path);
        if (di.Exists == false) di.Create();
    }

    int numOfDigits(int number)
    {
        int ret = 0;
        while(number != 0)
        {
            ret++;
            number /= 10;
        }

        return ret;
    }

    string padStart(int number, int padSize, char pad)
    {
        string strNum = number.ToString();
        string ret = "";

        for (int i = 0; i < padSize - strNum.Length; i++) ret = ret + pad;

        return ret + strNum;
    }

    void Start()
    {
        deleteAllDirectoryFiles(RESULT_PATH);
        makeDirectory(RESULT_PATH);

        string[] files = Directory.GetFiles(PATH, "*.*", SearchOption.AllDirectories);

        int padSize = numOfDigits(files.Length);
        int fileIndex = 0;

        foreach(string file in files)
        {
            Debug.Log(file);
            
            string fileName = getLastSplit(file, '\\');
            string destPath = RESULT_PATH + "\\" + fileName;

            File.Copy(file, destPath, true);

            string changeName = RESULT_PATH + "\\file_" + padStart(fileIndex++, padSize, '0') + "." + getLastSplit(fileName, '.');

            File.Move(destPath, changeName);
        }
    }
}

 

이제 아래 폴더를 적절한 경로(=PATH)에 놔둔 후 RESULT_PATH에 결과를 확인해보자.

MyFiles.zip
0.00MB

 

위의 폴더는 아래의 디렉토리와 파일로 이루어져 있다.

 

MyFiles - Folder1, 2, 3 / a, b, z

Folder1 - a, b, z

Folder1 - Folder1-1 - a, b, z

Folder1 - Folder1-2 - a, b, c

Folder2 - a, b, c

Folder3 - a, b

 

게임을 실행하면 아래의 결과를 얻는다.


하지만 위의 결과는 C, C++에서 파일을 합친 것다른 결과를 가진다.

 

각 파일에 recursive할 때와 C# 순서에 의한 번호를 저장해뒀으니 검증할 때 참고하자.


C, C++에서는 파일이 아래의 순서대로 결합된다. 

 

MyFiles - Folder1, 2, 3 / a(00), b(01), z(02)

Folder1 - a(03), b(04), z(05)

Folder1 - Folder1-1 - a(06), b(07), z(08)

Folder1 - Folder1-2 - a(09), b(10), c(11)

Folder2 - a(12), b(13), c(14)

Folder3 - a(15), b(16)

 

즉, recursive하게 파일을 먼저 찾고, 해당 폴더로 들어간 후, 파일을 찾고, 다시 폴더를 찾는다.

 

하지만 C#을 이용해서 합친 결과는 아래와 같다.

 

MyFiles - Folder1, 2, 3 / a(00), b(01), z(02)

Folder1 - a(03), b(04), z(05)

Folder1 - Folder1-1 - a(06 → 11), b(07  12), z(08  13)

Folder1 - Folder1-2 - a(09  14), b(10  15), c(11  16)

Folder2 - a(12  06), b(13  07), c(14  08)

Folder3 - a(15  09), b(16  10)

 

아래의 코드를 보자.

string[] files = Directory.GetFiles(PATH, "*.*", SearchOption.AllDirectories);

 

C#에서 제공하는 Directory.GetDirectories에 옵션으로

SearchOption.AllDirectories를 주면 하위 파일을 모두 찾는다.

 

하지만 문제는 recursive하게 찾지 않고, 같은 깊이의 폴더에 대해 먼저 찾는다.

즉, 첫 번째 깊이의 폴더 Folder1 - Folder2 - Folder3를 찾고,

두 번째 깊이의 폴더 Folder1-1 , Folder1-2를 탐색한다. (너비 우선 탐색)

 

C#의 방식이 마음에 든다면 여기서 끝내면 된다.

 

하지만 C, C++ 방식으로 하고 싶다면 직접 recursive하게 file을 구하도록 getRecursiveFiles를 구현해야 한다.

Directory.GetDirectories(path)로 주어진 경로에 있는 디렉토리의 목록을 구할 수 있다.

따라서 아래와 같이 함수를 만들 수 있다.

    List<string> files = new List<string>();
    void getRecursiveFiles(string path, string extension)
    {
        // 해당 경로의 파일을 먼저 찾는다.
        string[] f = Directory.GetFiles(path, extension);

        files.AddRange(f);

        // 폴더가 있다면
        string[] paths = Directory.GetDirectories(path);

        // 각 폴더에 파일을 찾는다.
        foreach(string p in paths) getRecursiveFiles(p, extension);
    }

files는 전역으로 변경하고 List<string> 타입으로 수정하였다.

그리고 Directory.GetDirectories에 아무 옵션도 주지 않으면 해당 폴더의 파일만 찾는다.

 

Start에서 실행은 아래와 같이 수정하면 된다.

    void Start()
    {
        deleteAllDirectoryFiles(RESULT_PATH);
        makeDirectory(RESULT_PATH);

        //string[] files = Directory.GetFiles(PATH, "*.*", SearchOption.AllDirectories);
        files = new List<string>();
        getRecursiveFiles(PATH, "*.*");

        int padSize = numOfDigits(files.Count);

 

추가로 bool recursive 변수를 줘서 원하는 방식대로 파일을 합칠 수 있도록 수정하였다.

 

최종 코드는 아래와 같다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;

public class RecursiveFolderMerger : MonoBehaviour
{
    const string PATH = "C:\\Users\\username\\Desktop\\MyFiles";
    const string RESULT_PATH = "C:\\Users\\username\\Desktop\\Result";

    public bool recursive;

    List<string> files = new List<string>();

    string getLastSplit(string str, char delimeter)
    {
        string[] split = str.Split(delimeter);
        int length = split.Length;

        return split[length - 1];
    }

    void deleteAllDirectoryFiles(string path)
    {
        DirectoryInfo di = new DirectoryInfo(path);

        if (di.Exists == false) return;

        FileInfo[] files = di.GetFiles();
        foreach (FileInfo file in files) file.Delete();

        DirectoryInfo[] subDirectories = di.GetDirectories();
        foreach (DirectoryInfo subDirectory in subDirectories) subDirectory.Delete();

        Directory.Delete(path);
    }

    void makeDirectory(string path)
    {
        DirectoryInfo di = new DirectoryInfo(path);
        if (di.Exists == false) di.Create();
    }

    int numOfDigits(int number)
    {
        int ret = 0;
        while(number != 0)
        {
            ret++;
            number /= 10;
        }

        return ret;
    }

    string padStart(int number, int padSize, char pad)
    {
        string strNum = number.ToString();
        string ret = "";

        for (int i = 0; i < padSize - strNum.Length; i++) ret = ret + pad;

        return ret + strNum;
    }
 
    void getRecursiveFiles(string path, string extension)
    {
        // 해당 경로의 파일을 먼저 찾는다.
        string[] f = Directory.GetFiles(path, extension);

        files.AddRange(f);

        // 폴더가 있다면
        string[] paths = Directory.GetDirectories(path);

        // 각 폴더에 파일을 찾는다.
        foreach(string p in paths) getRecursiveFiles(p, extension);
    }

    void Start()
    {
        deleteAllDirectoryFiles(RESULT_PATH);
        makeDirectory(RESULT_PATH);

        if(recursive)
        {
            files = new List<string>();
            getRecursiveFiles(PATH, "*.*");
        }
        else
        {
            string[] strfiles = Directory.GetFiles(PATH, "*.*", SearchOption.AllDirectories);
            files.AddRange(strfiles);
        }

        int padSize = numOfDigits(files.Count /* <- files.Length */);
        int fileIndex = 0;

        foreach(string file in files)
        {
            Debug.Log(file);

            string fileName = getLastSplit(file, '\\');
            string destPath = RESULT_PATH + "\\" + fileName;

            File.Copy(file, destPath, true);

            string changeName = RESULT_PATH + "\\file_" + padStart(fileIndex++, padSize, '0') + "." + getLastSplit(fileName, '.');

            File.Move(destPath, changeName);
        }
    }
}

 

Unity Plus:

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Unity Pro:

 

Unity Pro

The complete solutions for professionals to create and operate.

unity.com

 

Unity 프리미엄 학습:

 

Unity Learn

Advance your Unity skills with live sessions and over 750 hours of on-demand learning content designed for creators at every skill level.

unity.com

반응형

댓글