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


94

我有一个名为 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不起作用,因为byfscanf所返回的字符串可以大于该值。我该如何解决?

Answers:


134

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

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

cint上方,因为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()with %sas格式方法会丢失有关文件中空白的信息,因此它并不完全是将文件复制到stdout


可以从文件中读取数据而无需在c / c ++中打开该文件?
Sagar Patel 2015年

如果文本文件包含逗号分隔的整数值怎么办?而不是代码,您也可以在其中编辑答案。
Mohsin

以上适用于任何类型的文本文件。如果要解析CSV文件中的数字,那就是另一个问题。
阿洛克·辛哈尔

1
@overexchange这个问题不是关于行的,而是关于读取文件并将其内容复制到的stdout
Alok Singhal'1

1
@shjeff文件不能包含EOF字符。请注意,它c是int,并且C将保证它EOF不等于任何有效字符。
阿洛克·辛哈尔

60

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

我并不是说更好。并非如此,就像里卡多有时可能会很糟糕一样,但是我发现这对于简单的案例是一个不错的解决方案。

我添加了评论,因为有很多事情要做。

#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;
}

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


2
难道不应该读buffer[string_size] = '\0';,而不是string_size+1?Afaik实际的字符串从0string_size-1,因此\0字符必须在string_size,对吗?
aepsil0n 2014年

4
使用ftellfseek查找文件的大小是不安全的:securecoding.cert.org/confluence/display/seccode/…–
Joakim

1
此代码包含内存泄漏,您永远不会关闭文件。失踪了fclose(handle)
乔阿基姆(Joakim)

1
有一个错字,您称fclose(handle),应该是fclose(handler)
Eduardo Cobuci

3
您可以使用calloc(2)而不是malloc(1)跳过必须设置空终止符的方式。

14

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

#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;
}

6

使用“ read()”代替fscanf:

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

描述

read()函数应尝试将nbyte与打开的文件描述符关联的文件中的字节读取fildes到所指向的缓冲区中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将允许您阅读一定数量的字符。读入足够的内容以填充缓冲区,然后将缓冲区转储到屏幕上,将其清除,然后重复直到到达文件末尾。
2010年

1

有两种方法可供考虑。

首先,不要使用scanf。使用fgets()带有参数的参数来指定缓冲区大小,并保留所有换行符。在打印缓冲区内容的文件上进行的简单循环自然应该完整地复制文件。

其次,将fread()或常见的C习语与一起使用fgetc()。这些将以固定大小的块或一次单个字符处理文件。

如果必须使用空格分隔的字符串处理文件,请使用fgetsfread读取文件,并使用类似的方法在空白处strtok分割缓冲区。不要忘记处理从一个缓冲区到下一个缓冲区的过渡,因为目标字符串很可能跨越缓冲区边界。

如果需要外部scanf读取,请使用格式说明符中的precision字段限制可能读取的字符串的长度。如果您使用的是999字节的缓冲区,请说出scanf("%998s", str);哪个将最多998个字符写入缓冲区,为nul终止符留出空间。如果允许使用比缓冲区更长的单个字符串,那么您必须将它们分为两部分进行处理。如果没有,您可以有礼貌地告诉用户有关错误的信息,而不会造成缓冲区溢出安全漏洞。

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


1

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

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

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

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

0

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

因此,最好阅读文件的短部分并进行打印。

#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;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.