在C中创建一个函数来读取文件但它无限循环
我基本上想打印出我制作的文件中的所有行,但它只是一遍又一遍地循环回到开始,基本上是因为我在函数中设置了fseek(fp, 0, SEEK_SET);这部分,但我知道否则我将如何放置它以完成所有其他操作行我基本上每次都回到开始。
#include<stdio.h>
#include <stdlib.h>
char *freadline(FILE *fp);
int main(){
FILE *fp = fopen("story.txt", "r");
if(fp == NULL){
printf("Error!");
}else{
char *pInput;
while(!feof(fp)){
pInput = freadline(fp);
printf("%sn", pInput); // outpu
}
}
return 0;
}
char *freadline(FILE *fp){
int i;
for(i = 0; !feof(fp); i++){
getc(fp);
}
fseek(fp, 0, SEEK_SET); // resets to origin (?)
char *pBuffer = (char*)malloc(sizeof(char)*i);
pBuffer = fgets(pBuffer, i, fp);
return pBuffer;
}
这是我目前的工作
回答
继续我的评论,您正在按照正确的思路思考,您只是没有按照正确的顺序将各个部分放在一起。在main()您循环调用您的函数时,分配一行,然后输出一行文本并一遍又一遍地执行。(并且不释放您分配的任何内存 - 造成每行读取的内存泄漏)
如果您正在分配存储以保存该行,您通常希望通过文件分配和存储所有行,并返回指向您的行集合的指针以在main()(或任何调用函数)。
您可以通过添加一个额外的间接级别并让您的函数 return 来做到这一点char **。这是一个简单的两步过程,您可以:
-
分配包含指针的内存块(每行一个指针)。由于您事先不知道有多少行,因此您只需分配一些指针,然后
realloc()在用完时分配更多指针; -
对于您读取的每一行,您分配
length + 1存储字符并将当前行复制到该内存块,将该行的地址分配给您的下一个可用指针。
(您要么跟踪分配的指针和行的数量,要么在最后一个指针分配一行之后提供一个额外的指针设置NULL为哨兵——由您决定,简单地跟踪计数器在概念上可能更容易)
阅读完最后一行后,您只需将指针返回到分配给调用者使用的指针集合。(您也可以将 a 的地址char **作为参数传递给您的函数,导致类型为char ***,但成为三星级程序员并不总是一种恭维)。但是,这样做并没有错,在某些情况下,这是必需的,但如果您有其他选择,那通常是首选路线。
那么这在实践中如何运作呢?
只需将您的函数返回类型更改为char **并传递一个指向计数变量的附加指针,这样您就可以使用从函数返回之前读取的行数更新该地址处的值。例如,你可以这样做:
char **readfile (FILE *fp, size_t *n);
char **readfile (FILE *fp, size_t *n);
这将使您的文件指针指向一个打开的文件流,然后从中读取每一行,为该行分配存储空间并将该分配的地址分配给您的一个指针。在该函数中,您将使用一个足够大的字符数组来保存您使用fgets(). 'n'从末尾修剪并获取长度,然后分配length + 1字节来保存该行。将新分配的块的地址分配给一个指针,然后从您的数组复制到新分配的块。
(strdup()可以分配和复制,但它不是标准库的一部分,它是 POSIX——尽管如果提供适当的选项,大多数编译器都支持它作为扩展)
在readfile()函数下面把它放在一起,从单个指针开始,当你用完时重新分配两倍于当前数字(这提供了所需分配数量和指针数量之间的合理权衡。(仅调用 20 次后realloc(),您将有 1M 指针)。您可以选择任何您喜欢的重新分配和增长方案,但您希望避免调用realloc()每一行——realloc()仍然是一个相对昂贵的调用。
#define MAXC 1024 /* if you need a constant, #define one (or more) */
#define NPTR 1 /* initial no. of pointers to allocate */
/* readfile reads all lines from fp, updating the value at the address
* provided by 'n'. On success returns pointer to allocated block of pointers
* with each of *n pointers holding the address of an allocated block of
* memory containing a line from the file. On allocation failure, the number
* of lines successfully read prior to failure is returned. Caller is
* responsible for freeing all memory when done with it.
*/
char **readfile (FILE *fp, size_t *n)
{
char buffer[MAXC], **lines; /* buffer to hold each line, pointer */
size_t allocated = NPTR, used = 0; /* allocated and used pointers */
lines = malloc (allocated * sizeof *lines); /* allocate initial pointer(s) */
if (lines == NULL) { /* validate EVERY allocation */
perror ("malloc-lines");
return NULL;
}
while (fgets (buffer, MAXC, fp)) { /* read each line from file */
size_t len; /* variable to hold line-length */
if (used == allocated) { /* is pointer reallocation needed */
/* always realloc to a temporary pointer to avoid memory leak if
* realloc fails returning NULL.
*/
void *tmp = realloc (lines, 2 * allocated * sizeof *lines);
if (!tmp) { /* validate EVERY reallocation */
perror ("realloc-lines");
break; /* lines before failure still good */
}
lines = tmp; /* assign reallocted block to lines */
allocated *= 2; /* update no. of allocated pointers */
}
buffer[(len = strcspn (buffer, "n"))] = 0; /* trim n, save length */
lines[used] = malloc (len + 1); /* allocate storage for line */
if (!lines[used]) { /* validate EVERY allocation */
perror ("malloc-lines[used]");
break;
}
memcpy (lines[used], buffer, len + 1); /* copy buffer to lines[used] */
used++; /* increment used no. of pointers */
}
*n = used; /* update value at address provided by n */
/* can do final realloc() here to resize exactly to used no. of pointers */
return lines; /* return pointer to allocated block of pointers */
}
在 中main(),您只需传递文件指针和size_t变量地址并在遍历指针之前检查返回值,以使用您需要的任何行(它们只是简单地打印在下面),例如
int main (int argc, char **argv) {
char **lines; /* pointer to allocated block of pointers and lines */
size_t n; /* number of lines read */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
lines = readfile (fp, &n);
if (fp != stdin) /* close file if not stdin */
fclose (fp);
if (!lines) { /* validate readfile() return */
fputs ("error: no lines read from file.n", stderr);
return 1;
}
for (size_t i = 0; i < n; i++) { /* loop outputting all lines read */
puts (lines[i]);
free (lines[i]); /* don't forget to free lines */
}
free (lines); /* and free pointers */
return 0;
}
(注意:完成后不要忘记释放您分配的内存。当您调用在其他函数中分配的函数时,这变得至关重要。在 中main(),内存将在退出时自动释放,但要养成良好的习惯。)
示例使用/输出
该程序将读取任何文件,无论它是 4 行还是 400,000 行,直到系统内存的物理限制(MAXC如果您的行超过 1023 个字符,则进行调整)。
内存使用/错误检查
在你写的,可动态分配内存的任何代码,你有2个职责关于分配的内存中的任何模块:(1)始终保持一个指针起始地址的内存,因此块,(2),可以释放时,它是没有不再需要。
您必须使用内存错误检查程序来确保您不会尝试访问内存或写入超出/超出分配块的边界,尝试读取或基于未初始化值的条件跳转,最后,确认你释放了你分配的所有内存。
对于Linuxvalgrind是正常的选择。每个平台都有类似的内存检查器。它们都易于使用,只需通过它运行您的程序即可。
#define MAXC 1024 /* if you need a constant, #define one (or more) */
#define NPTR 1 /* initial no. of pointers to allocate */
/* readfile reads all lines from fp, updating the value at the address
* provided by 'n'. On success returns pointer to allocated block of pointers
* with each of *n pointers holding the address of an allocated block of
* memory containing a line from the file. On allocation failure, the number
* of lines successfully read prior to failure is returned. Caller is
* responsible for freeing all memory when done with it.
*/
char **readfile (FILE *fp, size_t *n)
{
char buffer[MAXC], **lines; /* buffer to hold each line, pointer */
size_t allocated = NPTR, used = 0; /* allocated and used pointers */
lines = malloc (allocated * sizeof *lines); /* allocate initial pointer(s) */
if (lines == NULL) { /* validate EVERY allocation */
perror ("malloc-lines");
return NULL;
}
while (fgets (buffer, MAXC, fp)) { /* read each line from file */
size_t len; /* variable to hold line-length */
if (used == allocated) { /* is pointer reallocation needed */
/* always realloc to a temporary pointer to avoid memory leak if
* realloc fails returning NULL.
*/
void *tmp = realloc (lines, 2 * allocated * sizeof *lines);
if (!tmp) { /* validate EVERY reallocation */
perror ("realloc-lines");
break; /* lines before failure still good */
}
lines = tmp; /* assign reallocted block to lines */
allocated *= 2; /* update no. of allocated pointers */
}
buffer[(len = strcspn (buffer, "n"))] = 0; /* trim n, save length */
lines[used] = malloc (len + 1); /* allocate storage for line */
if (!lines[used]) { /* validate EVERY allocation */
perror ("malloc-lines[used]");
break;
}
memcpy (lines[used], buffer, len + 1); /* copy buffer to lines[used] */
used++; /* increment used no. of pointers */
}
*n = used; /* update value at address provided by n */
/* can do final realloc() here to resize exactly to used no. of pointers */
return lines; /* return pointer to allocated block of pointers */
}
始终确认您已释放所有分配的内存并且没有内存错误。
用于示例的完整代码仅包含标头,但为了完整起见,将其包含在下面:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
#define NPTR 1 /* initial no. of pointers to allocate */
/* readfile reads all lines from fp, updating the value at the address
* provided by 'n'. On success returns pointer to allocated block of pointers
* with each of *n pointers holding the address of an allocated block of
* memory containing a line from the file. On allocation failure, the number
* of lines successfully read prior to failure is returned. Caller is
* responsible for freeing all memory when done with it.
*/
char **readfile (FILE *fp, size_t *n)
{
char buffer[MAXC], **lines; /* buffer to hold each line, pointer */
size_t allocated = NPTR, used = 0; /* allocated and used pointers */
lines = malloc (allocated * sizeof *lines); /* allocate initial pointer(s) */
if (lines == NULL) { /* validate EVERY allocation */
perror ("malloc-lines");
return NULL;
}
while (fgets (buffer, MAXC, fp)) { /* read each line from file */
size_t len; /* variable to hold line-length */
if (used == allocated) { /* is pointer reallocation needed */
/* always realloc to a temporary pointer to avoid memory leak if
* realloc fails returning NULL.
*/
void *tmp = realloc (lines, 2 * allocated * sizeof *lines);
if (!tmp) { /* validate EVERY reallocation */
perror ("realloc-lines");
break; /* lines before failure still good */
}
lines = tmp; /* assign reallocted block to lines */
allocated *= 2; /* update no. of allocated pointers */
}
buffer[(len = strcspn (buffer, "n"))] = 0; /* trim n, save length */
lines[used] = malloc (len + 1); /* allocate storage for line */
if (!lines[used]) { /* validate EVERY allocation */
perror ("malloc-lines[used]");
break;
}
memcpy (lines[used], buffer, len + 1); /* copy buffer to lines[used] */
used++; /* increment used no. of pointers */
}
*n = used; /* update value at address provided by n */
/* can do final realloc() here to resize exactly to used no. of pointers */
return lines; /* return pointer to allocated block of pointers */
}
int main (int argc, char **argv) {
char **lines; /* pointer to allocated block of pointers and lines */
size_t n; /* number of lines read */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
lines = readfile (fp, &n);
if (fp != stdin) /* close file if not stdin */
fclose (fp);
if (!lines) { /* validate readfile() return */
fputs ("error: no lines read from file.n", stderr);
return 1;
}
for (size_t i = 0; i < n; i++) { /* loop outputting all lines read */
puts (lines[i]);
free (lines[i]); /* don't forget to free lines */
}
free (lines); /* and free pointers */
return 0;
}
如果您还有其他问题,请告诉我。