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

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输出胡言乱语。为什么?


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

你可以:

在调用函数中创建变量,并将其地址传递给readLine() 使用malloc()为行分配内存——在这种情况下,行将是持久的 使用全局变量,尽管这通常是一个不好的做法


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库的工作方式。

我知道还有其他办法

defining the char line[] as static (static char line[MAX_LINE_LENGTH] -> it will hold it's value AFTER returning from the function). -> bad, the function is not reentrant, and race condition can occur -> if you call it twice from two threads, it will overwrite it's results malloc()ing the char line[], and freeing it in calling functions -> too many expensive mallocs, and, delegating the responsibility to free the buffer to another function (the most elegant solution is to call malloc and free on any buffers in same function)

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

在btw2中,不需要malloc() lineBuffer,只需要定义它为char lineBuffer[128],所以你不需要释放它

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


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

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

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

ADDED(对评论中后续问题的回应):readLine返回一个指向组成该行的字符的指针。这个指针是处理该行内容所需要的指针。当你用完这些字符占用的内存时,你也必须通过它来释放内存。下面是如何使用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);
}

这个例子有一些错误:

you forgot to add \n to your printfs. Also error messages should go to stderr i.e. fprintf(stderr, .... (not a biggy but) consider using fgetc() rather than getc(). getc() is a macro, fgetc() is a proper function getc() returns an int so ch should be declared as an int. This is important since the comparison with EOF will be handled correctly. Some 8 bit character sets use 0xFF as a valid character (ISO-LATIN-1 would be an example) and EOF which is -1, will be 0xFF if assigned to a char. There is a potential buffer overflow at the line lineBuffer[count] = '\0'; If the line is exactly 128 characters long, count is 128 at the point that gets executed. As others have pointed out, line is a locally declared array. You can't return a pointer to it. strncpy(count + 1) will copy at most count + 1 characters but will terminate if it hits '\0' Because you set lineBuffer[count] to '\0' you know it will never get to count + 1. However, if it did, it would not put a terminating '\0' on, so you need to do it. You often see something like the following: 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];//你可以根据你的要求改变缓冲区的大小和一个文件中的单行长度。

注意,每次读取line时,我都将缓冲区初始化为Null字符。这个功能可以自动化,但因为我需要一个概念的证明,并想要一个字节一个字节地设计程序

#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中检测文件是否打开