C/C++ 语法教程(13)——文件输入输出

C/C++ 语法教程(13)——文件输入输出

Contents

文件输入输出

很多时候,输入输出并不在命令行中进行,我们会需要直接对数据文件进行读写操作,这就需要用到各类文件输入输出的方法。

C 文件读写

freopen() 方法

函数原型为:

FILE *freopen(const char *filename, const char *mode, FILE *stream);

filename 就是需要打开的文件的名字,moode 是文件访问模式,包括以下几种:

模式描述
"r"打开一个用于读取的文件。该文件必须存在。
"w"创建一个用于写入的空文件。如果文件名称与已存在的文件相同,则会删除已有文件的内容,文件被视为一个新的空文件。
"a"追加到一个文件。写操作向文件末尾追加数据。如果文件不存在,则创建文件。
"r+"打开一个用于更新的文件,可读取也可写入。该文件必须存在。
"w+"创建一个用于读写的空文件。
"a+"打开一个用于读取和追加的文件。

一般使用前两种。

stream 是指向 FILE 对象的指针,该 FILE 对象标识了要被重新打开的流,常用的就是 stdin 标准输入流和 stdout 标准输出流。

freopen("a.txt","r",stdin); 这样一个语句的作用,就是将原来通过标准输入流 stdin 输入的函数、流转为从文件 a.txt 中读入,比如 scanf()getchar() 等等函数。

同理,改成 "w" 就可以将 printf()puts()putchar() 等函数改为输出到 a.txt

具体可以看一下重定向到 stdout 的例子:

#include <stdio.h>

int main ()
{
    FILE *fp;

    printf("该文本重定向到 stdout\n");

    fp = freopen("file.txt", "w+", stdout);

    printf("该文本重定向到 file.txt\n");

    fclose(fp);

    return 0;
}

最后 fclose() 这个 fp 流,相当于关闭这个文件并保存。

这也是一般 OI 比赛中,选手们较为常用的方式。

然而这种重定向的方法往往会覆盖 stdinstdout 这两个常用的输入输出流,而且即便经过了 fclose(),这个重定向也不会还原,所以不建议使用这种重定向方法

fopen() 方法

函数原型为:

FILE *fopen( const char * filename, const char * mode );

这里的 filenamemode 的值与含义与上面 freopen 中所说的一样。

它们的区别是,freopen() 是以特定模式打开一个文件,并将其重定向到某个流上,而 fopen() 只是以特定模式打开了文件,对其的操作还需要另外的函数。

同样,想关闭 fopen 打开的文件,也需要使用 fclose() 函数。

接下来介绍一下对特定文件流(FILE *)的读写函数。

写入文件

最简单的函数是:

int fputc( int c, FILE *fp );

使用方法也就是 fputc('a',fp); ,跟 putchar() 的区别就是加了一个文件流的参数(也就是 fopen() 后获得的返回值)。

除此之外,还有:

int fputs( const char *s, FILE *fp );
int fprintf( FILE *fp,const char *format, ...);

两函数跟 puts()printf 的区别只是在不同的位置加上了文件流的参数。

举个例子:

#include <stdio.h>

int main()
{
    FILE *fp = NULL;

    fp = fopen("/tmp/test.txt", "w+");
    fprintf(fp, "This is testing for fprintf...\n");
    fputs("This is testing for fputs...\n", fp);
    fputc('*', fp);
    fclose(fp);

    return 0;
}

读取文件

最简单的函数是:

int fgetc( FILE *fp );

使用方法也就是 ch=fgetc(fp); ,跟 getchar() 的区别就是加了一个文件流的参数(也就是 fopen() 后获得的返回值)。

除此之外,还有:

char *fgets( char *buf, int n, FILE *fp );
int fscanf( FILE *fp, const char *format, ...);

函数 fgets()fp 所指向的输入流中读取 $n - 1$ 个字符。它会把读取的字符串复制到缓冲区 buf,并在最后追加一个 null 字符来终止字符串。

如果这个函数在读取最后一个字符之前就遇到一个换行符 '\n' 或文件的末尾 EOF,则只会返回读取到的字符,包括换行符。

当然 fgets()gets() 一样不建议使用,fscanf()scanf() 差距同样只是一个文件流参数。

再举个例子:

#include <stdio.h>

int main()
{
    FILE *fp = NULL;
    char buff[255];

    fp = fopen("/tmp/test.txt", "r");
    fscanf(fp, "%s", buff);
    printf("1: %s\n", buff );

    fgets(buff, 255, (FILE*)fp);
    printf("2: %s\n", buff );

    fgets(buff, 255, (FILE*)fp);
    printf("3: %s\n", buff );
    fclose(fp);

    return 0;
}

其读取上一部分创建的文件,输出为:

1: This
2: is testing for fprintf...

3: This is testing for fputs...

二进制 I/O 函数

下面两个函数用于二进制输入和输出:

size_t fread(void *ptr, size_t size_of_elements,
             size_t number_of_elements, FILE *a_file);
size_t fwrite(const void *ptr, size_t size_of_elements,
              size_t number_of_elements, FILE *a_file);

这两个函数都是用于存储块的读写 - 通常是数组或结构体。

这个可以自己研究,先留个坑。

C++ 文件与流

C++ 的流

数据类型描述
ofstream该数据类型表示输出文件流,用于创建文件并向文件写入信息。
ifstream该数据类型表示输入文件流,用于从文件读取信息。
fstream该数据类型通常表示文件流,且同时具有 ofstreamifstream 两种功能,这意味着它可以创建文件,向文件写入信息,从文件读取信息。

一般使用上述流进行文件处理,需要同时包含头文件 iostreamfstream

打开文件

首先对于这三种流,都具备成员函数(类似于上一讲的成员变量)open()

void open(const char *filename, ios::openmode mode);

filename 当然还是文件(目录和)名称,但是 mode 和之前有所不同:

模式标志描述
ios::app追加模式。所有写入都追加到文件末尾。
ios::ate文件打开后定位到文件末尾。
ios::in打开文件用于读取。
ios::out打开文件用于写入。
ios::trunc如果该文件已经存在,其内容将在打开文件之前被截断,即把文件长度设为 $0$。

如果需要使用多种模式的结合,可以使用 | 运算符将其结合。

举两个例子:

ofstream outfile;
outfile.open("file1.dat", ios::out | ios::trunc );

fstream  afile;
afile.open("file2.dat", ios::out | ios::in );

特别地,如果在定义流时,便知道需要打开的文件,比如需要向 1.in 中读取信息,向 1.out 中写入信息,可以直接这么打开文件:

ifstream fin("1.in");
ofstream fout("1.out");

关闭文件

同样这些流有一个成员函数 close()

void close();

只需要使用以下方法即可关闭文件:

outfile.close();
afile.close();

写入文件

可以使用 ofstreamfstream,然后使用 << 运算符,用和 cout 几乎没有区别的方法写入。

比如对于上述中的 outfile 流:

outfile<<"Hello World!"<<endl;

读取文件

可以使用 ifstreamfstream,然后使用 >> 运算符,用和 cin 几乎没有区别的方法写入。

比如对于上述中的 afile 流:

int a,b;
afile>>a>>b;

一个实例

#include <fstream>
#include <iostream>
using namespace std;

int main ()
{
    char data[100];

    // 以写模式打开文件
    ofstream outfile;
    outfile.open("afile.dat");

    cout << "Writing to the file" << endl;
    cout << "Enter your name: "; 
    cin.getline(data, 100);

    // 向文件写入用户输入的数据
    outfile << data << endl;

    cout << "Enter your age: "; 
    cin >> data;
    cin.ignore();

    // 再次向文件写入用户输入的数据
    outfile << data << endl;

    // 关闭打开的文件
    outfile.close();

    // 以读模式打开文件
    ifstream infile; 
    infile.open("afile.dat"); 

    cout << "Reading from the file" << endl; 
    infile >> data; 

    // 在屏幕上写入数据
    cout << data << endl;

    // 再次从文件读取数据,并显示它
    infile >> data; 
    cout << data << endl; 

    // 关闭打开的文件
    infile.close();

    return 0;
}

上述程序在命令行产生的结果可能是:

Writing to the file
Enter your name: Zara
Enter your age: 9
Reading from the file
Zara
9

上面的程序还使用了 cingetline()ignore 成员函数,前者用于读取一行(第二个参数是对于读取字符数的限制,如果超过了即便没有换行也停止读入),后者用于忽略之前读入语句后的多余字符。

对于这两个成员函数的更多使用可以自行百度。

文件位置指针

包括 ifstreamseekg()ofstreamseekp(),相关信息自行了解。

关于输入文件流的特别操作

如何判断文件结束?

一般通过 >> 运算符读入的流(包括 cin),会有一个返回值表示是否读入成功。

每个文件都会有一个结束标志符 EOF,如果文件结束,则读入 EOF 并返回 $0$。

所以可以通过 if (fin>>str) 等类似方法判断文件结束(输入是否成功)。

理论上 ifstream 流和 fstream 流还有一个专门的函数 eof() 判断文件结束,但对于文章末尾的换行符配合读入字符串的读入使用时,往往会出现问题,所以不建议使用。

习题

  1. 给定一个文件,由大小写英文和标点符号组成。依照 ASCII 值的从小到大的顺序,统计输出各字符的数量。

    input.txt 读入。

    文件中包含大小写英文、标点符号、空格等 ASCII 码可见字符,以及换行符、tab 等。

    输入文件小于 $100\ \mathrm{kb}$。

    输出到 output.txt 中。

    依照 ASCII 值的大小,统计输出各字符的数量。

    其中,没有出现的字符不用输出,不可见字符(换行符、tab 等)不用统计,空格不用统计。

    输出格式每行为 x:y,其中 x 为对应字符,y 为数量。具体格式参考见样例输出。

    样例输入

    Hello World! hahaha!
    

    样例输出

    !:2
    H:1
    W:1
    a:3
    d:1
    e:1
    h:3
    l:3
    o:2
    r:1
    
    
  2. 给定一个文件,里面为一篇英文文章。要求将文章中的每句话颠倒输出。每句话以英文句号(.)为分隔。

    input.txt 读入。

    文件包含大小写英文字符、标点符号、空格、换行符。每句话长度小于 $100000$。

    输入文件小于 $100\ \mathrm{kb}$。

    输入保证每句话都以句号结束。

    输出到 output.txt 中。

    将每句话颠倒后输出。

    输出时去掉所有空格和换行。

    样例输入

    Hi,Tsinghua.This is a pen.
    Ha ha ha.
    

    样例输出

    auhgnisT,iH.nepasisihT.ahahaH.
    

 

点赞 1

No Comments

Add your comment