Tuesday, April 28, 2020

C++ stack implementation to iterate over all files and sub-directories recursively with wildcard search pattern

Here's an actual working C++ implementation of iterating over every file/directory using a file wildcard characters [*?] in a file search pattern or file filter using a stack implementation and Unicode compliant to handle multiple languages.

No change required in default stack size in Visual Studio 2010 under Linker->System->Stack Reserve Size and Stack Commit Size, but if this fails to compile, then change these limits.


#include <windows.h>
#include <string>
#include <vector>
#include <stack>
#include <iostream>
#include <atlconv.h>
#include <io.h>
using namespace std;

//---------------------------------------------------------------------------------
//Mon 27-Apr-20 1:57pm  - working stack implementation of sub-dir transversal
//https://metadataconsulting.blogspot.com/2020/04/C-stack-implementation-to-iterate-over-all-files-and-sub-directories-recursively-with-wildcard-search-pattern.html
//---------------------------------------------------------------------------------
bool ListFiles(std::wstring path, std::wstring mask, std::vector<std::wstring>& files) {
    
    //HANDLE hFind = INVALID_HANDLE_VALUE;
    long h; 
    errno_t err;
    _wfinddata_t ffd;
    std::wstring spec;
    std::wstring pathtemp; 
    std::stack<std::wstring> directories;
    std::stack<std::wstring> transversal;

    if (path.length() > 0)
    {
        std::wstring::iterator it = path.end() - 1;
        if (*it == '\\')
        {
             path.erase(it);
        }
    }

    directories.push(path);
    transversal.push(path); 
    files.clear();
    
    //Enumerate all directories first, then all files //Tue 28-Apr-20 4:46pm  - 
    while (!transversal.empty()) {
        
        path = transversal.top();
        spec = path + L"\\*"; 
        transversal.pop();
        
        h = _wfindfirst(spec.c_str(), &ffd);
    
        if (h > 0) {
    
            do {
                if (wcscmp(ffd.name, L".") != 0 && 
                    wcscmp(ffd.name, L"..") != 0) {
                    pathtemp = path + L"\\" + ffd.name; 
                    if (ffd.attrib & FILE_ATTRIBUTE_DIRECTORY) {
                        std::wcout << "searched dir: " << pathtemp + L"\\" << " \n"; 
                        directories.push(pathtemp);
                        transversal.push(pathtemp);
                    }
                }
            } while (_wfindnext(h, &ffd) == 0);

            if (GetLastError() != ERROR_NO_MORE_FILES) {
                _findclose(h);
                return false;
            }

            _findclose(h);
            h = -1; 
        }
    }

    while (!directories.empty()) {
        
        path = directories.top();
        spec = path + L"\\" + mask;
        directories.pop();
    
        h = _wfindfirst(spec.c_str(), &ffd);
        //_get_errno( &err );

        //shortcuts recursive sub-directories transversal, do not use
        /*if (err==EINVAL) {
            std::wcout << "ERROR: Invalid path encontered." << spec << std::endl; 
            return false;
        }
        else if (err==ENOENT) {
            std::wcout << "No results for file pattern " << mask << std::endl;  
            return false;
        }*/
            
        if (h > 0) {
            //std::wcout << "ERROR: " << spec << " invalid path encontered.\n"; 
            //no files found
            //return false;
        

            do {
                if (wcscmp(ffd.name, L".") != 0 && 
                    wcscmp(ffd.name, L"..") != 0) {
                    pathtemp = path + L"\\" + ffd.name; 
                    if (!(ffd.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
                        files.push_back(pathtemp);
                    }
                }
            } while (_wfindnext(h, &ffd) == 0);

            if (GetLastError() != ERROR_NO_MORE_FILES) {
                _findclose(h);
                return false;
            }
        
        }
        _findclose(h);
        //h = INVALID_HANDLE_VALUE;
        h = -1;
        
    }

    return true;
}

int main(int argc, char* argv[])
{
    vector<wstring> files;

    int cnt = 0; 

    if (ListFiles(L"C:\\Windows\\Temp", L"*.log", files)) {
        for (vector<wstring>::iterator it = files.begin(); 
             it != files.end(); 
             ++it) {
            wcout  << ++cnt << ". " << it->c_str() << endl;
        }
    }
    return 0;
}

Sunday, April 26, 2020

Consuming Unicode filenames from the C/C++ command line


#define _UNICODE
#include <windows.h>
#include <cwchar>
#include <cstdio>

using namespace std;

int main() {

    int argc;
    wchar_t** argv = CommandLineToArgvW( GetCommandLineW(), &argc); //consumer Unicode

    if (argc != 3) {
        wprintf(L"usage: this [file] [text]\n");
        return 1;
    }

    FILE* out = _wfopen( argv[1], L"wb");

    if (!out) {
        return 1;
    }

    fwprintf(out, L"%c", 0xFEFF);
    fwprintf(out, L"%s", argv[2]);
    fclose(out);

}

Saturday, April 25, 2020

How to format double as integer and not use scientific notation


Here's how to format double as integer and not use scientific notation. See last line in code example below.

/******************************************************************************

                              Online C++ Compiler.
               Code, Compile, Run and Debug C++ program online.
Write your code in this editor and press "Run" button to compile and execute it.

*******************************************************************************/
// setprecision example
#include <iostream>     // std::cout, std::fixed
#include <iomanip>      // std::setprecision
#include <string>
int main () {
  double f =79324623498343.12323;
  std::cout << std::setprecision(5) << f << '\n';
  std::cout << std::setprecision(9) << f << '\n';
  std::cout << std::fixed;
  std::cout << std::setprecision(5) << f << '\n';
  std::cout << std::fixed << std::setprecision(0) << f << '\n';
  return 0;
}