c++文件的输入和输出

文件的输入和输出

​ 要对文件进行读写操作,首先要包含fstream头文件:

 #include  <fstream>

一、文件的输入

1. 文件的打开

​ 为了打开一个可供输出的文件,有两种打开方式,我们定义一个ofstream(供输出用的file stream)对象,并将文件名传入:

方式1:直接初始化打开:

// 以输出模式开启test.txt
ofstream outfile("test.txt");

方式2:使用open()方法:

ofstream outfile();
outfile.open("test.txt");

​ 声明 outfile 的同时,会发生什么事?如果指定的文件并不存在,便会有一个文件被产生出来并打开供输出使用。如果指定的文件已经存在,这个文件会被打开用于输出,而文件中原有的数据会被丢弃。

2. 文件的插入

​ 如果文件已经存在,但我们并不希望丢弃其原有内容,而是希望将新数据增加到该文件中,那么我们必须以追加模式(append mode)打开这个文件。为此,我们提供第二个参数 ios_base::app 给 fstream 对象。

//以追加模式(append mode) 打开test.txt

//新数据会被加到文件末尾

 ofstream outfile ("test.txt", ios_base::app);

3. 判断文件是否成功打开

​ 文件有可能打开失败。在进行写入操作之前,我们必须确定文件打开成功。新式的c++用以下方式检验文件是否打开成功:

if (!outfile.is_open)

​ 一般都采取以上的检查方式,因为它能够检测出其他方式不能检测出的微妙问题。

​ 老式的c++实现没有is_open方法,则采取以下方式:

if (!outfile) ...
if (!outfile.fail()) ...
if (!outfile.good()) ...

​ 上面三种检查方式等价,但是这些检查方式无法检测到这样一种情况:试图以不合适的文件模式打开文件时失败。方法is_open()能够检测到这种错误以及good()能够检测到的错误。

程序实例:

#include <iostream>
#include <fstream>
#include <cstdlib>

using namespace std;

int main()
{
    string usr_name;
    int num_tries = 0;
  	int num_right = 0;
    ofstream outfile("test.txt");
    if (!outfile.is_open())
    {
        cerr << "Could not open file" <<  endl;
        exit(EXIT_FAILURE);
    } 
    else
    {
        outfile << usr_name << " "
                << num_tries << " "
                << num_right << endl;
    }
        
    return 0;
}

​ 如果文件顺利打开,我们便将输出信息定向到该文件,就像将信息写入 cout 及 cerr 这两个 ostream 对象一样。本例中,我们将三个数值写入 outfile,并以空格区分后两个数值。endl 是事先定义好的所谓操纵符(manipulator),由 iostream library 提供。

二、文件的输出

1. 文件的打开

​ 如果要打开一个可供读取的文件,我们就定义一个fstream(供输入的file stream)对象,并将文件名传入。如果文件成功打开,该文件的位置会被设定在起始处,同样的,打开文件也是有两种方式:

方式1:直接初始化打开:

// 以读取模式开启test.txt
ifstream infile("test.txt");

方式2:使用open()方法:

ifstream infile();
ifstream.open("test.txt");

2. 判断文件是否成功打开

判断文件是否成打开的方式同上。

3. 文件名格式要求

​ 打开文件输入的文件名必须是c-style的字符串,如果文件名是string对象,则需要使用c_str()方法转换成一个c-style的字符串。

string filename = "test.txt"
ifstream infile();
ifstream.open(filename.c_str());

文件输入输出的完整程序实例:

#include <iostream>
#include <fstream>
#include <cstdlib>

int main(int argc, char *argv[])
{
    using namespace std;
    char ch;
    if (argc < 3)
    {
        cerr << "Usage: " << argv[0] << " filename\n";
        exit(EXIT_FAILURE);
    }
    ifstream fin(argv[1]);

    if (!fin.is_open())
    {
        cerr << "Could not open " << argv[1] << endl;
        exit(EXIT_FAILURE);
    }
    ofstream fout(argv[2]);
    if (!fout.is_open())
    {
        cerr << "Could not open " << argv[2] << endl;
        exit(EXIT_FAILURE);
    }
    while (fin.get(ch))
    {
        fout << ch;
    }
    fin.close();
    fout.close();
    cout << "Copy done.\n";
    return 0;

}

输出:

edwin@edwindeMacBook-Pro testProjects % ./17-3 test_in.txt test_out.txt
Copy done.

​ 以上程序使用了命令行参数来指定文件。命令行参数是用户在输入命令时,在命令行中输入的参数。

比如程序运行时,输入了两个参数:./17-3 test_in.txt test_out.txt。“test_in.txt ”是要读取的文件名,“test_out.txt”为要写入文件的名字。

4. 文件的关闭

​ 需要注意的是,当文件操作结束后,需要进行关闭:

fin.close();
fout.close();

有些c++实现要求在程序结尾使用fin.clear(),有些则不要求,这取决于将文件与ifstream对象关联起来时,是否重置流状态。使用fin.clear()是无害的,即使不必使用它的时候使用。

5. 使用临时文件

​ 开发应用程序时,经常需要使用临时文件,这种文件的存在是短暂的,必须受程序控制。您是否考虑过,在C++中如何使用临时文件呢?创建临时文件、复制另一个文件的内容并删除文件其实都很简单。首先,需要为临时文件制定一个命名方案,但如何确保每个文件都被指定了独一无二的文件名呢?cstdio中声明的tmpnam()标准函数可以帮助您。

char* tmpnam( char* pszName );

​ tmpnam()函数创建一个临时文件名,将它放在pszName指向的C-风格字符串中。常量L_tmpnam和TMP_MAX(二者都是在cstdio中定义的)限制了文件名包含的字符数以及在确保当前目录中不生成重复文件名的情况下tmpnam()可被调用的最多次数。下面是生成10个临时文件名的代码。

#include <cstdio>
#include <iostream>
int main()
{
    using namespace std;
    cout << "This system can generate up to " << TMP_MAX <<
         <<" temporary names of up to " << L_tmpnam
         <<" characters.\n";
    char pszName [L_tmpnam] = {'\0'};
    cout <<"Here are ten names:\n";
    for(inti=0; 10>i; i++)
    {
        tmpnam(pszName);
        cout << pszName << endl;
    }
    return 0;
}

​ 更具体地说,使用tmpnam()可以生成TMP_NAM个不同的文件名,其中每个文件名包含的字符不超过L_tmpnam个。生成什么样的文件名取决于实现,您可以运行该程序,来看看编译器给您生成的文件名。

三、文件模式

​ 文件模式描述的是文件将被如何使用:读、写、追加等。将流与文件关联时(无论是使用文件名初始化文件流对象,还是使用open()方法),都可以提供指定文件模式的第二个参数:

ifstream infile("test.txt", mode1);
ofstream outfile();
outfile.open("test.txt", mode2);

​ ios_base类定义了一个openmode类型,用于表示模式;它是一种bitmask类型(以前其类型为int)。可以选择ios_base类中定义的多个常量来指定模式,下表列出了这些常量及其含义。

image-20210619151713200

​ 如果ifstream和ofstream构造函数以及open()方法都接受两个参数,为什么前面的例子只使用一个参数就可以调用它们呢?您可能猜到了,这些类成员函数的原型为第二个参数(文件模式参数)提供了默认值。例如,ifstream open()方法和构造函数用ios_base::in(打开文件以读取)作为模式参数的默认值,而ofstream open()方法和构造函数用ios_ base::out|ios_base::trunc(打开文件,以读取并截短文件)作为默认值。位运算符OR(|)用于将两个位值合并成一个可用于设置两个位的值。fstream类不提供默认的模式值,因此在创建这种类的对象时,必须显式地提供模式。
​ 注意,ios base::trunc标记意味着打开已有的文件,以接收程序输出时将被截短;也就是说,其以前的内容将被删除。虽然这种行为极大地降低了耗尽磁盘空间的危险,但您也许能够想象到这样的情形,即不希望打开文件时将其内容删除。当然,C++提供了其他的选择。例如,如果要保留文件内容,并在文件尾添加(追加)新信息,则可以使用ios_ base::app模式:

ofstream outfile("test.txt", ios_base::out|ios_base::app);

​ 上述代码也使用|运算符来合并模式,因此ios_base::out|ios_base::app意味着启用模式out和app。

同时读写同一文件

​ 如果想要同时读写同一个文件,我们得定义一个fstream对象。为了以追加模式打开,我们得传入第二个参数值 ios_base::in|ios_base::app。

#include <iostream>
#include <fstream>
#include <cstdlib>

using namespace std;

int main()
{
    string usr_name;
    int num_tries = 0;
  	int num_right = 0;
    fstream iofile("test.txt", ios_base::in|ios_base::app);
    if (!iofile.is_open())
    {
        cerr << "Could not open file" <<  endl;
        exit(EXIT_FAILURE);
    } 
    else
    {
      	// 开始读取之前,将文件重新定位到起始处
      	iofile.seekg(0);
      	// 开始读取
      	iofile >> usr_name;
      	// 开始写入
        iofile << usr_name << " "
                << num_tries << " "
                << num_right << endl;
    }
        
    return 0;
}

​ 当我们以追加模式来打开档案,文件位置会位于末尾。如果我们没有先重新定位,就试着读取文件内容,那么立刻就会遇上“文件结束”的状况。seekg()可将iofile 重新定位至文件的起始处。由于此文件是以追加模式开启,因此任何写入操作都会将数据添加在文件末尾。

c++文件模式与c文件模式的区别

​ c++mode是一个openmode值,如ios_base::in;而cmode是相应的C模式字符串,如“r”。下表列出了c++文件模式与c文件模式的对应关系。

image-20210619153329739

注意,ios_base::ate与ios_base::app都将文件指针指向打开的文件尾。二者的区别在于,ios_base::app模式只允许将数据添加到文件尾,而ios_base::ate模式将指针放到文件尾。

posted on 2021-06-20 23:08  xufat  阅读(2433)  评论(0编辑  收藏  举报

导航

/* 返回顶部代码 */ TOP