I'm trying to store a list of files with a .txt extension into an array from my current working directory. I've gotten as far as getting the array together, now I just need to return it. I'm new to C and I'm not used to the pointer situation. How can I return an array of char pointers? I have the array created, and its allocated based on how many Text files I find in the directory.
I'm getting two errors when I try to compile which leads me to believe that my understanding of pointers is not what I thought it was. I also have been reading that my array will be destroyed when returning from the function, because it is on the stack. I'm not sure how to fix that. Any help or criticism would be welcome.
// prototypes
char* getLine();
void replaceWord();
char * getTxtFilesInDir(char*);
bool isTxt(char *);
int main(int argc, char **argv) {
// Check to see if correct number of arguments is given
if (argc < 4) {
printf("ERROR: Not enough parameters supplied. Please supply a find,"
"replace, and prefix parameter and try again.\n");
return -1;
}
// Initialize variables
char *find, *replace, *prefix;
find=argv[1];
replace=argv[2];
prefix=argv[3];
char cd[1024];
// Get the Current working directory to grab files
if (getcwd(cd, sizeof(cd)) != NULL) {
printf("Current working dir is: %s\n", cd);
} else {
perror("getcwd() error");
}
// Get the values of the arguments
printf("Value of find: %s\nValue of replace: %s\nValue of prefix: %s\n",
find, replace, prefix);
// create an array of the files that are valid
char* files = getTxtFilesInDir(cd);
return 0;
}
char* getTxtFilesInDir(char* directory) {
DIR *d;
struct dirent *dir;
d = opendir(directory);
int n=0, i=0;
// get the number of text files in the directory
if (d) {
while((dir = readdir(d)) != NULL) {
if (isTxt(dir->d_name)) {
n++;
}
}
}
rewinddir(d);
// Create the array of text files depending on the size
static char* txtFiles[n];
// add the text files to the array
if (d) {
printf("Found: \n");
while ((dir = readdir(d)) != NULL)
{
if (isTxt(dir->d_name)) {
printf("%s\n", dir->d_name);
txtFiles[i]= (char*) malloc (strlen(dir->d_name)+1);
strncpy(txtFiles[i], dir->d_name, strlen(dir->d_name));
i++;
}
}
closedir(d);
}
return txtFiles;
}
bool isTxt(char *file) {
size_t len = strlen(file);
return len > 4 && strcmp(file + len -4, ".txt") == 0;
}
If you want that getTxtFilesInDir
returns an array of strings, it should return a char**
, not a char*
.
Also you don't need to declare the variable as static
. You declare a variable in a function as static
, when you want that the variable remains the same for all calls of the function. In this case this is probably not what you want to do.
Without modifying too much of your initial code, you can first allocate memory for an arrray of strings, and then resize it when you've got a new entry. In this example I do that at once, because realloc(NULL, somesize)
is the same as doing malloc(somesize)
. That's why it's important to initialize *tmp = 0
and txtFiles = NULL
, so this trick works. You should also pass a pointer to an size_t
where you store the number of entries:
char **getTxtFilesInDir(const char* directory, size_t *len) {
if(directory == NULL || len == NULL)
return NULL;
...
*len = 0;
char **txtFiles = NULL, **tmp;
char *str;
if (d) {
printf("Found: \n");
while ((dir = readdir(d)) != NULL)
{
if (isTxt(dir->d_name))
{
tmp = realloc(txtFiles, (len+1) * sizeof *textFiles);
if(tmp == NULL)
return txtFiles; // return what you've got so far
str = malloc(strlen(dir->d_name) + 1);
if(str == NULL)
{
if(txtFiles == NULL) // first time, free everything
{
free(tmp);
return NULL;
}
return tmp; // return all you've got so far
}
strcpy(str, dir->d_name); // no need of strcnpy, you've allocated
// enough memory
txtFiles = tmp;
txtFiles[(*len)++] = tmp;
}
}
closedir(d);
return txtFiles;
}
The important bits here is 1. how you expand the memory with realloc
. Then it allocates memory for the string using malloc
. I do that before txtFiles = tmp;
, so that I don't have to write to many if(...==NULL)
. If something goes wrong along the way, the function returns all the file names it already has stored.
Now in main
you do:
int main(int argc, char **argv)
{
...
size_t len = 0;
char **files = getTxtFilesInDir(cd, &len);
if(file == NULL)
{
fprintf(stderr, "No files found\n");
return 1;
}
for(size_t i = 0; i < len; ++i)
printf("file: %s\n", files[i]);
// freeing memory
for(size_t i = 0; i < len; ++i)
free(files[i]);
free(files);
return 0;
}
I also want to comment on your orginal way of copying:
strncpy(txtFiles[i], dir->d_name, strlen(dir->d_name));
strncpy
is great when you want to limit the number of bytes to be copied. In your case you've already allocated enough memory, so there is no need to. And when you use strncpy
, the limit should be bound to the number of bytes available to the destination, not the source, otherwise you might copy more bytes than it should. In your case you won't get a valid c-string, because you are limiting to copy to the lenth of dir->d_name
, so the '\\0'
-terminating won't be copied.
man strcpy
#include <string.h> char *strcpy(char *dest, const char *src); char *strncpy(char *dest, const char *src, size_t n);
[...]
The
strncpy()
function is similar, except that at mostn
bytes ofsrc
are copied. Warning: If there is no null byte among the firstn
bytes ofsrc
, the string placed in dest will not be null-terminated.
When you use strncpy
you must make sure that the copy is a valid string by setting the '\\0'
-terminating byte yourself. Because you've allocated strlen(dir->d_name)+1
bytes for the string:
strncpy(txtFiles[i], dir->d_name, strlen(dir->d_name));
textFiles[i][strlen(dir->d_name)] = 0;
Also the exit value of a program is a unsigned
value, it goes from 0 to 255. In main
you should return 1
instead of -1
on error.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.