C逐行读取文件

我写这个函数是从文件中读取一行:

const char *readLine(FILE *file) {


if (file == NULL) {
printf("Error: file pointer is null.");
exit(1);
}


int maximumLineLength = 128;
char *lineBuffer = (char *)malloc(sizeof(char) * maximumLineLength);


if (lineBuffer == NULL) {
printf("Error allocating memory for line buffer.");
exit(1);
}


char ch = getc(file);
int count = 0;


while ((ch != '\n') && (ch != EOF)) {
if (count == maximumLineLength) {
maximumLineLength += 128;
lineBuffer = realloc(lineBuffer, maximumLineLength);
if (lineBuffer == NULL) {
printf("Error reallocating space for line buffer.");
exit(1);
}
}
lineBuffer[count] = ch;
count++;


ch = getc(file);
}


lineBuffer[count] = '\0';
char line[count + 1];
strncpy(line, lineBuffer, (count + 1));
free(lineBuffer);
const char *constLine = line;
return constLine;
}

该函数正确地读取文件,使用printf我看到constLine字符串也被正确读取。

然而,如果我像这样使用函数:

while (!feof(myFile)) {
const char *line = readLine(myFile);
printf("%s\n", line);
}

Printf输出胡言乱语。为什么?

1158361 次浏览

readLine()返回指向局部变量的指针,这会导致未定义的行为。

你可以:

  1. 在调用函数中创建变量,并将其地址传递给readLine()
  2. 使用malloc()line分配内存——在这种情况下,line将是持久的
  3. 使用全局变量,尽管这通常是一个不好的做法
const char *readLine(FILE *file, char* line) {


if (file == NULL) {
printf("Error: file pointer is null.");
exit(1);
}


int maximumLineLength = 128;
char *lineBuffer = (char *)malloc(sizeof(char) * maximumLineLength);


if (lineBuffer == NULL) {
printf("Error allocating memory for line buffer.");
exit(1);
}


char ch = getc(file);
int count = 0;


while ((ch != '\n') && (ch != EOF)) {
if (count == maximumLineLength) {
maximumLineLength += 128;
lineBuffer = realloc(lineBuffer, maximumLineLength);
if (lineBuffer == NULL) {
printf("Error reallocating space for line buffer.");
exit(1);
}
}
lineBuffer[count] = ch;
count++;


ch = getc(file);
}


lineBuffer[count] = '\0';
char line[count + 1];
strncpy(line, lineBuffer, (count + 1));
free(lineBuffer);
return line;


}




char linebuffer[256];
while (!feof(myFile)) {
const char *line = readLine(myFile, linebuffer);
printf("%s\n", line);
}

注意,'line'变量是在调用函数中声明的,然后传递,因此readLine函数填充预定义的缓冲区并返回它。这是大多数C库的工作方式。

我知道还有其他办法

    char line[]定义为静态 (static char line[MAX_LINE_LENGTH] ->它将保存函数返回后的值)。- >坏, 函数是不可重入的,并且 竞态条件可以发生->如果你 从两个线程调用它两次 将覆盖它的结果
  • malloc()ing char行[],和 在调用函数时释放它-> 太多昂贵的__abc,和, 将释放缓冲区的责任委托给另一个函数(最优雅的解决方案是对同一函数中的任何缓冲区调用mallocfree)

顺便说一句,“显式”强制转换从char*const char*是多余的。

在btw2中,不需要malloc() lineBuffer,只需将其定义为char lineBuffer[128],因此不需要释放它

btw3不使用“动态大小堆栈数组”(将数组定义为char arrayName[some_nonconstant_variable]),如果你不确切地知道你在做什么,它只在C99中工作。

在你的readLine函数中,你返回一个指向line数组的指针(严格来说,一个指向它的第一个字符的指针,但区别在这里是无关的)。由于它是一个自动变量(即,它“在堆栈上”),当函数返回时,内存将被回收。你会看到乱码,因为printf已经把它自己的东西放在堆栈上了。

您需要从函数返回一个动态分配的缓冲区。你已经有了一个,它是lineBuffer;你所要做的就是把它截断成所需的长度。

    lineBuffer[count] = '\0';
realloc(lineBuffer, count + 1);
return lineBuffer;
}

添加(对注释中后续问题的响应):readLine返回一个指向组成该行的字符的指针。这个指针是处理该行内容所需要的指针。当你用完这些字符占用的内存时,它也是你必须传递给free的值。下面是readLine函数的使用方法:

char *line = readLine(file);
printf("LOG: read a line: %s\n", line);
if (strchr(line, 'a')) { puts("The line contains an a"); }
/* etc. */
free(line);
/* After this point, the memory allocated for the line has been reclaimed.
You can't use the value of `line` again (though you can assign a new value
to the `line` variable if you want). */

如果你的任务不是发明逐行读取函数,而只是逐行读取文件,你可以使用包含getline()函数的典型代码片段(参见手册页在这里):

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>


int main(void)
{
FILE * fp;
char * line = NULL;
size_t len = 0;
ssize_t read;


fp = fopen("/etc/motd", "r");
if (fp == NULL)
exit(EXIT_FAILURE);


while ((read = getline(&line, &len, fp)) != -1) {
printf("Retrieved line of length %zu:\n", read);
printf("%s", line);
}


fclose(fp);
if (line)
free(line);
exit(EXIT_SUCCESS);
}

这个例子有一些错误:

  • 你忘记在你的printfs中添加\n。此外,错误消息应该去stderr,即fprintf(stderr, ....
  • (不是很大的但是)考虑使用fgetc()而不是getc()getc()是一个宏,fgetc()是一个固有函数
  • getc()返回int,因此ch应该声明为int。这很重要,因为与EOF的比较将被正确处理。一些8位字符集使用0xFF作为有效字符(ISO-LATIN-1就是一个例子),而EOF是-1,如果分配给char,则将是0xFF
  • 该行存在潜在的缓冲区溢出

    lineBuffer[count] = '\0';
    

    如果该行的长度恰好为128个字符,count在执行时的值为128

  • 正如其他人指出的那样,line是一个局部声明的数组。你不能返回指向它的指针。

  • strncpy(count + 1)将最多复制count + 1字符,但如果它到达'\0'将终止,因为你将lineBuffer[count]设置为'\0',你知道它永远不会到达count + 1。然而,如果它这样做了,它就不会打开终止符'\0',所以你需要这样做。你经常会看到如下内容:

    char buffer [BUFFER_SIZE];
    strncpy(buffer, sourceString, BUFFER_SIZE - 1);
    buffer[BUFFER_SIZE - 1] = '\0';
    
  • if you malloc() a line to return (in place of your local char array), your return type should be char* - drop the const.

您应该使用ANSI函数来读取一行,例如。fgets。调用后,你需要在调用上下文中使用free(),例如:

...
const char *entirecontent=readLine(myFile);
puts(entirecontent);
free(entirecontent);
...


const char *readLine(FILE *file)
{
char *lineBuffer=calloc(1,1), line[128];


if ( !file || !lineBuffer )
{
fprintf(stderr,"an ErrorNo 1: ...");
exit(1);
}


for(; fgets(line,sizeof line,file) ; strcat(lineBuffer,line) )
{
if( strchr(line,'\n') ) *strchr(line,'\n')=0;
lineBuffer=realloc(lineBuffer,strlen(lineBuffer)+strlen(line)+1);
if( !lineBuffer )
{
fprintf(stderr,"an ErrorNo 2: ...");
exit(2);
}
}
return lineBuffer;
}
你犯了一个错误,返回一个指向自动变量的指针。 变量行被分配到堆栈中,并且只在函数存在的时间内存在。 你不允许返回指向它的指针,因为一旦它返回,内存就会被分配到其他地方
const char* func x(){
char line[100];
return (const char*) line; //illegal
}
为了避免这种情况,你可以返回一个位于堆上的内存指针。lineBuffer 当用户使用完free()时,调用它应该是用户的责任。 或者,您可以要求用户将一个内存地址作为参数传递给您,以便在。

处写入行内容

我想要一个代码从地面0,所以我这样做,逐行读取字典的单词的内容。

char temp_str [20];//你可以根据你的要求改变缓冲区的大小和一个文件中的单行长度。

请注意我已经初始化缓冲区空字符每次我读行。这个功能可以自动化,但因为我需要一个概念的证明,并想要一个字节一个字节地设计程序

#include<stdio.h>


int main()
{
int i;
char temp_ch;
FILE *fp=fopen("data.txt","r");
while(temp_ch!=EOF)
{
i=0;
char temp_str[20]={'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0'};
while(temp_ch!='\n')
{
temp_ch=fgetc(fp);
temp_str[i]=temp_ch;
i++;
}
if(temp_ch=='\n')
{
temp_ch=fgetc(fp);
temp_str[i]=temp_ch;
}
printf("%s",temp_str);
}
return 0;
}
//open and get the file handle
FILE* fh;
fopen_s(&fh, filename, "r");


//check if file exists
if (fh == NULL){
printf("file does not exists %s", filename);
return 0;
}




//read line by line
const size_t line_size = 300;
char* line = malloc(line_size);
while (fgets(line, line_size, fh) != NULL)  {
printf(line);
}
free(line);    // dont forget to free heap memory
void readLine(FILE* file, char* line, int limit)
{
int i;
int read;


read = fread(line, sizeof(char), limit, file);
line[read] = '\0';


for(i = 0; i <= read;i++)
{
if('\0' == line[i] || '\n' == line[i] || '\r' == line[i])
{
line[i] = '\0';
break;
}
}


if(i != read)
{
fseek(file, i - read + 1, SEEK_CUR);
}
}

这个怎么样?

使用fgets()从文件句柄中读取一行。

FILE* filePointer;
int bufferLength = 255;
char buffer[bufferLength]; /* not ISO 90 compatible */


filePointer = fopen("file.txt", "r");


while(fgets(buffer, bufferLength, filePointer)) {
printf("%s\n", buffer);
}


fclose(filePointer);

实现从文件中读取和获取内容的方法(input1.txt)

#include <stdio.h>
#include <stdlib.h>


void testGetFile() {
// open file
FILE *fp = fopen("input1.txt", "r");
size_t len = 255;
// need malloc memory for line, if not, segmentation fault error will occurred.
char *line = malloc(sizeof(char) * len);
// check if file exist (and you can open it) or not
if (fp == NULL) {
printf("can open file input1.txt!");
return;
}
while(fgets(line, len, fp) != NULL) {
printf("%s\n", line);
}
free(line);
}

希望这对你有所帮助。编码快乐!

这是我的几个小时…逐行读取整个文件。

char * readline(FILE *fp, char *buffer)
{
int ch;
int i = 0;
size_t buff_len = 0;


buffer = malloc(buff_len + 1);
if (!buffer) return NULL;  // Out of memory


while ((ch = fgetc(fp)) != '\n' && ch != EOF)
{
buff_len++;
void *tmp = realloc(buffer, buff_len + 1);
if (tmp == NULL)
{
free(buffer);
return NULL; // Out of memory
}
buffer = tmp;


buffer[i] = (char) ch;
i++;
}
buffer[i] = '\0';


// Detect end
if (ch == EOF && (i == 0 || ferror(fp)))
{
free(buffer);
return NULL;
}
return buffer;
}


void lineByline(FILE * file){
char *s;
while ((s = readline(file, 0)) != NULL)
{
puts(s);
free(s);
printf("\n");
}
}


int main()
{
char *fileName = "input-1.txt";
FILE* file = fopen(fileName, "r");
lineByline(file);
return 0;
}

我的工具从头开始:

FILE *pFile = fopen(your_file_path, "r");
int nbytes = 1024;
char *line = (char *) malloc(nbytes);
char *buf = (char *) malloc(nbytes);


size_t bytes_read;
int linesize = 0;
while (fgets(buf, nbytes, pFile) != NULL) {
bytes_read = strlen(buf);
// if line length larger than size of line buffer
if (linesize + bytes_read > nbytes) {
char *tmp = line;
nbytes += nbytes / 2;
line = (char *) malloc(nbytes);
memcpy(line, tmp, linesize);
free(tmp);
}
memcpy(line + linesize, buf, bytes_read);
linesize += bytes_read;


if (feof(pFile) || buf[bytes_read-1] == '\n') {
handle_line(line);
linesize = 0;
memset(line, '\0', nbytes);
}
}


free(buf);
free(line);

提供一个可移植的通用getdelim函数,通过msvc, clang, gcc进行测试。

/*
* An implementation conform IEEE Std 1003.1-2017:
* https://pubs.opengroup.org/onlinepubs/9699919799/functions/getdelim.html
*
* <nio.h>:
* https://github.com/junjiemars/c/blob/c425bd0e49df35a2649327664d3f6cd610791996/src/posix/nio.h
* <nio.c>:
* https://github.com/junjiemars/c/blob/c425bd0e49df35a2649327664d3f6cd610791996/src/posix/nio.c
*
*/


#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>




/*
* LINE_MAX dependents on OS' implementations so check it first.
* https://github.com/junjiemars/c/blob/c425bd0e49df35a2649327664d3f6cd610791996/src/posix/nlim_auto_check
*/
#define NM_LINE_MAX  4096       /* Linux */


#if (MSVC)
typedef SSIZE_T  ssize_t;
#  if !defined(SSIZE_MAX)
#    define SSIZE_MAX  ((ssize_t)((size_t)((ssize_t)-1) >> 1))
#  endif
#endif




ssize_t getdelim(char **restrict lineptr, size_t *restrict n, int delimiter,
FILE *restrict stream);


#if defined(getline)
#  undef getline
#endif
#define getline(lp, n, f)  getdelim((lp), (n), 0x0a, (f))




ssize_t
getdelim(char **restrict lineptr, size_t *restrict n, int delimiter,
FILE *restrict stream)
{
int       c;
char     *p, *p1;
ssize_t   len;


if (NULL == lineptr || NULL == n || NULL == stream
|| (UCHAR_MAX < delimiter || delimiter < 0))
{
errno = EINVAL;
return EOF;
}


if (feof(stream) || ferror(stream))
{
return EOF;
}


if (0 == *lineptr)
{
if (0 == *n)
{
*n = NM_LINE_MAX;
}


*lineptr = malloc(*n);
if (0 == *lineptr)
{
return EOF;
}
}


p = *lineptr;
len = 0;


while (EOF != (c = fgetc(stream)))
{
if (SSIZE_MAX == (ssize_t) len)
{
errno = EOVERFLOW;
return EOF;
}


if ((size_t) len == (*n - 1))
{
*n <<= 1;
p1 = realloc(*lineptr, *n);
if (0 == p1)
{
return EOF;
}
*lineptr = p1;
p = p1 + len;
}
*p++ = (char) c;
len++;


if (c == delimiter)
{
break;
}
}


if (ferror(stream))
{
return EOF;
}


*p = 0;
return len;
}






int
main(void)
{
FILE     *fp;
char     *line  =  NULL;
size_t    len   =  0;
ssize_t   read;


fp = fopen("/some-file", "r");
if (fp == NULL)
exit(1);
while ((read = getline(&line, &len, fp)) != -1) {
printf("Retrieved line of length %zu :\n", read);
printf("%s", line);
}
if (ferror(fp)) {
/* handle error */
}
free(line);
fclose(fp);


return 0;


}

一个完整的fgets()解决方案:

#include <stdio.h>
#include <string.h>


#define MAX_LEN 256


int main(void)
{
FILE* fp;
fp = fopen("file.txt", "r");
if (fp == NULL) {
perror("Failed: ");
return 1;
}


char buffer[MAX_LEN];
while (fgets(buffer, MAX_LEN, fp))
{
// Remove trailing newline
buffer[strcspn(buffer, "\n")] = 0;
printf("%s\n", buffer);
}


fclose(fp);
return 0;
}

输出:

First line of file
Second line of file
Third (and also last) line of file

记住,如果你想从标准输入(而不是像这种情况下的文件)中读取,那么你所要做的就是将stdin作为fgets()方法的第三个参数,如下所示:

while(fgets(buffer, MAX_LEN, stdin))

附录

从fgets()输入中删除后面的换行符

如何检测文件在c中是否打开