More effective file check, then rename code?

This code works relatively well for my purposes, however I had in mind some kind for loop that didn't have a limit. I've tried both 'for' and 'while' loops, however i don't have enough knowledge on how they worked.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <fstream>
#include <conio.h>
using namespace std;

int main(){
    if (fstream("C:\\Users\\Robin\\Videos\\OBS Capture.mp4")){
        if (fstream("C:\\Users\\Robin\\Videos\\OBS Capture1.mp4")){
            if (fstream("C:\\Users\\Robin\\Videos\\OBS Capture2.mp4")){
                if (fstream("C:\\Users\\Robin\\Videos\\OBS Capture3.mp4")){
                    if (fstream("C:\\Users\\Robin\\Videos\\OBS Capture4.mp4")){
                        if (fstream("C:\\Users\\Robin\\Videos\\OBS Capture5.mp4")){
                            cout << "YOU NEED TO DELETE SOME FILES NOW!!!" << endl;
                            getch();
                        }
                        else {rename("C:\\Users\\Robin\\Videos\\OBS Capture.mp4","C:\\Users\\Robin\\Videos\\OBS Capture5.mp4");
                        cout << "OBS Capture5.mp4 created." << endl;}
                    }
                    else {rename("C:\\Users\\Robin\\Videos\\OBS Capture.mp4","C:\\Users\\Robin\\Videos\\OBS Capture4.mp4");
                    cout << "OBS Capture4.mp4 created." << endl;}
                }
                else {rename("C:\\Users\\Robin\\Videos\\OBS Capture.mp4","C:\\Users\\Robin\\Videos\\OBS Capture3.mp4");
                cout << "OBS Capture3.mp4 created." << endl;}
            }
            else {rename("C:\\Users\\Robin\\Videos\\OBS Capture.mp4","C:\\Users\\Robin\\Videos\\OBS Capture2.mp4");
            cout << "OBS Capture2.mp4 created." << endl;}
        }
        else {rename("C:\\Users\\Robin\\Videos\\OBS Capture.mp4","C:\\Users\\Robin\\Videos\\OBS Capture1.mp4");
        cout << "OBS Capture1.mp4 created." << endl;}
    }
}
Last edited on
If your library supports the latest TS for filesystem (as the latest version of VC's does):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <cctype>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>

namespace fs = std::tr2::sys;

fs::path next_file_name(fs::path p)
{
    auto file_name = p.stem().string();

    if (!std::isdigit(file_name.back()))
        file_name += '1';
    else if (file_name.back() == '9')
        file_name.replace(file_name.size() - 1, 1, "10");
    else
        ++file_name.back();

    file_name += p.extension().string();

    if (p.has_parent_path())
        p.replace_filename(file_name);
    else
        p = fs::path(file_name);

    return p;
}

void test()
{
    fs::path path = "C:/Users/Robin/Videos/OBS Capture.mp4";

    for (std::size_t i = 0; i < 10; ++i)
        std::cout << (path = next_file_name(path)) << '\n';
    std::cout << '\n';

    path = "OBS Capture.mp4";

    for (std::size_t i = 0; i < 10; ++i)
        std::cout << (path = next_file_name(path)) << '\n';
    std::cout << '\n';
}

int main()
{
    // test();

    fs::path path = "C:/Users/Robin/Videos/OBS Capture.mp4";

    if (fs::exists(path))
    {
        fs::path p = path;
        while (fs::exists(p = next_file_name(p)))
            ;

        fs::rename(path, p);
    }

    std::ofstream out(path);
    if (out.is_open())
    {
        // ...
    }
}


Otherwise, you could use Boost's version with minor modification.
I appreciate the reply, but I felt that having to install 'filesystem' on every device that was to use it kind of defeated the purpose. I did, however, have an epiphany while reading your code for a limitless and effective way to do it. "What if I could convert the Int in the For statement to a String and add it to the filename":

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <fstream>
#include <iostream>
#include <sstream>
using namespace std;

int main(){
    string ogStr("C:\\Users\\Robin\\Videos\\OBS Capture.mp4");
    string ogStr1;
    const char * ogC = ogStr.c_str();
    ifstream ogFile (ogStr.c_str());

    for(int nr = 0;getline(ogFile,ogStr1);nr +=1){
        ostringstream convert;
        convert << nr;
        string Result (convert.str());
        string str("C:\\Users\\Robin\\Videos\\OBS Capture"+Result+".mp4");
        string str1;
        const char * c = str.c_str();
        ifstream file (str.c_str());

        if(!getline(file,str1)){
            ogFile.close();
            file.close();
            rename(ogC,c);
        }
    }
}
Last edited on
> "What if I could convert the Int in the For statement to a String and add it to the filename":

Yes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <fstream>
#include <string>
#include <cstdio>

bool file_exists( std::string file_name ) { return std::ifstream{file_name}.is_open() ; }

std::string next_available_file_name( std::string base_name, std::string ext )
{
    for( int i = 1 ; ; ++i )
    {
        std::string candidate = base_name + std::to_string(i) + ext ;
        if( !file_exists(candidate) ) return candidate ;
    }
}

int main()
{
    const std::string base_name = "C:\\Users\\Robin\\Videos\\OBS Capture" ;
    const std::string ext = ".mp4" ;

    const std::string file_name = base_name + ext ;
    if( file_exists(file_name) )
        std::rename( file_name.c_str(), next_available_file_name( base_name, ext ).c_str() ) ;
}
Oh, i didn't mean that as a question to be answered since my code works, however I appreciate the reply and I will study your code thoroughly.
Last edited on
I put the code into my compiler to test it and I got a bunch of errors that I can't seen to fix.

6| warning: extended initializer lists only available with -std=c++11 or -std=gnu++11 [enabled by default]

6| error: no matching function for call to 'std::basic_ifstream<char>::basic_ifstream(<brace-enclosed initializer list>)'|

12| error: 'to_string' is not a member of 'std'|

6| warning: control reaches end of non-void function [-Wreturn-type]|
> I got a bunch of errors that I can't seen to fix

That snippet uses C++11 features.

This should work with legacy C++:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
#include <fstream>
#include <string>
#include <cstdio>
#include <sstream>

bool file_exists( std::string file_name ) { return std::ifstream( file_name.c_str() ).is_open() ; }

std::string to_string( int v )
{
    std::ostringstream stm ;
    stm << v ;
    return stm.str() ;
}

std::string next_available_file_name( std::string base_name, std::string ext )
{
    for( int i = 1 ; ; ++i )
    {
        std::string candidate = base_name + ::to_string(i) + ext ;
        if( !file_exists( candidate.c_str() ) ) return candidate ;
    }
}

int main()
{
    const std::string base_name = "C:\\Users\\Robin\\Videos\\OBS Capture" ;
    const std::string ext = ".mp4" ;

    const std::string file_name = base_name + ext ;
    if( file_exists(file_name) )
        std::rename( file_name.c_str(), next_available_file_name( base_name, ext ).c_str() ) ;
}
Yes, this worked perfectly. Thank-you so much for your help. I'm just wondering what benefits/detriments there are to using your code over mine and vice versa.
In general, favour writing small functions, each function doing one thing, and doing it well.

With small functions, the code becomes easier to understand, easier to test and debug (each function can be tested/debugged in isolation), and we get units of reusable functionality.

5. Give one entity one cohesive responsibility.

Summary
Focus on one thing at a time: Prefer to give each entity (variable, class, function, namespace, module, library) one well-defined responsibility. ...

Discussion
... An entity with several disparate purposes is generally disproportionately harder to use, because it carries more than the sum of the intellectual overhead, complexity, and bugs of its parts. ... Prefer brief single-purpose functions ...

Prefer to build higher-level abstractions from smaller lower-level abstractions. Avoid collecting several low-level abstractions into a larger low-level conglomerate. Implementing a complex behavior out of several simple ones is easier than the reverse.


20. Avoid long functions. Avoid deep nesting.

Summary
Short is better than long, flat is better than deep: Excessively long functions and nested code blocks are often caused by failing to give one function one cohesive responsibility (see Item 5), and both are usually solved by better refactoring.

Discussion
Every function should be a coherent unit of work bearing a suggestive name... When a function instead tries to merge such small conceptual elements inside a long function body, it ends up doing too much.

Excessive straight-line function length and excessive block nesting depth ... are twin culprits that make functions more difficult to understand and maintain, and often needlessly so. Each level of nesting adds intellectual overhead when reading code because you need to maintain a mental stack (e.g., enter conditional, enter loop, enter try, enter conditional, ...). Have you ever found a closing brace in someone's code and wondered which of the many fors, whiles, or ifs it matched? Prefer better functional decomposition to help avoid forcing readers to keep as much context in mind at a time.

Exercise common sense and reasonableness: Limit the length and depth of your functions.

- Alexandrescu and Sutter in 'C++ Coding Standards: 101 Rules, Guidelines, and Best Practices'
Thank you very much. I will keep this in mind in my future programming endeavours.
I appreciate the reply, but I felt that having to install 'filesystem' on every device that was to use it kind of defeated the purpose.

Do you need to install a compiler for every device that uses your programs?
Topic archived. No new replies allowed.