Ваш вопрос не в том, чтобы проанализировать содержимое файла, а в том, чтобы просто прочитать строки файла в адекватном хранилище внутри функции таким образом, чтобы объект, содержащий сохраненные строки, мог быть возвращен вызывающемуфункция.Это довольно стандартно, но у вас есть несколько способов приблизиться к нему.
Самое большое соображение - не знать длину строк, которые нужно прочитать.Вы говорите, что в настоящее время нужно прочитать 3 строки, но не нужно заранее знать, сколько строк (зная - вы можете избежать realloc
, но это единственная экономия)
Вы хотите создать как можно более надежный и гибкий метод для чтения строк и их сохранения таким образом, чтобы выделить достаточно памяти для хранения прочитанного.Хороший подход состоит в том, чтобы объявить временный буфер фиксированного размера для хранения каждой строки, считанной из файла с fgets
, а затем вызвать strlen
в буфере, чтобы определить необходимое количество символов (а также обрезать завершающий символ новой строки).на fgets
) Поскольку вы читаете информацию о пути, предопределенный макрос PATH_MAX
может использоваться для адекватного размера вашего временного буфера, чтобы гарантировать, что он может содержать путь максимального размера, используемый системой.Вы также можете использовать POSIX geline
вместо fgets
, но сейчас мы будем придерживаться стандартной библиотеки C.
Базовый тип, который позволит вам выделить память для нескольких строк в вашей функции ивернуть единственный указатель, который вы можете использовать в вызывающей функции: char **
( указатель на указатель на символ - или произвольно динамический массив указателей ).Схема проста: вы выделяете некоторое начальное количество указателей (3 в вашем случае), а затем перебираете файл, читая строку за раз, получая длину строки, а затем выделяя length + 1
символов хранения длядержать строй.Например, если вы выделите 3 указателя с помощью:
#define NPATHS 3
...
char **readcmdfile (FILE *fp, size_t *n)
{
...
char buf[PATH_MAX] = ""; /* temp buffer to hold line */
char **paths = NULL; /* pointer to pointer to char to return */
size_t idx = 0; /* index counter (avoids dereferencing) */
...
paths = calloc (NPATHS, sizeof *paths); /* allocate NPATHS pointers */
if (!paths) { /* validate allocation/handle error */
perror ("calloc-paths");
return NULL;
}
...
while (idx < NPATHS && fgets (buf, sizeof buf, fp)) {
size_t len = strlen (buf); /* get length of string in buf */
...
paths[idx] = malloc (len + 1); /* allocate storage for line */
if (!paths[idx]) { /* validate allocation */
perror ("malloc-paths[idx]"); /* handle error */
return NULL;
}
strcpy (paths[idx++], buf); /* copy buffer to paths[idx] */
...
return paths; /* return paths */
}
( note: , вы можете отменить ограничение idx < NPATHS
, если включить проверку перед выделением для каждой строки и *На 1026 * больше указателей, по мере необходимости)
Остальное - просто обработка открытия файла и передача открытого файлового потока в вашу функцию.Основной подход состоит в том, чтобы либо указать имя файла в командной строке, а затем открыть имя файла с fopen
(или прочитать из stdin
по умолчанию, если имя файла не указано).Как и на каждом шаге в вашей программе, вам нужно проверить возвращаемые значения и обработать любую ошибку , чтобы избежать обработки мусора (и вызова Undefined Behavior )
Простым примером будет:
int main (int argc, char **argv) {
char **paths; /* pointer to pointer to char for paths */
size_t i, n = 0; /* counter and n - number of paths read */
/* open file given by 1st argument (or read stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("fopen-failed");
return 1;
}
paths = readcmdfile (fp, &n); /* call function to read file */
/* passing open file pointer */
if (!paths) { /* validate return from function */
fprintf (stderr, "error: readcmdfile failed.\n");
return 1;
}
for (i = 0; i < n; i++) { /* output lines read from file */
printf ("path[%lu]: %s\n", i + 1, paths[i]);
free (paths[i]); /* free memory holding line */
}
free (paths); /* free pointers */
return 0;
}
Соединение всех частей вместе, добавление кода обрезки для чтения '\n'
и включение его в buf
на fgets
, и добавление дополнительного теста кубедитесь, что строка, которую вы прочитали, действительно помещается в buf
, вы можете сделать что-то вроде этого:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h> /* for PATH_MAX */
#define NPATHS 3
/* read lines from file, return pointer to pointer to char on success
* otherwise return NULL. 'n' will contain number of paths read from file.
*/
char **readcmdfile (FILE *fp, size_t *n)
{
char buf[PATH_MAX] = ""; /* temp buffer to hold line */
char **paths = NULL; /* pointer to pointer to char to return */
size_t idx = 0; /* index counter (avoids dereferencing) */
*n = 0; /* zero the pointer passed as 'n' */
paths = calloc (NPATHS, sizeof *paths); /* allocate NPATHS pointers */
if (!paths) { /* validate allocation/handle error */
perror ("calloc-paths");
return NULL;
}
/* read while index < NPATHS & good read into buf
* (note: instead of limiting to NPATHS - you can simply realloc paths
* when idx == NPATHS -- but that is for later)
*/
while (idx < NPATHS && fgets (buf, sizeof buf, fp)) {
size_t len = strlen (buf); /* get length of string in buf */
if (len && buf[len - 1] == '\n') /* validate last char is '\n' */
buf[--len] = 0; /* overwrite '\n' with '\0' */
else if (len == PATH_MAX - 1) { /* check buffer full - line to long */
fprintf (stderr, "error: path '%lu' exceeds PATH_MAX.\n", idx);
return NULL;
}
paths[idx] = malloc (len + 1); /* allocate storage for line */
if (!paths[idx]) { /* validate allocation */
perror ("malloc-paths[idx]"); /* handle error */
return NULL;
}
strcpy (paths[idx++], buf); /* copy buffer to paths[idx] */
}
*n = idx; /* update 'n' to contain index - no. of lines read */
return paths; /* return paths */
}
int main (int argc, char **argv) {
char **paths; /* pointer to pointer to char for paths */
size_t i, n = 0; /* counter and n - number of paths read */
/* open file given by 1st argument (or read stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("fopen-failed");
return 1;
}
paths = readcmdfile (fp, &n); /* call function to read file */
/* passing open file pointer */
if (!paths) { /* validate return from function */
fprintf (stderr, "error: readcmdfile failed.\n");
return 1;
}
for (i = 0; i < n; i++) { /* output lines read from file */
printf ("path[%lu]: %s\n", i + 1, paths[i]);
free (paths[i]); /* free memory holding line */
}
free (paths); /* free pointers */
return 0;
}
( примечание: если вы выделите память - это ваше делосохранить указатель на начало каждого блока - чтобы его можно было освободить, когда он больше не нужен)
Пример входного файла
$ cat paths.txt
/home/bla/dirname
/home/bla/bla/file1.txt
/home/bla/bla/file2.txt
Пример использования / Вывод
$ ./bin/readpaths <paths.txt
path[1]: /home/bla/dirname
path[2]: /home/bla/bla/file1.txt
path[3]: /home/bla/bla/file2.txt
Как вы можете видеть, функция просто прочитала каждую строку входного файла, присвоила 3 указателя, присвоила каждой строке и присвоила адрес для каждого блока соответствующемууказатель, а затем возвращает указатель на коллекциюmain()
там, где ему назначено paths
.Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.