C++ 文件流操作详解

1. C++ I/O流

本文章有很多内容参考并借鉴了《C++ primer plus》 这本经典。这里先说明一下。

  • 1. C++ I/O流
    • 1.1. 数据流
    • 1.2. 控制台流
    • 1.3. 文件流
      • 1.3.1. 什么是文件流?
      • 1.3.2. 缓冲区
      • 1.3.3. 文件流和控制流的关系
      • 1.3.4. 文件处理
      • 1.3.5. 简单的文件I/O
      • 1.3.6. 流状态检查和is_open()
      • 1.3.7. 打开文件和open()
      • 1.3.8. 打开多个文件和clear()
      • 1.3.9. 二进制文件
      • 1.3.10. 随机存取
    • 1.4. 字符串流
    • 1.5. 总结

1.1. 数据流

数据流(data stream)是一组有序,有起点和终点的字节的数据序列。包括输入流和输出流。

c++通过一种称为流(stream)的机制提供了更为精良的输入和输出方法。流是一种灵活且面向对象的I/O方法。

什么是流?
C++程序把输入和输出看作字节流。输入时,程序从输入流中抽取字节;输出时,程序将字节插入到输出流中。对于面向文本的程序,每个字节代表一个字符,更通俗地说,字节可以构成字符或数值数据的二进制表示。

流充当了程序和流源或流目标之间的桥梁。这使得C++程序可以以相同的方式对待来自键盘的输入和来自文件的输入。C++程序只是检查字节流,而不需要知道字节来自何方。同理,通过使用流,C++程序处理输出的方式将独立于其去向。

根据操作对象不同分为文件流、字符串流、控制台流。


1.2. 控制台流

C++输入输出操作分别是由istream(输入流)和ostream(输出流)这两个类提供的,为了允许双向的输入/输出,由istream和ostream派生出了iostream类。

函数功能应用
cin输入istream类对象从设备读入数据
cout输出ostream类对象向设备输出或写入数据
cerr标准错误的ostream类对象屏幕设备输出数据

上面对象的 << 和 >> 操作符,是因为iostream.h头文件中,ostream类和 istream类对应每个基本数据类型都有其友元函数对流插入操作符进行了友元函数的重载。所以对于所有的流对象来说,流插入操作符都是可以被使用的。


1.3. 文件流

1.3.1. 什么是文件流?

大多数计算机程序都使用了文件。字处理程序创建文档文件;数据库程序创建和搜索信息文件;编译器读取源代码文件并生成可执行文件。

文件本身是存储在某种设备(磁带、光盘、软盘或硬盘)上的一系列字节。

文件流是将程序与文件相连、让程序读取文件内容以及让程序创建和写入文件的途径。

文件输入流需要两个连接,每端各一个。文件端部连接提供了流的来源,程序端连接将流的流出部分转储到程序中(文件端连接可以是文件,也可以是设备,如键盘)。

同样,对输出的管理包括将输出流连接到程序以及将输出目标与流关联起来。这就像将字节(而不是水)引入到水管中。

1.3.2. 缓冲区

通常,通过使用缓冲区可以更高效地处理输入和输出。

缓冲区是用作中介的内存块,它是将信息从设备传输到程序或从程序传输给设备的临时存储工具。

缓冲方法则从磁盘上读取大量信息,将这些信息存储在缓冲区中,然后每次从缓冲区里读取一个字节。

因为从内存中读取单个字节的速度比从硬盘上读取快很多,所以这种方法更快,也更方便。当然,到达缓冲区尾部后,程序将从磁盘上读取另一块数据。

输出时,程序首先填满缓冲区,然后把整块数据传输给硬盘,并清空缓冲区,以备下一批输出使用。这被称为刷新缓冲区(flushing the buffer)。

键盘输入每次提供一个字符,因此在这种情况下,程序无需缓冲区来帮助匹配不同的数据传输速率。

然而,对键盘输入进行缓冲可以让用户在将输入传输给程序之前返回并更正。C++程序通常在用户按下回车键时刷新输入缓冲区。

1.3.3. 文件流和控制流的关系

文件流的输入和输出类在fstream头文件中被定义,和控制流继承的关系为
如图:
在这里插入图片描述

C++ I/O类软件包处理文件输入和输出的方式与处理标准输入和输出的方式非常相似。

要写入文件,需要创建一个ofstream对象,并使用ostream方法,如<<插入运算符或write()。

要读取文件,需要创建一个ifstream对象,并使用istream方法,如>>抽取运算符或get()。

然而,与标准输入和输出相比,文件的管理更为复杂。

1.3.4. 文件处理

C++的文件处理也可以看成一个对象使用文件流的类,使用头文件istream,处理的时候有文本文件和二进制文件之分,主要的区别就是存储的形式。

C++通过以下几个类支持文件的输入输出︰

ofstream: 写操作(输出)的文件类(由ostream引申而来)

ifstream: 读操作(输入)的文件类(由istream引申而来)

fstream: 可同时读写操作的文件类(由iostream引申而来)

要在C++中进行文件处埋,必须包含头文件
iostream和fstream。

1.3.5. 简单的文件I/O

要让程序写入文件,必须这样做:

  1. 创建一个ofstream对象来管理输出流;

  2. 将该对象与特定的文件关联起来;

  3. 以使用cout的方式使用该对象,唯一的区别是输出将进入文件,而不是屏幕。

读取文件内容与写入文件相似:

  1. 创建一个ifstream对象来管理输入流;

  2. 将该对象与特定的文件关联起来;

  3. 以使用cin的方式使用该对象。

下面是ofstream/ifstream类的默认构造函数原型为:


ofstream/ifstream(const char*filename, int mode=ios::out|ios::trunc, int penprot=filebuf::openprot);

参数说明

如图:

参数值来源
filename要打开的文件名
mode要打开的文件模式(ios::out)
prot打开文件属性(filebuf::openprot)

mode属性表

如图:

类型解释
ios::app追加方式打开文件
ios::ate文件打开后定位到文件尾
ios::binary以二进制格式存储打开文件,默认为文本格式
ios::out文件以输入方式打开
ios::in文件以输出方式打开
ios::nocreate不建立文件,文件不存在时打开失败
ios::noreplace不覆盖文件,文件存在时打开失败

打开文件属性值

如图:

类型解释
0普通文件,用于访问
1只读文件
2隐藏文件
4系统文件

注意:

ios_base::ate和ios_base::app都将文件指针指向打开的文件尾。

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

程序写入文件案例:

#include<fstream>  //首先包含头文件fstream
#include<iostream>  
//对于大多数(但不是全部)实现来说
//包含该文件便自动包括iostream文件
//因此不必显示包含iostream。
//但为了代码方便移植,建议加上int main()
{using namespace std;ofstream outf;//然后声明一个ofstream对象//必须将这个对象与特定的文件关联起来。//为此,可以使用构造函数//将这两步(创建对象和关联到文件)合并成一条语句//文件名后面使用函数默认的参数ios::out|ios::truncofstream outf("file.txt");//outf.open("file.txt");  //也可以使用open()方法。(后面会详讲)//然后,以使用cout的方式使用outf//假设我们要将"I love China"写入文件outf<<"I love China"<<endl;outf.close();  //关闭文件return 0;
}

注意:
由于ostream是ofstream类的基类,因此可以使用所有的ostream方法,包括各种插入运算符定义、格式化方法和控制符。

且以这种方式打开文件来进行输出时,如果没有这样的文件,将创建一个新文件;如果有这样的文件,则打开文件将清空文件,输出将进入到一个空文件中。

以默认模式打开文件进行输出将自动把文件的长度截短为零,这相当于删除已有的内容。

程序读取文件案例:

#include<iostream>
#include<fstream>int main()
{using namespace std;//首先,当然要包含头文件fstream。//然后声明一个ifstream对象,将它与文件名关联起来。ifstream inf("file.txt");//现在,可以像使用cin那样使用fin或fis。char ch[80];inf>>ch;cout<<ch<<endl;string s;inf>>s;cout<<s<<endl;inf.close();  //关闭文件
}

注意:
输入和输出一样,也是被缓冲的,因此创建ifstream对象与fin一样,将创建一个由fin对象管理的输入缓冲区。

当输入和输出流对象过期(如程序终止)时,到文件的连接将自动关闭。

另外,一般使用close()方法来显式地关闭到文件的连接。
写程序的时候应该养成成一种好习惯,再程序终止之前关闭所有打开文件。

outf.close();
inf.close();

关闭这样的连接并不会删除流,而只是断开流到文件的连接。

1.3.6. 流状态检查和is_open()

C++文件流类从ios_base类那里继承了一个流状态成员。

该成员存储了指出流状态的信息:一切顺利、已到达文件尾、I/O操作失败等。

如果一切顺利,则流状态为零(没有消息就是好消息)。其他状态都是通过将特定位设置为1来记录的。

可以通过检查流状态来判断最后一个流操作是否成功。对于文件流,这包括检查试图打开文件时是否成功。

流状态检测案例:

fin(argv[file]);//fali()
if(fin.fail()){...
}//!fin
//由于ifstream对象和istream对象一样
//被放在需要bool类型的地方时
//将被转换为bool值,因此也可以这样做
if(!fin){...
}//fin.good()
//fin对象被用于测试条件时
//如果文件操作正常,返回true,否则返回false
if(!fin.good()){}//is_open()
//然而,较新的C++实现提供了一种更好的检查文件是否被打开的方法
//这种方式之所以更好
//是因为它能够检测出其他方式不能检测出的微妙问题
if(!fin.is_open()){...
}

注意:
这些测试无法检测到这样一种情形:试图以不合适的文件模式打开文件时失败。

方法is_open()能够检测到这种错误且包括good()能够检测到的错误。但老式C++实现没有is_open()。

1.3.7. 打开文件和open()

在从文件读取信息或者向文件写入信息之前,必须先打开文件。

ofstream和fstream对象都可以用来打开文件进行写操作,如果只需要打开文件进行读操作,则使用ifstream对象。

下面是open()函数的标准语法,open()函数是fstream、ifstream和ofstream对象的一个成员。


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

open()成员函数的第一参数指定要打开的文件的名称和位置,第二个参数定义文件被打开的模式。

如图:

模式标志描述
ios::app追加模式,所有写入都追加到文件末尾
ios::ate文件打开后定位到文件尾
ios::in打开文件用于读取
ios::out打开文件用于写入
ios::trunc如果文件已经存在,打开文件前文件内部的内容将被清空,文件宽度设为0

可以把以上两种或两种以上的模式结合使用。例如,如果想要以写入模式打开文件,并希望截断文件,以防文件已存在,那么可以使用下面的语法:

fstream outf;
outf.open("file.txt",ios::out|ios::trunc);

如果想要打开一个文件用于读写,可以使用下面的语法:


fstream file;
file.open("file.txt",ios::out|ios::in);

当然:
ofstream,ifstream和fstream所有这些类的成员函数open都包含了一个默认打开文件的方式。
如图:

默认方式
ofstreamios::out | ios::trunc
ifstreamios::in
fstreamios::in | ios::out

案例:


#include <iostream>
#include <fstream>
#include <string>int main()
{using namespace std;ofstream outf;if(outf.is_open()){cout<<"文件打开失败"<<endl;return 0;}outf.open("file.txt", ios::out|ios::trunc);outf << "千山鸟飞绝,万径人踪灭。" << endl;outf << "孤舟蓑笠翁,独钧寒江雪。" << endl;outf.close();ifstream inf;if(inf.is_open()){cout<<"文件打开失败"<<endl;return 0;}inf.open("file.txt", ios::in);string s;// 逐行读取文件内容while ( getline(inf, s) ){cout << s << endl;}//逐行读取文件内容//while(inf>>s){//    cout<<s<<endl;//}//逐字符读取文件内容//char ch;//while ( inf.get(ch) )//{//    cout << ch;//}//逐行读取和逐字符读取两者效果一样,区别在于读取方式。inf.close();return 0;
}

1.3.8. 打开多个文件和clear()

程序可能需要打开多个文件。打开多个文件的策略取决于它们将被如何使用。

如果需要同时打开两个文件,则必须为每个文件创建一个流。

例如,将两个排序后的文件拼接成第三个文件的程序,需要为两个输入文件创建两个ifstream对象,并为输出文件创建一个ofstream对象。可以同时打开的文件数取决于操作系统。

然而,可能要依次处理一组文件。例如,可能要计算某个名称在10个文件中出现的次数。在这种情况下,可以打开一个流,并将它依次关联到各个文件。这在节省计算机资源方面,比为每个文件打开一个流的效率高。

案例1:


#include<iostream>
#include<fstream>
#include<string>//默认file已经存在
const char* file="file.txt";const char* file1="file1.txt";
int main(){using namespace std;fstream ff.open(file1,ios::out|ios::trunc);if(!f.is_open()){cout<<"open file1 fail"<<endl;return 0;}string contect;cout<<"intput"<<endl;cin>>contect;f<<contect;//断开f与当前流的连接f.close();f.clear();//重新连接另一个流f.open(file,ios::in);if(f.is_open()){while(getline(f,contect)){cout<<contect<<endl;}}return 0;
}

案例2:


#include<iostream>
#include<fstream>
#include<string>
#include<cstdlib>const char* file="guests.txt"int main(){using namespace std;char ch;ifstream fin;fin.open(file);//输出文件内刚开始的内容if(fin.is_open()){cout<<"文件内容:"<<endl;while(fin.get(ch)){cout<<ch;}fin.close();}//添加新内容ofstream fout(fin,ios::out|ios::app);if(!fout.is_open()){cerr<<"fail"<<endl;return 0;}cout<<"输入:"<<endl;string name;while(getline(cin,name) &&name.size()){fout<<name<<endl;}fout.close();//输出文件更新后的内容//重置流状态//防止打不开文件fin.clear();fin.open("file");if(fin.is_open()){cout<<"文件更新后的内容:"<<endl;}while(getline(fin,name)){cout<<name<<endl;}fin.close();cout<<"Done.\n";return 0;}

注意:
在早期,文件I/O可能是C++最不标准的部分,很多老式编译器都不遵守当前的标准。

例如,有些编译器使用诸如nocreate等模式,而这些模式不是当前标准的组成部分。另外,只有一部分编译器要求在第二次打开同一个文件进行读取之前调用clear()

有些C++实现要求在重用的流前,需使用clear()来重置流状态,有些则不要求,这取决于将文件与对象关联起来时,是否自动重置流状态。使用clear()是无害的,即使在不必使用它的时候也使用。

1.3.9. 二进制文件

将数据存储在文件中时,可以将其存储为文本格式或二进制格式。

文本格式指的是将所有内容(甚至数字)都存储为文本。

二进制格式指的是存储值的计算机内部表示。

也就是说,计算机不是存储字符,而是存储这个值的二进制表示。对于字符来说,二进制表示与文本表示是一样的,即字符的ASCII码的二进制表示。对于数字来说,二进制表示与文本表示有很大的差别。

如图:
在这里插入图片描述

每种格式都有自己的优点。

文本格式便于读取,可以使用编辑器或字处理器来读取和编辑文本文件,可以很方便地将文本文件从一个计算机系统传输到另一个计算机系统。

二进制格式对于数字来说比较精确,因为它存储的是值的内部表示,因此不会有转换误差或舍入误差。以二进制格式保存数据的速度更快,因为不需要转换,并可以大块地存储数据。二进制格式通常占用的空间较小,这取决于数据的特征。

然而,如果另一个系统使用另一种内部表示,则可能无法将数据传输给该系统。同一系统上不同的编译器也可能使用不同的内部结构布局表示。

我们来看个例子:


struct people{char name[20];int age;long secret;
}p;

要将结构p的内容以文本格式保存,可以这样做:


ofstream outf("people.txt",ios::out|ios::app);outf<<p.name<<" "<<p.age<<" "<<p.secret<<endl;

必须使用成员运算符显式地提供每个结构成员,还必须将相邻的数据分隔开,以便区分。如果结构有30个成员,则这项工作将很乏味。

要用二进制格式存储相同的信息,可以这样做:


ofstream outf("people.txt",ios::out|ios::app|ios::binary);outf.write((char*)&p,sizeof(p));

上述代码使用计算机的内部数据表示,将整个结构作为一个整体保存。不能将该文件作为文本读取,但与文本相比,信息的保存更为紧凑、精确。

它确实更便于键入代码。这种方法做了两个修改:

使用二进制文件模式;
使用成员函数write()。

使用二进制文件模式时,程序将数据从内存传输给文件(反之亦然)时,将不会发生任何隐藏的转换,而默认的文本模式并非如此。

各操作系统之间的文本文件换行符并不相同:

windows(回车加换行)代表换行。
Mac(回车)代表换行。
Unix和Linux(换行)代表换行。

C++为了增加可移植性,在读写文件时会自动发生对应的转换。
但对于二进制数据,文本格式可能会产生冲突,且在文件尾检测方式也有所区别。因此,C++存储二进制数据应该使用二进制文件模式。

要以二进制格式(而不是文本格式)存储数据,可以使用write( )成员函数。

这种方法将内存中指定数目的字节复制到文件中。它只逐字节地复制数据,而不进行任何转换。唯一不方便的地方是,必须将地址强制转换为指向char的指针。

要使用文件恢复信息,请通过一个ifstream对象使用相应的read( )方法:


ifstream inf("people.txt",ios::in|ios::binary);inf.read((char*)&p,sizeof(p));

这将从文件中复制sizeof p个字节到p结构中。同样的方法也适用于不使用虚函数的类。在这种情况下,只有数据成员被保存,而方法不会被保存。

如果类有虚方法,则也将复制隐藏指针(该指针指向虚函数的指针表)。由于下一次运行程序时,虚函数表可能在不同的位置,因此将文件中的旧指针信息复制到对象中,将可能造成混乱。

read()和write()成员函数的功能是相反的。请用read()来恢复用write()写入的数据。

注意:
虽然二进制文件概念是ANSII C的组成部分,但一些C和C++实现并没有提供对二进制文件模式的支持。

原因在于:有些系统只有一种文件类型,因此可以将二进制操作(如read()和write())用于标准文件格式。

1.3.10. 随机存取

随机存取指的是直接移动(不是依次移动)到文件的任何位置。随机存取常被用于数据库文件,程序维护一个独立的索引文件,该文件指出数据在主数据文件中的位置。

这样,程序便可以直接跳到这个位置,读取(还可能修改)其中的数据。

我们来看个案例:
该案例将完成以下工作:

  1. 显示文件当前的内容;
  2. 询问要修改哪条记录;
  3. 修改该记录;
  4. 显示修改后的文件。

为读取文件,需要使用ios_base::in模式。为执行二进制I/O,需要使用ios_base::binary模式。

为写入文件,需要ios_base::out或ios_base::app模式。

然而,追加模式只允许程序将数据添加到文件尾,文件的其他部分是只读的;也就是说,可以读取原始数据,但不能修改它;要修改数据,必须使用ios_base::out。


finout.open(file,ios::out|ios::in|ios::binary);

接下来,需要一种在文件中移动的方式。

fstream类为此继承了两个方法:seekg()和seekp(),前者将输入指针移到指定的文件位置,后者将输出指针移到指定的文件位置。

实际上,由于fstream类使用缓冲区来存储中间数据,因此指针指向的是缓冲区中的位置,而不是实际的文件。

也可以将seekg()用于ifstream对象,将seekp()用于oftream对象。下面是这两个成员函数的原型:


//seekg
istream& seekg(streamoff offset, seekdir origin);istream& seekg(streamoff offset);//seekp
ostream& seekp(streamoff offset, seekdir origin);ostream& seekp(streamoff offset);

第一个原型定位到离第二个参数指定的文件位置特定距离(单位为字节)的位置;第二个原型定位到离文件开头特定距离(单位为字节)的位置。

offset表示偏移量,origin表示移动的基准位置,取值如下∶
ios::beg 文件开头

ios::cur 文件当前位置

ios::end 文件结尾

示例∶


inFile.seekg(2,ios::beg);  //把文件从开始位置向后移动2个字节
inFile.seekg(2,ios::cur);  //把文件从当前位置尚后移动2个字节

如果要检查文件指针的当前位置,则对于输入流,可以使用tellg()方法。
对于输出流,可以使用tellp()方法。
它们都返回一个表示当前位置的streampos值(以字节为单位,从文件开始处算起)。
示例:


auto fpos=inFile.tellg(); //返回当前文件输出指针的位置
auto fpos=inFile.tellp(); //返回当前文件输入指针的位置

注意:
如果读者还对输入流和输出流这两个概念感到有些混淆,可以这样想,输入流和输出流不是相对文件,而是相对程序本身而言的,从文件中读取信息到程序,对程序而言就是输入流,从程序输出信息到文件,对程序而言就是输出流。

案例1:


#include<iostream>
#include<fstream>
#include<string>struct people {char name[20];int age;int weight;
}p;const char* file = "fileName.txt";  int main()
{using namespace std;fstream f;//打开二进制文件用于读写f.open(file, ios_base::binary | ios_base::in | ios_base::out);//判断文件是否正常打开if ( f.is_open() ){//如果正常打开,输出文件内已有的信息while (f.read((char*)&p,sizeof(p))){cout << "名字:"<<p.name << "  ";cout << "年龄:"<<p.age << "  ";cout << "体重:"<<p.weight << endl;}//重置流状态f.clear();}else{//如果文件未存在//重置流状态f.clear();//以写入文件的方式打开文件,即创建该文件f.open(file, ios_base::binary | ios_base::out |ios_base::trunc);}//设置输出文件指针的位置f.seekp(0, ios_base::end);cout << "输入名字:" << endl;cin.get(p.name,20);//循环输入信息,直到输入的名字为换行while (p.name[0]!='\0'){//输入信息,并在输入每条信息后将多余的输入丢弃掉while ( getchar() != '\n' );cout << "请输入年龄:" << endl;cin >> p.age;while ( getchar() != '\n' );cout << "请输入体重:" << endl;cin >> p.weight;while ( getchar() != '\n' );//将信息写输入文件f.write((char*) &p, sizeof(p));cout << "输入名字:" << endl;cin.get(p.name, 20);}//关闭文件f.close();//重置流状态f.clear();//以读文件的方式打开文件f.open(file, ios_base::binary | ios_base::in);if ( f.is_open() ){//读取文件内容while ( f.read((char*) &p, sizeof(p)) ){cout << "名字:" << p.name << "  ";cout << "年龄:" << p.age << "  ";cout << "体重:" << p.weight << endl;}//关闭文件f.close();f.clear();}return 0;
}

案例2:


#include<iostream>
#include<fstream>//默认文件已存在
const char* file = "fileName.txt";struct people {char name[20];int age;int weight;
}p;void eatLine()
{while ( getchar() != '\n' );
}int main()
{using namespace std;//以读写文件方式打开文件fstream f(file, ios_base::in | ios_base::out | ios_base::binary);int count = 0;  //显示的序号int ret = 0;    //要修改的序号int replace=0;  //修改的位置//如果文件打开正常if ( f.is_open() ){//输出文件内已有的内容while ( f.read((char*) &p, sizeof(p)) ){cout << "个人信息:" << endl;cout << count++ << " ";cout << p.name << " "; cout << p.age << " ";cout << p.weight << endl;}//如果到文件尾,重置流状态if(f.eof())f.clear();else{//如果未完全读取,结束程序cout << "未完全读取" << endl;return 0;}cout << "input---->>>" << endl;cout << "请输入你要修改的序号    " << count << endl;//重置标准输入流状态cin.clear();//输入修改的序号cin >> ret;//当输入值非法时while( ret < 0 || ret >= count ){cout << "输入值非法,重新输入" << endl;cin >> ret;}eatLine();  //丢弃多余的部分//计算要修改的值在文件内对应的位置replace = ret * sizeof(p);//输出指针定位f.seekp(replace);if ( f.fail() ){cout << "定位失败" << endl;return 0;}cout << "请输入要修改的内容:" << endl;cin >> p.name;eatLine();cin >> p.age;eatLine();cin >> p.weight;eatLine();f.write((char*) &p, sizeof(p));if ( f.fail() ){cout << "写入失败" << endl;return 0;}count = 0;//定位输入指针f.seekg(0);//重置流状态f.clear();//输出文件更新后的内容while ( f.read((char*) &p, sizeof(p)) ){cout << "个人信息:" << endl;cout << count++ << " ";cout << p.name << " ";cout << p.age << " ";cout << p.weight << endl;}if ( f.eof() )f.clear();}//关闭文件f.close();return 0;
}

注意;
如果该程序是否可以使用string对象而不是字符数组来表示people结构的name成员?

答案是否定的,至少在不对设计做重大修改的情况下是否定的。

问题在于,string对象本身实际上并没有包含字符串,而是包含一个指向其中存储了字符串的内存单元的指针。

因此,将结构复制到文件中时,复制的将不是字符串数据,而是字符串的存储地址。当您再次运行该程序时,该地址将毫无意义。


1.4. 字符串流

字符串流就是能够控制字符串类型对象进行输入输出的类,C++不光支持C++风格的字符串流控制,还支持C风格的字符串流控制。

继承关系如下图:
在这里插入图片描述

istrstream   //C风格的串流的输入操作,字符串数组作为输入ostrstream  //C风格的串流输出操作,字符串数组作为输出strstream   //支持C风格的串流的输入输出操作

iostream族(family)支持程序与终端之间的I/O,而fstream族使用相同的接口提供程序和文件之间的I/O。

C++库还提供了sstream族,它们使用相同的接口提供程序和string对象之间的I/O。

读取string对象中的格式化信息或将格式化信息写入string对象中被称为内核格式化(incore formatting)。

头文件sstream定义了从ostream类派生而来的ostringstream类和istringstream类。

如果创建了一个ostringstream对象,则可以将信息写入其中,它将存储这些信息。可以将可用于cout的方法用于ostringstream对象。

例如:


ostringstream outstr;
double price =380.0;char*p="for a copy of the ISO/ETC C++ standard!";outstr<<"pay only CHF"<<price<<ps<<endl;

ostringstream类有一个名为str()的成员函数,该函数返回一个被初始化为缓冲区内容的字符串对象。

使用str()方法可以“冻结”该对象,这样便不能将信息写入该对象中。


string s=outstr.str();
cout<<s<<endl;

istringstream类允许使用istream方法族读取istringstream对象中的数据,istringstream对象可以使用string对象进行初始化。
例如:


string s("I am Chinese");
istringstream instr(s);

这样,便可以使用istream方法读取instr中的数据。


string word;
while(instr>>word)
cout<<word<<endl;

总之,istringstream和ostringstream类使得能够使用istream和ostream类的方法来管理存储在字符串中的字符数据。当然,使用时应记得包含头文件sstream。

案例:


#include<iostream>
#include<sstream>
#include<string>int main()
{using namespace std;ostringstream outstr;string s;getline(cin, s);float f;cin >> f;const char* ch = "谢谢各位帅哥美女的支持!!!";outstr << "感谢:" << ch << s << f << endl;string ret = outstr.str();cout << ret << endl;string s = "Life is like a sea, only the strong will of people, to reach the other shore。";istringstream p(s);string word;while ( p >> word ){cout << word << endl;}return 0;
}

1.5. 总结

fstream文件提供了将iostream方法扩展到文件I/O的类定义。

ifstream类是从istream类派生而来的。通过将ifstream对象与文件关联起来,可以使用所有的istream方法来读取文件。

同样,通过将ofstream对象与文件关联起来,可以使用ostream方法来写文件;通过将fstream对象与文件关联起来,可以将输入和输出方法用于文件。

要将文件与流关联起来,可以在初始化文件流对象时提供文件名,也可以先创建一个文件流对象,然后用open()方法将这个流与文件关联起来。

close()方法终止流与文件之间的连接。

类构造函数和open()方法接受可选的第二个参数,该参数提供文件模式。

文件模式决定文件是否被读和/或写、打开文件以便写入时是否截短文件、试图打开不存在的文件时是否会导致错误、是使用二进制模式还是文本模式等。

文本文件以字符格式存储所有的信息,例如,数字值将被转换为字符表示。常规的<<和>>运算符以及get(),put()和getline()都支持这种模式。

二进制文件使用计算机内部使用的二进制表示来存储信息。与文本文件相比,二进制文件存储数据(尤其是浮点值)更为精确、简洁,但可移植性较差。read()和write()方法都支持二进制输入和输出。

seekg()和seekp()函数提供对文件的随机存取。这些类方法使得能够将文件指针放置到相对于文件开头、文件尾和当前位置的某个位置。

tellg()和tellp()方法报告当前的文件位置。

sstream头文件定义了istringstream和ostringstream类,这些类使得能够使用istream和ostream方法来抽取字符串中的信息,并对要放入到字符串中的信息进行格式化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/17594.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mysql-学习笔记

文章目录 1. 数据库1.1 Mysql安装及常用代码1.2 SQL介绍1.3 SQL分类1. DDL-操作数据库&#xff0c;表2. DML-对表中的数据进行增删改3. DQL-对表中的数据进行查询条件查询模糊查询排序查询分组查询分页查询 4. DCL-对数据库进行权限控制外键约束表关系-多对多多表查询事务 1. 数…

ETHERNET/IP转RS485/RS232网关什么是EtherNet/IP?

网络数据传输遇到的协议不同、数据互通麻烦等问题&#xff0c;一直困扰着大家。然而&#xff0c;现在有一种神器——捷米JM-EIP-RS485/232&#xff0c;它将ETHERNET/IP网络和RS485/RS232总线连接在一起&#xff0c;让数据传输更加便捷高效 那么&#xff0c;它是如何实现这一功能…

建木使用进阶-创建密钥管理

阿丹&#xff1a; 第一次我们进入建木&#xff0c;第一件事情就是配置我们相关的密钥。 解读&#xff1a; 在建木中我们可以进行创建密钥来对我们服务器等密码进行方便的管理。 注意&#xff1a; 登录的时候账号为&#xff1a;admin 密码为&#xff1a;123456 这是初始…

linux -网络编程一网络基本概念和Socket编程

目录 1 网络基础概念 1.1 协议 1.2分层模型 1.3 数据通信过程 1.4 网络应用程序的设计模式 1.5 以太网帧格式 1.6网络名词术语解析(自行阅读扫盲) 2 SOCKET编程 2.1 socket编程预备知识 2.2 socket编程主要的API函数介绍 目标&#xff1a; 了解OSI七层、TCP/IP四层模…

【vue】 前端vue2 全局水印效果

最近写项目遇到一个需求&#xff0c;全局显示水印&#xff0c;不管在哪个路由都要显示。 想要实现的效果&#xff1a; 新建shuiyin.js文件 // 定义水印函数 const addWatermark ({container document.body, // 水印添加到的容器&#xff0c;默认为 bodywidth "200px&…

MaxPatrol SIEM 增加了一套检测供应链攻击的专业技术

我们为 MaxPatrol SIEM 信息安全事件监控系统增加了一套新的专业技术。 该产品可帮助企业防范与供应链攻击相关的威胁。 此类攻击正成为攻击者的首要目标&#xff1a;它们以软件开发商和供应商为目标&#xff0c;网络犯罪分子通过他们的产品进入最终目标的基础设施。 因此&a…

新版塔罗占卜网站源码八字合婚风水起名附带搭建视频

新版塔罗占卜网站源码八字合婚风水起名PHP源码附带搭建视频,附带文本教学及视频教程安装方法以linux为例: 1、建议在服务器上面安装宝塔面板,以便操作,高逼格技术员可以忽略这步操作。 2、把安装包文件解压到根目录,同时建立数据库,把数据文件导入数据库 3、修改核心文件…

安达发|APS智能排程软件推动企业智能转型

随着智能化技术的飞速发展&#xff0c;企业在构建智能计划排产规划方面有了新的可能性。APS排程软件&#xff08;Advanced Planning and Scheduling&#xff09;作为一种强大的工具&#xff0c;为企业提供了实现智能计划排产规划的新模式&#xff0c;主要是利用计算机运算速度快…

无线电蓝牙音频-BES数字音频系统音频流图

+我V hezkz17进数字音频系统研究开发交流答疑群(课题组) (1)音乐播放音频流图 Decode"(解码)是指将编码后的数据转换回原始格式或可读取的形式的过程,SBC解码成PCM

06 HTTP(下)

06 HTTP&#xff08;下&#xff09; 介绍服务器如何响应请求报文&#xff0c;并将该报文发送给浏览器端。介绍一些基础API&#xff0c;然后结合流程图和代码对服务器响应请求报文进行详解。 基础API部分&#xff0c;介绍stat、mmap、iovec、writev。 流程图部分&#xff0c;描…

linux 安装FTP

检查是否已经安装 $] rpm -qa |grep vsftpd vsftpd-3.0.2-29.el7_9.x86_64出现 vsftpd 信息表示已经安装&#xff0c;无需再次安装 yum安装 $] yum -y install vsftpd此命令需要root执行或有sudo权限的账号执行 /etc/vsftpd 目录 ftpusers # 禁用账号列表 user_list # 账号列…

集成kisso框架springboot解决登录不了问题

前端使用 VUE 框架&#xff0c;要求请求头中传入 token值&#xff0c;获取设置token使用cookie操作 import Cookies from js-cookieconst TokenKey token-c export function getToken() {return Cookies.get(TokenKey) } export function setToken(token) {Cookies.set(Token…

html学习3(表格table、列表list)

1、html表格由<table>标签来定义。 <thead>用来定义表格的标题部分&#xff0c;其内部用 <th > 元素定义列的标题&#xff0c;可以使其在表格中以粗体显示&#xff0c;与普通单元格区分开来。<tbody>用来定义表格的主体部分&#xff0c;其内部用<t…

无涯教程-jQuery - Tabs组件函数

窗口小部件选项卡函数可与JqueryUI中的窗口小部件一起使用。选项卡用于在分成逻辑部分的内容之间交换。 Tabs - 语法 $( "#tabs" ).tabs(); Tabs - 示例 以下是显示Tab用法的简单示例- <!doctype html> <html lang"en"><head><m…

【文生图系列】Runaway Gen-2试用体验

文章目录 风景示例动物示例人物动作示例 Runway旗下的视频生成产品Gen-1和Gen-2已彻底开放&#xff0c;任何人都可注册一个账号免费尝试。免费的时长是105s&#xff0c;每个视频生成4s。 看gen-2官网和各公众号放出来的示例&#xff0c;非常震撼&#xff0c;不禁感慨现在文生视…

Ubuntu Server版 之 apache系列 常用配置 以及 隐藏 版本号 IP、Port 搭建服务案例

查看版本 旧的 用 httpd -v 新的 用 apache2 -v 配置检测 旧的 httpd -t 新的 apachectl configtest window用的apache 是 httpd -t Linux 中 apachectl configtest 主配置文件 之前旧版apache 是httpd 现在都改成 apache2 /etc/apache2/apache2.conf window中 httpd.con…

Git全栈体系(三)

第六章 GitHub 操作 一、创建远程仓库 二、远程仓库操作 命令名称作用git remote -v查看当前所有远程地址别名git remote add 别名 远程地址起别名git push 别名 分支推送本地分支上的内容到远程仓库git clone 远程地址将远程仓库的内容克隆到本地git pull 远程库地址别名 远…

【C++】模板进阶(模板的特化,非类型模板参数,模板的分离编译)

文章目录 一、模板使用时一定要加typename的情况二、 非类型模板参数三、模板的特化1.函数模板特化2.类模板特化1.全特化&#xff1a;2. 偏特化&#xff1a;1. 部分特化2.参数更一步限制 四、模板的分离编译1.Stack.h2.Stack.cpp(定义)3.test.cpp 一、模板使用时一定要加typena…

TSINGSEE青犀视频汇聚平台EasyCVR多种视频流播放协议介绍

众所周知&#xff0c;TSINGSEE青犀视频汇聚平台EasyCVR可支持多协议方式接入&#xff0c;包括主流标准协议GB28181、RTSP/Onvif、RTMP等&#xff0c;以及厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。今天我们来说一说&#xff0c;EasyCVR平台支持分…

maven本地仓库地址修改+maven国内镜像设置+maven运行所需pos.xml文件配置基本写法

1&#xff0c;maven本地仓库地址修改 maven在使用过程中&#xff0c;本地项目仓库其空间占用会越来越大&#xff0c;但是其默认仓库位置往往是以C盘为主&#xff0c;C盘作为系统盘常常会遇到所在盘空间占满的情况&#xff0c;所以我们将其改至其他硬盘空间位置为适合做法&#…