Упорядочивание файлов в папке

138
02 июня 2019, 13:00

Есть вот такая задачка:

Написать программу на С++ выполняющую следующую задачу: допустим в текущей директории есть файлы a.txt (10 байт), 3.jpg(1000 байт), а также папка 1 с содержимым KISH.mp3 (4000 байт) то есть присутствуют вложенные папки с файлами

Программа должна создать директорию renamedFiles с содержимым:

  1. a.txt
  2. 3.jpg
  3. KISH.mp3

// файлы упорядочены по размеру

Есть такое решение: проверяем есть ли папка с именем renamedFiles, удаляем ее со вложенными файлами и создаем новую, копируем туда файлы попутно сохраняя их путь и размеры, в общем все просто.

Вот далее проблемка. Есть два массива с размерами файлов, один отсортированный, другой исходный. Сравнивая отсортированный массив i с исходным, находим по j-тому элементу файл и переименовываем. Но данный метод не подходит для файлов одинаковых размеров. Быть может есть другой способ упорядочить файлы в папке? Может как-то по другому идентифицировать файлы (а не по размеру)? Исходный код прилагаю ниже. С библиотекой filesystem работаю впервые, поэтому готов выслушать замечания в целом по коду.

#include <algorithm>
#include <vector>
#include <iostream>
#include <filesystem>
#include <iomanip>
constexpr char NEWFOLDER[] = "renamedFiles";
constexpr int TAB = 60;
using namespace std;
namespace fs = std::experimental::filesystem;
string getFileName(const string& s)
{
    char sep = '\\';
    size_t i = s.rfind(sep, s.length());
    if (i != string::npos)
    {
        return(s.substr(i + 1, s.length() - i));
    }
    return("");
}
int main()
{
    setlocale(LC_ALL, "Rus");
    error_code err;
    vector<uintmax_t> fsize;
    vector<uintmax_t> sort_fsize;
    vector<fs::path> path_list;
    fs::path currPath = fs::current_path();
    fs::path newPath = currPath / NEWFOLDER;
    cout << "\nCurrent directory: " << endl;
    cout << currPath << endl << endl;
    if (fs::exists(newPath))
    {
        fs::remove_all(newPath, err);
        if (err)
        {
            cout << "Error: can't remove folder: " << NEWFOLDER;
            return -1;
        }
    }
    if (!fs::create_directory(newPath, err))
    {
        cout << "Error: can't create new folder: " << NEWFOLDER;
        return -1;
    }
    cout << "Created new folder: " << NEWFOLDER << endl << endl;
    cout << "Coping files..." << endl;
    for (auto& entry : fs::recursive_directory_iterator(currPath))
    {
        if (!fs::is_directory(entry) && entry.path().parent_path() != newPath)
        {
            fs::path pathFile = newPath / entry.path().filename();
            if (fs::copy_file(entry, pathFile, fs::copy_options::skip_existing))
            {
                cout << entry << endl;
                path_list.push_back(pathFile);
                fsize.push_back(fs::file_size(pathFile));
            }
        }
    }
    sort_fsize = fsize;
    sort(sort_fsize.begin(), sort_fsize.end(), less<uintmax_t>());
    cout << "\nSorted files in folder: " << NEWFOLDER << endl;
    cout << setiosflags(ios::left) << setw(TAB) << "File:" << "Size:" << endl;
    for (uintmax_t i = 0, count = 0; i < sort_fsize.size(); ++i)
    {
        for (uintmax_t j = 0; j < fsize.size(); ++j)
        {
            if (sort_fsize[i] == fsize[j])
            {
                string newFileName = to_string(++count) + '.' + getFileName(path_list[j].string());
                fs::rename(newPath / path_list[j].filename(), newPath / newFileName, err);
                cout << setw(TAB) << newFileName << sort_fsize[i] << " bytes" << endl;
            }
        }
    }  
    return 0;
}
Answer 1

Для начала поясню, что по заданию нужно было скопировать все файлы из текущего каталога в новый с именем "renamedFiles" и приписать в именах файлов порядок, то есть пронумеровать их в порядке возрастания размера. Вот так новый каталог должен выглядеть:

В общем, решил проблему так. Как подсказали в комментариях, нужно было просто напросто хранить имена и размеры файлов в одном массиве (массив структур). Для удобства сделал конструктор.

struct file_struct {
    fs::path fpath;
    uintmax_t fsize;
    file_struct(fs::path _fpath, uintmax_t _fsize) :
        fpath(_fpath), fsize(_fsize) {}
};

Объявление в main:

vector<file_struct> list;

Чтобы заполнять вектор со структурой, необходимо каждый раз создавать эту структуру:

for (auto& entry : fs::recursive_directory_iterator(currPath))
{
    if (!fs::is_directory(entry) && entry.path().parent_path() != newPath)
    {
        fs::path pathFile = newPath / entry.path().filename();
        if (fs::copy_file(entry, pathFile, fs::copy_options::skip_existing))
        {
            file_struct currFile(pathFile, fs::file_size(pathFile)); //заполняются поля структуры
            list.push_back(currFile); //заносится в массив
            cout << entry.path() << endl;
        }
    }
}

Раз мы находимся в стандарте C++17, то для сортировки по некоторому полю структуры (размеру файла) грех не воспользоваться нововведением стандарта C++11, то есть лямбда-функцией в функции sort:

sort(list.begin(), list.end(),
    [](const file_struct& l1, const file_struct& l2) -> bool
    {
        return l1.fsize < l2.fsize; 
    });

Осталось пронумеровать имена файлов:

for (size_t i = 0; i < list.size(); ++i)
{
    string newFileName = to_string(i + 1) + '.' + getFileName(list[i].fpath.string());
    fs::rename(newPath / list[i].fpath.filename(), newPath / newFileName, err);
    cout << setw(TAB) << newFileName << list[i].fsize << " bytes" << endl;
}

Результат работы программы был приведён на изображении выше. По поводу комментариев об одинаковых файлов или файлов с одинаковыми именами, то такие файлы пропускаются если один из них уже окажется в новой директории на что указывает опция fs::copy_options::skip_existing, которая пропускает ошибки при копировании файла в строчке:

if (fs::copy_file(entry, pathFile, fs::copy_options::skip_existing))

Можно обработать данное исключение, но задание этого не требует.

Доработанный код привожу ниже. Всем спасибо за подсказки.

#include <iostream>
#include <iomanip>
#include <algorithm>
#include <vector>
#include <filesystem>

using namespace std;
namespace fs = std::filesystem;
constexpr char NEWFOLDER[] = "renamedFiles";
constexpr int TAB = 70;
struct file_struct {
    fs::path fpath;
    uintmax_t fsize;
    file_struct(fs::path _fpath, uintmax_t _fsize) :
        fpath(_fpath), fsize(_fsize) {}
};
string getFileName(const string& s)
{
    char sep = '\\';
    size_t i = s.rfind(sep, s.length());
    if (i != string::npos) {
        return(s.substr(i + 1, s.length() - i));
    }
    return("");
}
int main()
{
    setlocale(LC_ALL, "Rus");
    error_code err;
    vector<file_struct> list;
    fs::path currPath = fs::current_path();
    fs::path newPath = currPath / NEWFOLDER;
    cout << "\nCurrent directory: " << endl;
    cout << currPath << endl << endl;
    if (fs::exists(newPath))
    {
        cout << "Detected folder: " << NEWFOLDER << endl;
        fs::remove_all(newPath, err);
        if (err)
        {
            cout << "Error: can't remove folder: " << NEWFOLDER;
            return -1;
        }
        else
        {
            cout << "Folder removed!" << endl;
        }
    }
    if (!fs::create_directory(newPath, err))
    {
        cout << "Error: can't create new folder: " << NEWFOLDER;
        return -1;
    }
    cout << "Created new folder: " << NEWFOLDER << endl << endl;
    cout << "Coping files..." << endl;
    for (auto& entry : fs::recursive_directory_iterator(currPath))
    {
        if (!fs::is_directory(entry) && entry.path().parent_path() != newPath)
        {
            fs::path pathFile = newPath / entry.path().filename();
            if (fs::copy_file(entry, pathFile, fs::copy_options::skip_existing))
            {
                file_struct currFile(pathFile, fs::file_size(pathFile));
                list.push_back(currFile);
                cout << entry.path() << endl;
            }
        }
    }
    sort(list.begin(), list.end(),
        [](const file_struct& l1, const file_struct& l2) -> bool
        {
            return l1.fsize < l2.fsize; 
        });
    cout << "\nSorted files in folder: " << NEWFOLDER << endl;
    cout << setiosflags(ios::left) << setw(TAB) << "File:" << "Size:" << endl;
    for (size_t i = 0; i < list.size(); ++i)
    {
        string newFileName = to_string(i + 1) + '.' + getFileName(list[i].fpath.string());
        fs::rename(newPath / list[i].fpath.filename(), newPath / newFileName, err);
        cout << setw(TAB) << newFileName << list[i].fsize << " bytes" << endl;
    }
    return 0;
}
READ ALSO
тип float в бинарный вид C++

тип float в бинарный вид C++

Задача из универа: Для числа типа float при выводе на экран его битового представления указать знаковый бит, порядок и мантиссуЧисло вводят...

187
Не линкуется библиотека xblas

Не линкуется библиотека xblas

Собрал библиотеку по инструкции с сайта следующими командами:

144
Ошибка при использование команды fin.open() и getline

Ошибка при использование команды fin.open() и getline

Пытаюсь открыть файл, который находится на диске С, но выдает ошибки

143
jar с внешними банками в exe

jar с внешними банками в exe

У меня есть два jar файла, один лежит в lib, соответственно все работаетЕсть ли способ засунуть все это добро в один exe файл ?!

160