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?
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>
.)
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;
}
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);
}
}
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);
@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.
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.