如何在C语言中只获取目录中的txt文件?

4
我想获取给定目录中仅为*.txt文件的名称,类似于以下内容:
#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;
}

我该如何在纯Unix C中实现此操作?

6个回答

10

首先,在Unix中没有文件扩展名的概念,因此struct dirent没有extension成员。其次,你不能使用==来比较字符串。你可以使用类似于

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

> 4 部分确保文件名 .txt 不被匹配。

(从 <stdbool.h> 中获取 bool。)


7
您可以使用 glob() 函数来实现。更多信息可以通过您喜欢的搜索引擎、Linux man 手册或这里获取。
#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;
}

2

可能性:

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);
    }
}

2
你已经快完成了,只需要检查文件名是否以.txt结尾。有一种方法是使用strcmpstrcasecmpmemcmp:
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
        }
    }
}

调用完整文件路径上的stat(2)并检查st_mode字段与S_ISxxx宏,可以验证其是否为常规文件(而不是目录或其他类型的特殊文件)。请注意,readdir返回的DIR结构体的d_type成员并不总是被支持的,因此依赖它不是一个好主意。
另外,您可以使用glob(3)函数,而不是使用opendirreaddirclosedir
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);

你缺少memcmp的最后一个参数。另外,stricmp不是标准(C或POSIX)函数,而strcasecmp是(POSIX)。但是因为建议使用stat,所以加一分。 - Fred Foo
@AdamRosenfield:好的,我也需要检查一下(类型),谢谢 :) - Katie

1
@BartFriedrich指出了glob()函数,但他没有给出它的使用示例。非常简要地(且未经测试),您可以尝试类似以下的东西。
#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()函数在细节上实际上是相当复杂的,对于更一般的文件匹配要求,我可能不会使用它,但它确实可以有效地处理您的问题。有关更多信息,请查看您的Linux机器上的man glob或查看在线手册


糟糕,没有注意到Bart Friedrich已经在他的问题中添加了一个类似的示例。 - Dale Hagglund

0
你可以编写一个endswith函数:
int endswith (const char *name, const char *suffix)

只需通过反向循环(从末尾开始)遍历后缀并检查每个字符是否相同。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接