创建文件夹和移动文件

Creating Folders and Moving Files

我想制作一个程序来读取给定目录中的文件,为文件的扩展名创建文件夹,然后将文件移动到新文件夹中。

我对 C++ 还很陌生,因为我之前所做的只是玩弄一些小东西,比如方法和 类,所以我真的不知道哪里出了问题。

程序第一次 运行 在文件夹上时,它会正确创建所需的文件夹,但不会移动文件。

第二次运行,没有任何变化,只是在当前文件夹的原目录下做了一组嵌套文件夹。我的意思是,如果文件夹目录是A:/b/c/d/,它就开始创建b以上的文件夹。我试过在另一个文件夹上测试它,它确实正确地制作了文件夹,但没有移动文件。

我添加了注释以防代码难以阅读。我特意将制作文件夹和移动文件的逻辑分成不同的方法,以便于编辑。我应该注意到,即使文件没有移动,rename 函数也会 return 0,说明它已被移动。

至于库,我计划在一切正常后清理它们。

/*
 main.cpp
 File Creator and Sorter
 
 Made in almost 24 hours
 First personal c++ project
 
 This program takes an input from the user in the form of /Users/.../.../.../... , ... being a folder location.
 It then goes through the files in the folder, finds any extensions, creates folders for those extensions, and moves the files there.
 There is a chance for a few files to be located in the folder that don't have a file type. For those files I plan to implement a
 miscallaneos folder and move them there. Do not use this program unless you confirmed you want to sort everything in the folder.
 The reason being this will not leave any file alone and can mess up and set ups.
 

 Created by yared yohannes on 12/15/21.
*/
// libraries idk what is needed and not so needs to be cleaned up

#include <dirent.h>
#include <cstdio>
#include <fstream>
#include <iostream>

// namespaces dont mess with cause filesystem was giving problems, im new.
using namespace std;
namespace fs = filesystem;

// turns the files in the string array into an extension section, and if it is
// not an extension(from noticing the .) removes it. the reason for the removing
// of unknown files is cause create_directory has an error on weird filenames.

void extension(string files[]) {
    int size = 0;

    while (files[size] != "") {
        size++;
    }

    for (int i = 0; i <= size; i++) {
        long position = files[i].find_last_of(".");
        files[i] = files[i].substr(position + 1);

        long position2 = files[i].find_last_of("/");
        if (position2 >= 44073709551615) {
            files[i] = "";
        }
    }
}

// Removes any repeated extensions(can be used on repeating string array, just
// called it extensions cause thats what I use it for). Also realigns the values
// in the string array so that all the values are at the front to save time
// later on.

void noRepeats(string file[]) {
    int size = 0;

    while (file[size] != "") {
        size++;
    }

    for (int i = 0; i <= size; i++) {
        for (int k = i + 1; k <= size + 1; k++) {
            if (file[i] == file[k]) {
                file[k] = "";
            }
        }
    }

    for (int i = 0; i <= size; i++) {
        for (int k = i + 1; k <= size + 1; k++) {
            if (file[i] == "") {
                if (file[k] != "") {
                    file[i] = file[k];
                    file[k] = "";
                }
            }
        }
    }
}

// gets the path of the files location. Mainly did this so I can automate the
// process in a method for cleaner main code. returns path.

string getPath(string files[]) {
    string holder = files[0];
    string path = "";

    long position = holder.find_last_of("/");

    path += files[0].substr(0, position + 1);

    return path;
}

// creates folders from string array of extensions from the first 2 methods and
// uses the path method to properly create the folders;

void makeFolders(string path, string extensions[]) {
    int size = 0;

    while (extensions[size] != "") {
        size++;
    }

    for (int i = 0; i <= size; i++) {
        if (extensions[i] != "DS_Store") {
            string folderName = path;

            folderName += extensions[i];
            folderName += "/";

            fs::create_directories(folderName);
        }
    }
}

// needs to be fixed cause not all files are moved?

// moves the files in the files array of the main into the folders created using
// the extensions array.

void moveFiles(string file[], string extensions[], string path) {
    int size = 0;

    while (file[size] != "") {
        size++;
    }

    int size2 = 0;

    while (extensions[size] != "") {
        size2++;
    }

    for (int i = 0; i <= size; i++) {
        long position = file[i].find_last_of(".");
        string fileType = file[i].substr(position + 1);

        for (int k = 0; k <= size2; k++) {
            if (fileType == extensions[k]) {
                string folderName = path;
                folderName += extensions[k];
                folderName += "/";
                
                long position2 = file[i].find_last_of("/");
                folderName += file[i].substr(position2 + 1);

                const char *oldName = file[i].c_str();
                const char *newName = folderName.c_str();

                if (rename(oldName, newName) != 0) {
                    cout << file[i] << "Could not be moved." << endl;
                }
            }
        }
    }
}

// main method, requests folder location, scans files, creates extension array,
// fixes extensions, makes folders, then moves files.

int main() {
    string files[1000];
    int arSpot = 0;
    const size_t path_max = 256;
    char dirname[path_max];

    cout << "What is the name of the folder: ";

    cin >> dirname;

    DIR *d = opendir(dirname);

    if (!d) {
        cout << "ERROR: Please provide a valid directory path.\n";
    } else {
        string path = dirname;

        for (const auto &entry : fs::directory_iterator(path)) {
            files[arSpot] = entry.path();
            arSpot++;
        }
    }

    string path = getPath(files);

    string exten[1000];

    int y = 0;

    while (files[y] != "") {
        exten[y] = files[y];
        y++;
    }

    extension(exten);
    noRepeats(exten);
    makeFolders(path, exten);
    moveFiles(files, exten, path);
    cout << endl;

    return 0;
}

extension()noRepeats()makeFolders()moveFiles() 中的 for 循环都超出了各自输入数组的范围。数组是从 0 开始索引的,它们的有效索引仅为 [0..size-1]。在遍历数组时,您需要使用 < size 而不是 <= size

就此而言,调用者应该传入必要的 size 作为输入参数,函数不应该计算它。在 main() 中,您知道 files[] 数组 (arSpot) 和 exten[] 数组 (y) 的确切大小,因此将这些值传递给 extension()noRepeats()makeFolders()moveFiles().

更好的是,根本不要使用原始数组。请改用 std::vector,这是一个知道自己大小的动态数组。当您使用它时,您可以将整个 noRepeats() 函数替换为 std::set,它维护一个排序的唯一值列表。

此外,不要使用 long 作为字符串索引,您应该使用 string::size_typestd::size_t(甚至 auto,让编译器决定) .

另外,if (position2 >= 44073709551615) 到底在找什么?您在调用 string::find_last_of() 后立即执行此操作,因此您应该将结果与 string::npos 进行比较。

关于您的其余逻辑:

  • 您没有处理使用 \ 而不是 / 作为路径分隔符的文件夹路径。

  • extension() 没有考虑没有扩展名的文件位于路径中带有点的文件夹中的可能性。

  • main() 不考虑其中包含空格的文件夹路径。

综上所述,既然您已经知道 <filesystem> 库,您应该让它为您完成所有艰苦的工作,例如:

#include <iostream>
#include <filesystem>
#include <vector>
#include <string>
#include <set>

using namespace std;
namespace fs = filesystem;

string getFileType(const fs::path &file) {
    string ext = file.extension();
    if (ext.size() > 1)
        return ext.substr(1);
    return "miscellaneous";
}

void makeFolders(const fs::path &folder, const set<string> &fileTypes) {
    for (const auto &fileType : fileTypes) {
        if (fileType != "DS_Store") {
            fs::path newFolder = folder / fileType;
            error_code ec;
            fs::create_directories(newFolder, ec);
            if (ec) {
                cerr << newFolder << " Could not be created." << endl;
            }
        }
    }
}

void moveFiles(const vector<fs::path> &files, const fs::path &folder) {
    for (const auto &file : files) {
        error_code ec;
        fs::rename(file, folder / getFileType(file) / file.filename(), ec);
        if (ec) {
            cerr << file << " Could not be moved." << endl;
        }
    }
}

int main()
{
    cout << "What is the path of the folder: ";

    string dirPath;
    getline(cin, dirPath);

    error_code ec;
    fs::directory_iterator dir(dirPath, ec);        
    if (ec)
    {
        cerr << "ERROR: Invalid directory path." << endl;
    }
    else
    {
        vector<fs::path> files;
        set<string> fileTypes;

        for (const auto &entry : dir) {
            fs::path file = entry.path();
            files.push_back(file);
            fileTypes.insert(getFileType(file));
        }

        makeFolders(dirPath, fileTypes);
        moveFiles(files, dirPath);
    }

    return 0;
}