我正在尝试将目录中每个文件的名称放入一个数组中。在我打印数组本身之前,代码似乎运行良好。
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <dirent.h>
int main ()
{
char directory_name[10];
DIR *ptr;
int n, i;
n = 0;
i = 0;
//Ask user for directory name
struct dirent *directory;
printf ("Enter Directory Name:\t");
scanf ("%s", directory_name);
ptr = opendir (directory_name);
printf ("\nDirectory %s\n", directory_name);
while ((directory = readdir (ptr)) != NULL) {
if (!strcmp (directory->d_name, ".")
|| !strcmp (directory->d_name, "..")) {
} else {
n++;
}
}
rewinddir (ptr);
char *filesList[n];
//Put file names into the array
while ((directory = readdir (ptr)) != NULL) {
if (!strcmp (directory->d_name, ".")
|| !strcmp (directory->d_name, "..")) {
} else {
filesList[i] = (char *) malloc (strlen (directory->d_name) + 1);
strncpy (filesList[i], directory->d_name,
strlen (directory->d_name));
i++;
}
}
rewinddir (ptr);
for (i = 0; i <= n; i++) {
printf ("%s\n", filesList[i]);
}
closedir (ptr);
return 0;
}
filesList打印,但在打印输出结束时,它会添加另一行以及这些符号:
)──D$┴┴┴┴┴ëE╨Θï
我想从数组中删除这些符号。但是,由于我不知道它们是什么或它们来自哪里,我不知道从哪里开始。我是C的新手,所以如果这是一个初学者问题我会道歉。
我想澄清一下,程序将这些符号识别为文件名,即使目录中没有该名称的文件。索引会像文件一样增加。
答案 0 :(得分:1)
请勿在代码中使用幻数:
char directory_name[10];
如果您需要常量,请定义一个常量,或者如果已为特定应用程序提供了适当的常量,请使用该常量,例如
#ifndef PATH_MAX /* declare constants as required */
#define PATH_MAX 4096
#endif
#define NFILE 128
...
char directory_name[PATH_MAX] = "",
...
您无法绝对确定任何问题的根源,因为您未能确认任何所需的回报。除void
之外的所有函数都提供可用于表示成功或失败的返回 - 使用它们,例如
/* open and VALIDATE directory with opendir */
ptr = opendir (directory_name);
if (!ptr) {
fprintf (stderr, "error: directory open failed '%s'.\n",
directory_name);
return 1;
}
printf ("\nDirectory open '%s'\n", directory_name);
这是用户输入的绝对必须。
填充的数组索引介于0
和n-1
之间,因此您的for (i = 0; i <= n; i++)
索引通过超出VLA范围读取来调用未定义行为。使用for (i = 0; i < n; i++)
不是在你的目录上进行两次传递,(第一次计算文件数量,倒带,第二次读取文件名),最初只需要为char分配一些合理数量的指针,跟踪你的数字填写,并在达到当前限制时realloc
。以下代码就是这样做的(以及清理用户输入 - 使用fgets
代替scanf
来简化并使其更加健壮),例如。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <limits.h>
#ifndef PATH_MAX /* declare constants as required */
#define PATH_MAX 4096
#endif
#define NFILE 128
/* simple function to empty stdin.
* mandatory when taking user input with scanf
*/
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
int main (void) {
DIR *ptr = NULL;
struct dirent *directory = NULL;
char directory_name[PATH_MAX] = "",
**files = NULL; /* using pointer to pointer to char */
size_t i, n = 0, nptr = NFILE;
/* prompt for and VALIDATE input of directory_name */
for (;;) { /* loop until valid input received or user cancels */
int rtn;
printf ("Enter Directory Name: ");
rtn = scanf ("%s", directory_name);
if (rtn == 1) { /* good input, proceed */
empty_stdin();
break;
}
else if (rtn == EOF) { /* user canceled input */
fprintf (stderr, "user canceled input.\n");
return 1;
} /* handle other error */
fprintf (stderr, "error: invalid input.\n");
empty_stdin();
}
/* open and VALIDATE directory with opendir */
ptr = opendir (directory_name);
if (!ptr) {
fprintf (stderr, "error: directory open failed '%s'.\n",
directory_name);
return 1;
}
printf ("\nDirectory open '%s'\n", directory_name);
/* allocate and VALIDATE nptr pointer for filenames */
files = calloc (nptr, sizeof *files);
if (!files) {
perror ("pointer allocation failed");
return 1;
}
/* read each filename, allocate/VALIDATE storage, copy to files[n],
* check total pointer allocation, realloc as required.
*/
while ((directory = readdir (ptr)) != NULL) {
if (!strcmp (directory->d_name, ".") /* skip dot files */
|| !strcmp (directory->d_name, ".."))
continue;
/* allocate VALIDATE storage for filename */
files[n] = malloc (strlen (directory->d_name) + 1);
if (!files[n]) {
perror ("memory exhausted - filesList");
return 1;
}
strcpy (files[n++], directory->d_name); /* copy filename */
/* check if ptr limit reached, if so realloc 2x pointers */
if (n == nptr) {
void *tmp = realloc (files, nptr * 2 * sizeof *files);
if (!tmp) {
perror ("realloc failed - files");
break; /* files still points to original block */
}
files = tmp;
/* optional - zero all newly added memory */
memset (files + nptr, 0, nptr * sizeof *files);
nptr *= 2; /* increment number currently allocated */
}
}
for (i = 0; i < n; i++) { /* indexes are 0 -> n-1 */
printf ("%s\n", files[i]);
free (files[i]); /* don't forget to free memory */
}
free (files); /* free pointers */
closedir (ptr);
return 0;
}
示例使用/输出
$ ./bin/readdir_alloc
Enter Directory Name: dat
Directory open 'dat'
lastchgcol.txt
arrinpt.txt
...
内存使用/错误检查
在你编写的动态分配内存的任何代码中,你有2个职责关于任何分配的内存块:(1)总是保留一个指向起始地址的指针内存块,(2)当不再需要时,它可以释放。
对于Linux valgrind
是正常的选择。每个平台都有类似的记忆检查器。它们都很简单易用,只需通过它运行程序即可。
$ valgrind ./bin/readdir_alloc
==10547== Memcheck, a memory error detector
==10547== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==10547== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==10547== Command: ./bin/readdir_alloc
==10547==
Enter Directory Name: dat
Directory open 'dat'
lastchgcol.txt
arrinpt.txt
...
==10547==
==10547== HEAP SUMMARY:
==10547== in use at exit: 0 bytes in 0 blocks
==10547== total heap usage: 248 allocs, 248 frees, 38,729 bytes allocated
==10547==
==10547== All heap blocks were freed -- no leaks are possible
==10547==
==10547== For counts of detected and suppressed errors, rerun with: -v
==10547== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始终确认已释放已分配的所有内存并且没有内存错误。
使用fgets
是获取用户输入的推荐方法。 scanf
如果充满了陷阱新C程序员的陷阱。 fgets
只需要检查返回值,然后使用 nul-terminatedating 字符覆盖缓冲区中包含的尾随'\n'
。你可以删除循环的整个for
循环,直到使用scanf
使用fgets
使用以下简化代码收到有效输入:
size_t len;
printf ("Enter Directory Name: ");
if (!fgets (directory_name, PATH_MAX, stdin)) {
fprintf (stderr, "error: failed to read directory_name.\n");
return 1;
}
len = strlen (directory_name);
if (len && directory_name[len - 1] == '\n')
directory_name[--len] = 0;
else if (len + 1 == PATH_MAX) {
fprintf (stderr, "error: directory_name too long.\n");
return 1;
}
仔细看看,如果您有其他问题,请告诉我。
答案 1 :(得分:0)
该行
strncpy (filesList[i],directory->d_name, strlen(directory->d_name) );\
将复制名称没有终止NUL,所以当你以后打印它时,你会在字符串的末尾看到额外的垃圾。请改用strcpy
。更好的是,使用strdup
一步分配和复制字符串。
答案 2 :(得分:0)
在末尾添加一个终止空字符。您可以使用strdup()来避免这种情况。
答案 3 :(得分:0)
在声明char *filesList[n];
init指向NULL
filesList[n] = NULL;
的最后一个指针后,将您的循环更改为此
for(i=0; filesList[i]; i++) {
printf("%s\n", filesList[i]);
}
你有那个垃圾,因为你试图访问指向你没有初始化的内存部分的指针。 例如,您有目录Music /,其中包含四个文件,file1,file2,file3,file4。所以你的初始化将类似于:
filesList[0] file4 copy
filesList[1] file2 copy
filesList[2] file1 copy
filesList[3] file3 copy
filesList[4] -> point to some uninit part of memory
然后你循环打印filesList [4]并显示垃圾。
同样在这样的陈述中,最好使用continue
而不是空身:
if (!strcmp(directory->d_name, ".") || !strcmp(directory->d_name, ".."))
{
continue;
}
此外,您可以将strncpy()
替换为snprintf()
它还始终包含空终止符'\0'
,除非缓冲区大小为0但您可以检查它或只添加'\0'
手动
filesList[i][strlen(directory->d_name)] = '\0';