1

I'm using the opendir and readdir functions to search for file names containing .txt in the given directory.

Is there any way I can test a certain extension via a function without using a loop? (currently I have to loop through de-> d_filename to check but they are quite complicated, in addition I tried de->d_type however it did not return the extension)

In addition, this function is returning the name of the file name, my desired result is to get the path name from the beginning, is there a function return wchar_t* similar to de->d_fullfilepath?

This is all I have :

DIR* dr = opendir(lpszFolder);
vector<const wchar_t*> names;  //get list file with extension .txt then push to this vector

if (dr == NULL)  // opendir returns NULL if couldn't open directory 
{
    printf("Could not open current directory");
    return {};
}

// Refer http://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html 
// for readdir() 
while ((de = readdir(dr)) != NULL)
{
    if (de->d_type ... 'txt') // function get just .txt file.
    {
        wchar_t* pwc =new wchar_t(lpszFolder);       //initialize new instance file path
        const size_t cSize = de->d_namlen + 1;       //get file len
        mbstowcs(pwc, de->d_name, cSize);            //combine thisfilepath + extension
        names.push_back(pwc);
    }
}
Dang D. Khanh
  • 1,440
  • 6
  • 13
  • 1
    `new wchar_t(lpszFolder)` creates a *single* character, and initializes it to the value `lpszFolder`. Is there a reason you don't use `std::wstring`? – Some programmer dude Apr 16 '20 at 20:17
  • 2
    Just because the directory API uses character arrays and pointers doesn't mean you have to follow suit. As soon as you get the name, copy it to a `std::wstring` and be done with the awful array or character pointer. – PaulMcKenzie Apr 16 '20 at 20:19
  • thanks Mr.Someprogrammerdude and Mr.PaulMcKenzie , I tried and it worked perfectly with wstring. – Dang D. Khanh Apr 16 '20 at 22:00
  • 1
    A better option would be to use the classes in the [`` library](https://en.cppreference.com/w/cpp/filesystem) and let them deal with these details for you. In this case, have a look at [`std::filesystem::directory_iterator`](https://en.cppreference.com/w/cpp/filesystem/directory_iterator). – Remy Lebeau Apr 16 '20 at 23:17
  • Hi Mr. @Remy, I have seen filesystem before but I see it is different from the format I need: wchar_t *: ` C:\\Users\\MYFOLDER\\Downloads\\ ` so I ignored it :(((. now I was able. thanks for the suggestion! – Dang D. Khanh Apr 17 '20 at 00:19

2 Answers2

3

In modern C++ you should use algorithms from the std::algorithm library to avoid loops. These algorithms prevent many possible problems from the wrong usage of loops, mostly out of bounds problems.

And, C++ can deal with "wide strings" with the base data type wchar_t. You can simply use std::wstring instead of std::string.

Any you should not and never use plain C-Style arrays or pointers to char or wchar_t. These are that error prone that they should really not be used.

Even if you have legacy code with "old" "char*"-strings, put them into a std::string and use those in the future.

Next: You MUST NOT use raw pointers for owned memory. You should try to avoid pointers in general and use smart pointers instead. And you should not use new in C++. There is nearly no need for it any longer. Use containers from the STL.

Now back to your original question:

How to check char array contain any char without loop in C++?

Yes, by using std::algorithmsand iterators

Is there any way I can test a certain extension via a function without using a loop?

Yes, ths std::filesystem will help you. It has all the functionality you need and is superior to all handcraftedt solutions. It can especially also deal with wchar_t and wide stringsstd::wstring

In the following code, I generated an example function that returns a std::vector filled with all fule file paths in a specified directory with a given string.

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

// Name space alias for saving typing work
namespace fs = std::filesystem;

// A function, that gets a path to a director as wstring and returns all file paths as wstring with a given extension
std::vector<std::wstring> getFilesWithCertainExtension(const std::wstring& dirPath, const std::wstring& extension = L".txt") {

    // Put the wstring with path to the the directory in a generic path variable
     fs::path startPath{ dirPath };

    // Here we sill store all directory entries having a given extension
    std::vector<fs::directory_entry> filesInDirectory{};

    // Go thorugh the directory and copy all directory entries with a given extension int our vector
    std::copy_if(fs::directory_iterator(startPath), {}, std::back_inserter(filesInDirectory),
        [&](const fs::directory_entry& de) { return de.path().extension().wstring() == extension; });

    // The result of this function should be a vector of wstrings
    std::vector<std::wstring> result(filesInDirectory.size());

    // Convert directory entries to wstrings
    std::transform(filesInDirectory.begin(), filesInDirectory.end(), result.begin(),
        [](const fs::directory_entry& de) { return de.path().wstring(); });

    return result;
}

int main() {

    // Read all files from c:\\temp with the default extension ".txt"
    std::vector<std::wstring> files = getFilesWithCertainExtension(L"c:\\temp");

    // Show full paths to user
    for (const std::wstring& ws : files) std::wcout << ws << L"\n";

    return 0;
}

This is one of many possible solutions. This could even be optimized, if I would understand your requirements better.

I would explain the function in more detail. But, becuase anyway nobody will read this, I save the time.

A M
  • 14,694
  • 5
  • 19
  • 44
3

Best Libc function to search reversely

You might consider strrchr

Locate last occurrence of character in string Returns a pointer to the last occurrence of character in the C string str.
The terminating null-character is considered part of the C string. Therefore, it can also be located to retrieve a pointer to the end of a string.


Sample Program to find files with specific file extension

#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <string>
#include <vector>

using namespace std;

const char *get_filename_ext(const char *filename) {
    const char *dot = strrchr(filename, '.');
    return (!dot || dot == filename) ? "" : dot + 1;
}

int main(int ac, char **av) {
    if (ac != 2)
        return 1;
    const char *lookup = (ac==3) ? av[2] : "txt";

    const char *lpszFolder = av[1];
    DIR* dr = opendir(lpszFolder);
    vector<const wchar_t*> names;  //get list file with extension .txt then push     to this vector

    if (dr == NULL)  // opendir returns NULL if couldn't open directory 
    {
        printf("Could not open current directory");
        return (1);
    }
    struct dirent *ent;
    uint32_t len = sizeof(((dirent*)0)->d_name);
    char ext[len];
    while ((ent = readdir (dr)) != NULL) {
        (void)ext;
        strncpy(ext, get_filename_ext(ent->d_name), len-1);
        if (!strcmp(lookup, ext))
            names.push_back(reinterpret_cast < wchar_t*>(ent->d_name));
    }

    closedir(dr);

    for (auto name : names)
        printf("%s", (char *)name);
    return 0;
}

Main Usage

Test with:

g++ a.cpp && ./a.out myfolder

will look for all files with ".txt" extensions

Or if you want a specific extension like ☠ :

g++ a.cpp && ./a.out myfolder ☠ 
Antonin GAVREL
  • 9,682
  • 8
  • 54
  • 81
  • Wow. Good C++ code. Now I understand why this has been upvoted. Usage of: ````using namespace std;````, ````(void)ext;````, ````reinterpret_cast < wchar_t*>(ent->d_name)````. ````string.h```` and string functions, ````NULL````. Plain C arrays with magic number size. Really very impressive. Somehow missing to answer the part "without a loop in C++" from the question. But OK. And, unfortunately I cannot test it on my machine, because it uses none C++ portable language elements. But I trust that this really good C++ code. Thank you for this good answer. People can learn a lot from this. – A M Apr 17 '20 at 07:59