I want to make a program that reads the files in a given directory, creates folders for the extensions of the files, then moves the files into the new folders.
I am fairly new to C as in all I've done previously was play with small things like methods and classes, so I don't really know what went wrong.
The first time the program is ran on a folder, it properly creates the required folders, but doesn't move the files.
The second time it runs, nothing changes, but it makes a set of nested folders of the original directory of the current folder. What I mean is, if the folder directory is A:/b/c/d/
, it begins to create folders of b
up. I have tried on another folder to test it, and it did properly make the folders, but didn't move the files.
I added comments just in case the code is hard to read. I purposely made the logic for making folders and moving files into separate methods for easier editing. I should note that even though the files are not moved, the rename
function does return 0, stating that it was moved.
As for the libraries, I plan to clean them up after everything somewhat properly works.
/*
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;
}
CodePudding user response:
The for
loops in extension()
, noRepeats()
, makeFolders()
and moveFiles()
are all going out of bounds of their respective input arrays. Arrays are 0-indexed, their valid indexes are [0..size-1]
only. You need to use < size
instead of <= size
while looping through the arrays.
For that matter, the caller should be passing in the necessary size
as an input parameter, the functions should not be calculating that. In main()
, you know the exact sizes of the files[]
array (arSpot
) and the exten[]
array (y
), so pass those values to extension()
, noRepeats()
, makeFolders()
and moveFiles()
.
Even better, don't use raw arrays at all. Use std::vector
instead, which is a dynamic array that knows its own size. While you are at it, you can replace your entire noRepeats()
function with the std::set
, which maintains a sorted list of unique values.
Also, don't use long
for string indexes, you should be using string::size_type
or std::size_t
instead (or even auto
, let the compiler decide).
Also, what is if (position2 >= 44073709551615)
looking for exactly? You are doing this right after a call to string::find_last_of()
, so you should be comparing the result against string::npos
instead.
Regarding the rest of your logic:
You are not handling folder paths that use
\
instead of/
as path delimiters.extension()
doesn't take into account the possibility of a file without an extension being in a folder with a dot in its path.main()
is not accounting for folder paths with spaces in them.
With all of that said, since you are already aware of the <filesystem>
library, you should let it do all of the hard work for you, eg:
#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;
}