在 C 中,我应该如何读取文本文件并打印所有字符串

Posted

技术标签:

【中文标题】在 C 中,我应该如何读取文本文件并打印所有字符串【英文标题】:In C, how should I read a text file and print all strings 【发布时间】:2011-03-28 15:58:00 【问题描述】:

我有一个名为test.txt的文本文件

我想写一个可以读取这个文件并将内容打印到控制台的C程序(假设文件只包含ASCII文本)。

我不知道如何获取字符串变量的大小。像这样:

char str[999];
FILE * file;
file = fopen( "test.txt" , "r");
if (file) 
    while (fscanf(file, "%s", str)!=EOF)
        printf("%s",str);
    fclose(file);

999 的大小不起作用,因为fscanf 返回的字符串可能比这更大。我该如何解决这个问题?

【问题讨论】:

【参考方案1】:

最简单的方法是读取一个字符,读取后立即打印:

int c;
FILE *file;
file = fopen("test.txt", "r");
if (file) 
    while ((c = getc(file)) != EOF)
        putchar(c);
    fclose(file);

c 是上面的int,因为EOF 是一个负数,而普通的char 可能是unsigned

如果你想分块读取文件,但没有动态内存分配,你可以这样做:

#define CHUNK 1024 /* read 1024 bytes at a time */
char buf[CHUNK];
FILE *file;
size_t nread;

file = fopen("test.txt", "r");
if (file) 
    while ((nread = fread(buf, 1, sizeof buf, file)) > 0)
        fwrite(buf, 1, nread, stdout);
    if (ferror(file)) 
        /* deal with error */
    
    fclose(file);

上面的第二种方法本质上是如何读取具有动态分配数组的文件:

char *buf = malloc(chunk);

if (buf == NULL) 
    /* deal with malloc() failure */


/* otherwise do this.  Note 'chunk' instead of 'sizeof buf' */
while ((nread = fread(buf, 1, chunk, file)) > 0) 
    /* as above */

您的fscanf() 方法以%s 作为格式会丢失有关文件中空格的信息,因此它不会将文件完全复制到stdout

【讨论】:

不用c/c++打开文件就可以从文件中读取数据?? 如果文本文件包含逗号分隔的整数值怎么办?比代码是什么,您也可以在其中编辑您的答案。 以上适用于任何类型的文本文件。如果你想解析 CSV 文件中的数字,那就是另外一个问题了。 @overexchange 这个问题不涉及行 - 它是关于读取文件并将其内容复制到stdout @shjeff 文件不能包含 EOF 字符。注意c是int,C会保证EOF不等于任何有效字符。【参考方案2】:

这里有很多关于分块读取的好答案,我只是向您展示一个小技巧,它可以一次将所有内容读取到缓冲区并打印出来。

我并不是说它更好。并非如此,正如 Ricardo 有时它可能会很糟糕,但我发现它对于简单的情况来说是一个很好的解决方案。

我在上面撒了 cmets,因为发生了很多事情。

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

char* ReadFile(char *filename)

   char *buffer = NULL;
   int string_size, read_size;
   FILE *handler = fopen(filename, "r");

   if (handler)
   
       // Seek the last byte of the file
       fseek(handler, 0, SEEK_END);
       // Offset from the first to the last byte, or in other words, filesize
       string_size = ftell(handler);
       // go back to the start of the file
       rewind(handler);

       // Allocate a string that can hold it all
       buffer = (char*) malloc(sizeof(char) * (string_size + 1) );

       // Read it all in one operation
       read_size = fread(buffer, sizeof(char), string_size, handler);

       // fread doesn't set it so put a \0 in the last position
       // and buffer is now officially a string
       buffer[string_size] = '\0';

       if (string_size != read_size)
       
           // Something went wrong, throw away the memory and set
           // the buffer to NULL
           free(buffer);
           buffer = NULL;
       

       // Always remember to close the file.
       fclose(handler);
    

    return buffer;


int main()

    char *string = ReadFile("yourfile.txt");
    if (string)
    
        puts(string);
        free(string);
    

    return 0;

让我知道它是否有用,或者您可以从中学到一些东西:)

【讨论】:

不应该是buffer[string_size] = '\0';而不是string_size+1吗? Afaik 实际字符串从 0string_size-1\0 字符因此需要在 string_size,对吧? 使用ftellfseek查找文件大小是不安全的:securecoding.cert.org/confluence/display/seccode/… 此代码包含内存泄漏,您永远不会关闭该文件。缺少fclose(handle) 调用fclose(handle)的地方有个错字,应该是fclose(handler) 您可以使用calloc(2) 而不是malloc(1) 来跳过必须设置空终止符。【参考方案3】:

而只是直接将字符打印到控制台上,因为文本文件可能非常大并且您可能需要大量内存。

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

int main() 

    FILE *f;
    char c;
    f=fopen("test.txt","rt");

    while((c=fgetc(f))!=EOF)
        printf("%c",c);
    

    fclose(f);
    return 0;

【讨论】:

【参考方案4】:

使用“read()”代替 fscanf:

ssize_t read(int fildes, void *buf, size_t nbyte);

描述

read() 函数应尝试从与打开的文件描述符fildes 关联的文件中读取nbyte 字节到buf 指向的缓冲区中。

这是一个例子:

http://cmagical.blogspot.com/2010/01/c-programming-on-unix-implementing-cat.html

该示例的工作部分:

f=open(argv[1],O_RDONLY);
while ((n=read(f,l,80)) > 0)
    write(1,l,n);

另一种方法是使用 getc/putc 一次读取/写入 1 个字符。效率低很多。一个很好的例子:http://www.eskimo.com/~scs/cclass/notes/sx13.html

【讨论】:

read 会让你读入一定数量的字符。读入足够的内容以填充缓冲区,然后将缓冲区转储到屏幕上,将其清除,然后重复直到到达文件末尾。【参考方案5】:

想到了两种方法。

首先,不要使用scanf。使用fgets(),它接受一个参数来指定缓冲区大小,并且保持任何换行符不变。打印缓冲区内容的文件的简单循环自然会完整地复制文件。

其次,使用fread() 或常见的C 习惯用法与fgetc()。它们会以固定大小的块或一次处理单个字符来处理文件。

如果您必须通过空格分隔的字符串处理文件,则使用fgetsfread 读取文件,并使用strtok 之类的东西在空格处分割缓冲区。不要忘记处理从一个缓冲区到下一个缓冲区的转换,因为您的目标字符串可能会跨越缓冲区边界。

如果有使用scanf 进行读取的外部要求,则使用格式说明符中的精度字段限制它可能读取的字符串的长度。在您使用 999 字节缓冲区的情况下,然后说 scanf("%998s", str); 最多将 998 个字符写入缓冲区,为 nul 终止符留出空间。如果允许长于缓冲区的单个字符串,则必须将它们分成两部分处理。如果没有,您有机会礼貌地告诉用户错误,而不会造成缓冲区溢出安全漏洞。

无论如何,始终验证返回值并考虑如何处理错误、恶意或格式错误的输入。

【讨论】:

【参考方案6】:

您可以使用fgets 并限制读取字符串的大小。

char *fgets(char *str, int num, FILE *stream);

您可以将代码中的while 更改为:

while (fgets(str, 100, file)) /* printf("%s", str) */;

【讨论】:

【参考方案7】:

您可以通过动态内存分配读取整个文件,但这不是一个好主意,因为如果文件太大,您可能会遇到内存问题。

所以最好阅读文件的一小部分并打印出来。

#include <stdio.h>
#define BLOCK   1000

int main() 
    FILE *f=fopen("teste.txt","r");
    int size;
    char buffer[BLOCK];
    // ...
    while((size=fread(buffer,BLOCK,sizeof(char),f)>0)
            fwrite(buffer,size,sizeof(char),stdout);
    fclose(f);
    // ...
    return 0;

【讨论】:

【参考方案8】:

您可以使用getline() 来读取您的文本文件,而不必担心大行:

bool read_file(const char *filename)

    FILE *file = fopen(filename, "r");
    if (!file)
        return false;
    
    char *line = NULL;
    size_t linesize = 0;

    while (getline(&line, &linesize, file) != -1)
        printf("%s", line);
    
    free(line);
    fclose(file);

    return true;

你可以这样使用它:

int main(void)

    if (!read_file("test.txt")) 
        printf("Error reading file\n");
        exit(EXIT_FAILURE);
    

【讨论】:

【参考方案9】:

我用这个版本

char* read(char* filename)
    FILE* f = fopen(filename, "rb");
    if (f == NULL)
        exit(1);
    
    fseek(f, 0L, SEEK_END);
    long size = ftell(f);
    fclose(f);
    f = fopen(filename, "r");
    void* content = memset(malloc(size), '\0', size);
    fread(content, 1, size, f);
    fclose(f);
    return (char*) content;

【讨论】:

以上是关于在 C 中,我应该如何读取文本文件并打印所有字符串的主要内容,如果未能解决你的问题,请参考以下文章

如何从单个文本文件中同时打印字符串和整数?

如何在 C 编程中使用 fgets 编写返回文本文件所有内容的函数?

如何从文本文件创建二维数组并在 c 中打印到新文件

如何读取文件并在c中打印

如何在 C 中使用参数从文本文件中打印字符?

python读取指定目录中所有文本文件的第一行,并以此为该文本文件名重命名