-3

Good day,

I have a reference directory and a directory. I'd like to compare the directory basing upon my reference directory. Comparing the two folder by their contents. How will I be able to code this? Please help. Your help will be greatly appreciated. Thanks in advance. Bdw, I am using C++ in Visual Studio Express.

  • The question is very unclear. But generally, the C++ standard library doesn't yet support directory operations, but it's coming. Meanwhile, Visual C++ supports the not-yet-standard functionality, see e.g. (http://cpprocks.com/introduction-to-tr2-filesystem-library-in-vs2012/). Or you can use the [Boost filesystem library](http://www.boost.org/doc/libs/1_58_0/libs/filesystem/doc/index.htm), which may improve portability. – Cheers and hth. - Alf Jun 08 '15 at 08:13
  • on what part that you find it unclear? – sherlockholmes Jun 08 '15 at 08:19
  • First it's unclear what comparing means. You can ask an intelligent entity to compare two things, and he/she/it will look and tell you the most significant differences observed. But a computer program does something very much more specific, which in this case is what? Secondly, regarding "how", I suspect that even when the "what" is clear there will be zillions possible ways to do it, and none of them very C++-specific. My first comment was to direct you to probably relevant (upcoming) standard library functionality. After all, you will certainly require directory information. – Cheers and hth. - Alf Jun 08 '15 at 08:46

1 Answers1

0

For C++/CLI:

We need #include <vcclr.h> to convert C++ strings and CLI strings. Change to the following:

#include <vector> 
#include <string> 
#include <windows.h>

int get_files_and_folders(std::wstring dir, std::vector<std::wstring> &fullpath, std::vector<int> &filesize)
{
    if (!dir.size()) return 0;
    if (dir[dir.size() - 1] != '\\') dir += L"\\";

    WIN32_FIND_DATA find = { 0 };
    std::wstring wildcard = dir + L"*";
    HANDLE hfind = FindFirstFile(wildcard.c_str(), &find);
    if (hfind == INVALID_HANDLE_VALUE)
        return 0;

    do
    {
        std::wstring filename = find.cFileName;
        if (filename == L"." || filename == L"..")
            continue;
        std::wstring path = dir + filename;
        fullpath.push_back(path);
        filesize.push_back(find.nFileSizeLow);

        if (find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
            get_files_and_folders(path, fullpath, filesize);

    } while (FindNextFile(hfind, &find));

    FindClose(hfind);
    return 1;
}

int compare_folders(std::wstring &dir1, std::wstring &dir2, std::wstring &result)
{
    std::vector<int> filesize1, filesize2;
    std::vector<std::wstring> path1, path2;

    if (!get_files_and_folders(dir1, path1, filesize1)) return 0;
    if (!get_files_and_folders(dir2, path2, filesize2)) return 0;

    //test
    for (unsigned i = 0; i < path1.size(); i++)
    {
        System::String^ s = gcnew System::String(path1[i].c_str());
        System::Diagnostics::Trace::WriteLine(s);
    }

    if (path1.size() != path2.size())
    {
        result += L"file + folder count doesn't match\n";
        return 0;
    }

    for (unsigned i = 0; i < path1.size(); i++)
    {
        std::wstring filename1 = path1[i];
        std::wstring filename2 = path2[i];

        filename1.erase(0, dir1.size() + 1);
        filename2.erase(0, dir2.size() + 1);
        if (filename1 != filename2)
        {
            result += L"filename doesn't match\n";
            return 0;
        }

        if (filesize1[i] != filesize2[i])
        {
            result += L"filesize doesn't match\n";
            return 0;
        }

        //todo:
        //open file by fullpath name and compare each bit by bit...?
    }

    result = L"match found\n";
    return 1;
}

Now you can call the function from CLI

//In C++/CLI form: 
#include <vcclr.h>
String^ str1 = L"c:\\test1";
String^ str2 = L"c:\\test2";

//convert strings from CLI to C++
pin_ptr<const wchar_t> dir1 = PtrToStringChars(str1);
pin_ptr<const wchar_t> dir2 = PtrToStringChars(str2);
std::wstring result;

compare_folders(std::wstring(dir1), std::wstring(dir2), result);

//convert strings from C++ to CLI
System::String^ str = gcnew System::String(result.c_str());
MessageBox::Show(str);

ps, in an earlier example I included using namespace std; but that should not be there in Forms.

Barmak Shemirani
  • 30,904
  • 6
  • 40
  • 77
  • Why not code things at the C++ level instead of the C level, since the question is tagged C++. Also, note that using the `char` based API functions, as above, will in many cases give you incorrect operation, since those use Windows ANSI encoding which is unable to represent all file and directory names. Finally, it would be a good idea to use the C++ filesystem functionality instead of the Windows API. – Cheers and hth. - Alf Jun 08 '15 at 08:48
  • Just a tip: **don't** add a `pause` at the end of the program. It's very annoying when you run the program from the command line. And it's not needed when you run the program from e.g. Visual Studio (e.g. via `Ctrl+F5`). In short it has no positive gain, but it does have a number drawbacks. So it's very dumb, an anti-pattern, an evil meme that should not be propagated, especially not to beginners. – Cheers and hth. - Alf Jun 08 '15 at 08:53
  • @Cheersandhth.-Alf I am using `std::vectors` and `std::string`. In other place WinApi has C-style function, I don't want to force C++ in there for no reason. Also I need `system("pause")` for debugging from VS IDE, otherwise console closes immediately. If somebody else is doing something else they can remove the pause. – Barmak Shemirani Jun 08 '15 at 09:05
  • Re `system("pause")`, for debugging just place a breakpoint on the final right brace of `main`. It's really an anti-pattern, an evil meme. I didn't mention that it's non-portable since this is a Windows program, but as a general anti-pattern there's that aspect also. It's thoroughly all bad, nothing good. Just Don't Teach It™ (you can just remove such before posting code to SO). – Cheers and hth. - Alf Jun 08 '15 at 09:17
  • @Cheersandhth.-Alf, okay, I removed it. – Barmak Shemirani Jun 08 '15 at 09:25
  • Thanks! Okay, now, this may sound weird, but I think a good way to thank you is to teach you something. So. For functionality. If you change the hardcoded path to "." (and remove the result fixups accordingly), then should list contents of current directory, yes? But in the command interpreter, in the directory of te executable, try `mklink /j oops "%cd%"`. Then run the program. This exposes a problem with the code, where it yields an incorrect result. ;-) – Cheers and hth. - Alf Jun 08 '15 at 09:48
  • I have an activity wherein I am required to compare the contents of two directory and will warn the user if the directory being checked is not following the reference directory interms of content. I am required to program it in C++ in Visual Studio, I already man a UI and stuff. But I wasn't able to compare two directories. I can get the path of the reference directory and the directory to be checked. But comparing the files inside it is really difficult for me. :( Please help – sherlockholmes Jun 09 '15 at 00:31
  • Oh, uhm. Sorry. The wildcard variable in findfilefirst displays an error. Also, the "." and ".." inside the if statement too. And this line too string path = dir + (string)find.cFileName; – sherlockholmes Jun 09 '15 at 04:40
  • Error Message I got: IntelliSense: argument of type "const char *" is incompatible with parameter of type "LPCWSTR" – sherlockholmes Jun 09 '15 at 05:16
  • >HANDLE hfind = FindFirstFile(wildcard.c_str(), &find); >string path = dir + find.cFileName; >if (lstrcmp(find.cFileName, ".") == 0 || lstrcmp(find.cFileName, "..") == 0) these are the codes that have red lines under them. – sherlockholmes Jun 09 '15 at 05:18
  • Okay, I have updated it. And there error becomes error C3867: 'System::String::ToString': function call missing argument list; use '&System::String::ToString' to create a pointer to member error AND C2872: 'IServiceProvider' : ambiguous symbol – sherlockholmes Jun 09 '15 at 05:45
  • Unfortunately we are talking about different things. That looks like C++/CLI. It's different than C++. What platform you are targeting? Are you making a program for Windows, or Windows Tablet, or Windows Phone...? I am targeting "C++ Windows desktop applications". When you create a new project, you can choose File -> NewProject -> Win32 -> Win32 Console Application (command line prompt), or "Windows Project" which is actual GUI windows. The code I put in there is for Win32 console or GUI applications. – Barmak Shemirani Jun 09 '15 at 06:38
  • I am using Visual Studio, I am using windows form and I am creating a Windows Desktop App. – sherlockholmes Jun 09 '15 at 06:54
  • I updated the answer again. In future you should clarify that you are using Windows Forms, or CLR/CLI whatever it's called. Most C++ programmers don't even know what this is. – Barmak Shemirani Jun 09 '15 at 19:19
  • Ok. Thanks. And sorry. – sherlockholmes Jun 10 '15 at 02:10
  • If you are still having problems you can ask a new question, add `.Net` tag, or `winform` tag, those guys will know this better. – Barmak Shemirani Jun 10 '15 at 02:16