How can I get only txt files from directory in c?

2019-06-23 23:36发布

问题:

I would like to get names of only *.txt files in given directory, sth like this:

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>

int main(int argc, char **argv)
{
    char *dirFilename = "dir";

    DIR *directory = NULL;

    directory = opendir (dirFilename);
    if(directory == NULL)
        return -1;

    struct dirent *ent;

     while ((ent = readdir (directory)) != NULL)
     {
         if(ent->d_name.extension == "txt")
            printf ("%s\n", ent->d_name);
     }

    if(closedir(directory) < 0)
        return -1;

    return 0;
}

How can I do this in pure unixs c?

回答1:

Firstly, Unix has no notion of file extensions, so there's no extension member on struct dirent. Second, you can't compare strings with ==. You can use something like

bool has_txt_extension(char const *name)
{
    size_t len = strlen(name);
    return len > 4 && strcmp(name + len - 4, ".txt") == 0;
}

The > 4 part ensures that the filename .txt is not matched.

(Obtain bool from <stdbool.h>.)



回答2:

You can use the glob() function call for that. More info using your favourite search engine, Linux man pages, or here.

#include <glob.h>
#include <stdio.h>

int main(int argc, char **argv) {
  const char *pattern = "./*.txt";
  glob_t pglob; 

  glob(pattern, GLOB_ERR, NULL, &pglob);      

  printf("Found %d matches\n", pglob.gl_pathc);
  printf("First match: %s\n", pglob.gl_pathv[0]);

  globfree(&pglob);


  return 0;
}


回答3:

Possibility:

while ((ent = readdir (directory)) != NULL)
{
    const size_t len = strlen(ent->d_name);
    if (len > 4                     &&
        ent->d_name[len - 4] == '.' &&
        ent->d_name[len - 3] == 't' &&
        ent->d_name[len - 2] == 'x' &&
        ent->d_name[len - 1] == 't')
    {
        printf ("%s\n", ent->d_name);
    }
}


回答4:

You're almost there, you just need to check if the filename ends with .txt. One way to do that is to use strcmp, strcasecmp, or memcmp:

while ((ent = readdir (directory)) != NULL)
{
    int len = strlen(ent->d_name);
    if(len > 4 && memcmp(ent->d_name + len - 4, ".txt", 4) == 0)  // only checks lowercase
    {
        // It's a .txt file - now check that it's a regular file
        char filename[PATH_MAX];
        snprintf(filename, sizeof(filename), "%s/%s", dirFilename, ent->d_name);
        struct stat st;
        if(stat(filename, &st) == 0 && S_ISREG(st.st_mode))
        {
            // It's a regular file - process it
        }
    }
}

It's a good idea to verify that it's a regular file (and not a directory or other type of special file) by calling stat(2) on the full file path and checking the st_mode field with the S_ISxxx macros. Note that the d_type member of the DIR struct returned by readdir isn't always supported, so it's not a good idea to rely on it.

Alternatively, instead of using opendir, readdir, and closedir, you can use the glob(3) function:

glob_t globbuf;
if(glob("/path/to/dir/*.txt", 0, NULL, &globbuf) == 0)
{
  int i;
  for(i = 0; i < globbuf.gl_pathc; i++)
    process_filename(globbuf.gl_pathv[i]);
}
globfree(&globbuf);


回答5:

@BartFriedrich has points out the glob() function, however he didn't give an example of it's use. Very briefly (and wholly untested) you might try something like this

#include <glob.h>
#include <stdio.h>

void glob_example() {
    glob_t g;
    int i;
    glob("*.txt", 0, NULL, &g);
    for (i = 0; i < g.gl_pathc) 
        printf("matched: %s\n", g.pathv[i]);
    globfree(&g)
}

glob() is actually a fairly complicated function in detail, and for more general file matching requirements I probably wouldn't use it, but it does handle your problem effectively. For more information, check out man glob on your linux machine or look at the man page online.



回答6:

You could write a endswith function:

int endswith (const char *name, const char *suffix)

Just do a reverse-loop (start from the end) throught the suffix and check if each char is the same.