I/O流与文件

  • 概述

一、何为I/O

外围设备分为,存储设备和输入/输出设备;

存储设备用于存储信息,如磁盘、U盘、光盘、磁带等,数据以文件形式保存在这些存储设备中;

输入/输出设备分为,输入设备和输出设备;输入设备指计算机接收数据的来源设备,如键盘、鼠标、扫描仪等;输出设备指计算机处理完毕的数据送往外部设备,如显示器、打印机等;

I/O的中心是内存;在内存中,数据往外输送,即为输出;数据从外面进来,即为输入;

二、标准I/O流cin和cout

头文件iostream中,定义了两个流类:输入流类istream和输出流类ostream,且用这两个类定义了流对象cin和cout:

istream cin;
ostream cout;

cin和cout分别是输入数据流和输出数据流;在默认情况下,cin与键盘关联,键盘中输入的数据进入cin;cout与显示器关联,cout中的数据输出到显示屏上;

举例:

int someInt;
float someFloat;
char someChar;
cin>>someInt>>someFloat>>someChar;
// 通过键盘键入12 3.14 9<回车>
cout<<"The answer is: "<<someInt*someFloat;

分析:

cin和cout都只能装载字符,因此输入操作符">>"和输出操作符"<<"都要负责转化工作

a 对于cin,键盘键入的字符逐个进入cin。若接收这些数据的变量不是字符型变量或字符串变量,则">>"将根据变量类型,把cin的字符组合转化为该变量类型的值,再赋值给该变量。如该例第一个输入的变量是int类型,">>"把"1"和"3"这样的字符组合转化为整型值13,再赋值给someInt;第二个输入">>"把‘3‘、‘.‘、‘1‘和‘4‘转化为double值3.14,再赋值给someFloat;

b cin中每个字符都是”平等“的,都是按先后顺序等待着每次输入">>"消化掉前面一些字符,则每次输入是消化掉cin中哪些字符呢?每次">>"都是一次输入,且多次输入可连续进行。输入由两部分组成,键盘的数据进入cin和cin的数据进入变量,两个部分均完成才结束由多次输入组成的整个输入状态。每次输入在碰到空白符(空格、制表符、换行符),或碰到第一个非法字符(该数据类型中不能出现的字符)时结束。整个输入状态在键入了回车且已输入的数据足以满足所有输入时结束;

一般,整形变量接收"0"~"9"十个字符;浮点型变量接收这十个字符外,还接收".";字符型变量接收非空白符;

c 对于cout,输出操作符"<<"把数据项转换为字符组合,再置入cout中。如该例要输出的是double值40.82,"<<"将其转换为‘4‘、‘0‘、‘.‘、‘8‘和‘2‘,再输出到显示器上;

三、文件I/O流

文件I/O也称读文件(输入)和写文件(输出);C++标准库中提供两个类ifstream和ofstream,分别用于文件输入和输出;

举例1:

事先准备好一个纯文本文件source.txt,保存在与下面fileIO.cpp同一个文件夹中,其内容为:12 3.14 9;编写程序,把该文件的内容读入一个int变量、一个float变量和一个char变量中;

/*
	利用文件流实现文件I/O
*/
#include <fstream>             // ①
using namespace std;
int main()
{
	int someInt;
	float someFloat;
	char someChar;

	ifstream inFile;           // ②
	ofstream outFile;          // ②

	inFile.open("source.txt"); // ③
	outFile.open("result.txt"); // ④

	inFile >> someInt >> someFloat >> someChar; // ⑤
	outFile << "The answer is: " << someInt*someFloat << endl; // ⑥

	inFile.close();  // ⑦
	outFile.close(); // ⑦

	system("pause");
	return 0;
}

执行结果保存在result.txt中:The answer is: 40.82;

分析:

a 由于类ifstream和ofstream定义在fstream中,在文件头需要加上预编译指令①;

b ②语句定义了两个对象inFile和outFile,称为文件流对象,inFile负责文件输入,outFile负责文件输出;

c ③和④语句文件流对象与就具体的文件关联起来,使后续的具体读写操作作用于这些文件上。③把输入文件流对象inFile与文件sourcce.txt关联起来,后面从inFile输入数据便是从文件里读取数据;同理,④把输出文件流对象outFile与文件result.txt关联起来,后面输出的结果放入outFile中最终就会保存在result.txt里;

d 对于输入流,若相关联的文件不存在,则下面e中所介绍的都操作将不起任何作用;对于输出流,若相关联的文件不存在,将创建该文件;若该文件存在,先自动清空文件的原有内容,再e中介绍的写操作再把新内容写入文件;

e ⑤和⑥是具体的读写操作,⑤是从输入文件流inFile中读取数据,置入变量中,⑥是把要输出的内容放入输出文件流中,最终保存到文件result.txt中;

举例2:

假设已有纯文本文件1.txt,先编写程序将1.txt复制到另一个纯文本文件2.txt中;

/*
	利用文件流实现文本文件的复制
*/
#include <fstream>
using namespace std;
int main()
{
	char ch;

	ifstream inFile;
	ofstream outFile;

	inFile.open("1.txt");
	outFile.open("2.txt");

	inFile >> ch;			// ①
	while (inFile)			// ②
	{
		outFile << ch;
		inFile >> ch;		// ③
	}

	inFile.close();
	outFile.close();

	system("pause");
	return 0;
}

本段程序利用循环,逐个读取1.txt的字节,并输送给2.txt,达到复制目的;

利用inFile是否进入失效状态来判断1.txt是否已经读取完毕!!!

在inFile中装载的是一个一个字符,通过①和③的读取,每次从inFile中消耗掉一个字符,此时inFile一直处于正常状态;当读取完最后一个字符后,假如再次企图读取,此时inFile中已无字符,这个读取的大动作会使inFile进入失效状态。因此通过②的判断,得知文件是否读取完毕;

假如1.txt内容是:

I am in Nanjing.

I like baking and sports.

运行上述程序后,将得到2.txt,打开该文件其内容是:

IaminNanjing.Ilikebakingandsports.

可以看到,1.txt中的换行符和空格符都没有复制到2.txt中,因为输入符">>"会忽略掉空白字符!!!

为使1.txt中内容完整复制到2.txt中,修改程序如下:

/*
	利用文件流实现文本文件的完整复制
*/
#include <fstream>
using namespace std;
int main()
{
	char ch;

	ifstream inFile;
	ofstream outFile;

	inFile.open("1.txt");
	outFile.open("2.txt");

	inFile.get(ch);			// ①
	while (inFile)			// ②
	{
		outFile << ch;
		inFile.get(ch);		// ③
	}

	inFile.close();
	outFile.close();

	system("pause");
	return 0;
}

分析:

语句①中的get函数是输入流的公有成员函数,作用是按顺序获取输入流中的一个字符,包括空白字符都会被获取,并存放在参数变量ch中。

  • 二进制文件I/O

文本文件I/O不是文件I/O的主流,而是二进制文件I/O;

一、文本文件I/O VS 二进制文件I/O

文本文件输出,需要先将输出值转化为字符序列,再存储到文件中的内容是这些字符相应地ASCII码;文本文件输入,先将字符序列转化为相应的数值,再进入变量;

二进制文件输出,是直接将内存数据(二进制形式)输出到文件中保存;二进制文件输入,直接将文件内容输入内存中即可,无需转化过程。

一般,二进制文件比文本文件体积小,且二进制文件I/O省去”转化“,大大节约文件I/O的时间。

但文本文件I/O有一个优点:方便打开文本文件、直接看到结果,因此一般用于保存一些简单的结果数据用以方便查阅。

二、二进制文件I/O

1 利用ifstream和ofstream进行二进制文件I/O

举例:

/*
	利用ifstream和ofstream进行二进制文件I/O
*/
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
	int a[10] = { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
	int b[10];
	int i;

	ifstream inFile;								// ①
	ofstream outFile;								// ①

	outFile.open("1.dat", ios::binary);				// ②
	for (i = 0; i < 10; i++)
	{
		outFile.write((char*)&a[i], sizeof(a[i]));	// ③
	}
	outFile.close();								// ④

	inFile.open("1.dat", ios::binary);				// ②
	for (i = 0; i < 10; i++)
	{
		inFile.read((char*)&b[i], sizeof(b[i]));	// ⑤
	}
	inFile.close();									// ④

	for (i = 0; i < 10; i++)
		cout << b[i] << " ";
	cout << endl;

	system("pause");
	return 0;
}

运行程序屏幕显示:10 20 30 40 50 60 70 80 90 100

且在该cpp同目录下将出现一个共有40字节的1.dat,表示数组a个元素已写入该文件中,在程序中又将该文件的内容读取到b数组中;

分析:

a 利用语句①创建输出文件流或输入文件流对象,并利用语句②使得文件流对象与某个文件相关联;ifstream和ofstream类的open函数原型分别如下,

void open(const char* szName, int nMode=ios::in, int nProt=filebuf::openprot);
void open(const char* szName, int nMode=ios::out, int nProt=filebuf::openprot);

对于输入文件流,szName是输入文件名;对于输出文件流,szNAme是输出文件名。

nMode指定文件的打开模式,由ios类中定义的一组枚举常量表示;在默认情况下,输入文件流的nMode为ios::in,输出文件流的nMode为ios::out,表示文件I/O方式为文本文件I/O;若进行二进制文件I/O,将其设置为ios::binary。

nProt指定文件的保护方式,如只读、隐含等,一般情况下使用默认值即可。

b 语句③和⑤进行文件写和读的具体操作。对于write函数,第一个参数表示要输出的数据在内存中的存放地址,第二个参数表示要输出的数据占据内存空间的大小(字节);对于read函数,第一个参数表示数据输入后存放的地址,第二个参数表示输入数据占据内存空间的大小(字节)。

由于读写均以字节为单位进行传输,因此地址应该是指向自己的指针,即char*,因此需要把地址强制转化为char*类型。

c 文件I/O完毕,需关闭文件。

d 对于文件输出,若文件已经存在,默认情况下,open函数将清除文件中的原有数据。若希望以追加形式写文件,即不清除文件中的原有数据,而是从原有数据结尾处写入新数据,需在调用open函数时利用ios::app指定写方式为追加:

outFile.open("1.dat", ios::binary | ios::app);

位或操作|表示枚举常量的组合,如上述的ios::binary | ios::app表示以追加方式打开二进制文件。

2 文件的定位

默认情况下,对文件的读写是按顺序从头到尾进行。我们可以认为每个文件中有两个位置指针,指出在文件中进行读写操作的位置。每读/写完一个数据项后,读/写指针自动移动到下一个位置上。但有时,我们可能需要控制这两个指针的位置,以一种我们自己设计的顺序来读/写文件,这就是文件的定位。在对文件进行读/写操作时可利用seekg/seekp函数进行文件定位。

举例:

假设在下面程序中,希望隔个读取文件1.dat里面的数值,即只读取10、30、50、70、90。在读取10后,文件读指针自动移动到20的位置上,这时就应该调用seekg,改变指针位置使其移动到30的位置上,便可略过20。

/*
	利用函数seekg进行输入文件读指针的定位
*/
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
	int a[10] = { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
	int b[10] = { 0 };
	int i;

	ifstream inFile;
	ofstream outFile;

	outFile.open("1.dat", ios::binary);
	for (i = 0; i < 10; i++)
	{
		outFile.write((char*)&a[i], sizeof(a[i]));
	}
	outFile.close();

	inFile.open("1.dat", ios::binary);
	for (i = 0; i < 10; i++)						// ①
	{
		inFile.read((char*)&b[i], sizeof(b[i]));	// ②
		inFile.seekg(sizeof(int), ios::cur);		// ③
	}
	inFile.close();

	for (i = 0; i < 10; i++)
		cout << b[i] << " ";
	cout << endl;

	system("pause");
	return 0;
}

运行程序屏幕显示:10 30 50 70 90 0 0 0 0 0

分析:

在循环①中,并没有将1.dat的数据完全读入数组b中,因为在每次执行语句②读入一个int型数据后,语句③利用函数seekg移动了读指针的位置,使得读指针跳过一个int型数据,指向再下一个int型数据。函数seekg调用方式如下:

输入文件流对象.seekg (位移量, 起始点);

其含义是使得位置指针从当前位置移动到距离”起始点“为”位移量“的那个位置上,位移量按字节计数。起始点可以使ios::beg、ios::cur、ios::end,分别表示文件开始处、当前位置、文件末尾处。若位移量为正整数,则向文件结尾方向移动;若为负整数,则向文件开头方向移动。

对于输出文件,可以类似使用seekp函数进行文件写指针的定位。

时间: 2024-10-27 11:21:55

I/O流与文件的相关文章

Java学习记录(补充八:Date类;Java流(Stream),文件(File)和IO)

Date类,Calendar类package Box1; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.Random; //Date类 public class DateTest { public static void main(String[] args) { Date

Java:IO流与文件基础

Java:IO流与文件基础 说明: 本文所有内容包含图片均为MrSaber自己编写,转载请练习我哦. 本章内容将会持续更新,大家可以关注一下并给我提供建议,谢谢啦. 走进流 什么是流 流:从源到目的地的字节的有序序列. 在Java中,可以从其中读取一个字节序列的对象称作 输入流,可以向其中写入一个字节序列的对象称作 输出流. ? 这些字节序列的来源可以是:文件.网络连接.内存块等. ? 抽象类InputStream和OutputStream是构成输入/输出(I/O)的基础. ? 因为面向字节的流

C++学习47 文件的概念 文件流类与文件流对象 文件的打开与关闭

迄今为止,我们讨论的输入输出是以系统指定的标准设备(输入设备为键盘,输出设备为显示器)为对象的.在实际应用中,常以磁盘文件作为对象.即从磁盘文件读取数据,将数据输出到磁盘文件.磁盘是计算机的外部存储器,它能够长期保留信息,能读能写,可以刷新重写,方便携带,因而得到广泛使用. 文件(file)是程序设计中一个重要的概念.所谓“文件”,一般指存储在外部介质上数据的集合.一批数据是以文件的形式存放在外部介质(如磁盘.光盘和U盘)上的.操 作系统是以文件为单位对数据进行管理的,也就是说,如果想找存在外部

流与文件:NIO.2的介绍和使用

传统的Java里,只有一个File类,即代表文件,又代表目录.Java 7新增了如下API来访问文件 Path  - 接口,代表一个平台无关的目录.提供了大量的方法来操作目录. Paths - 工具类.所有方法都是static的. Files - 操作文件的工具类.提供了大量的方法来操作文件.该类所包含的大量方法可能与我们日常一般的期望有些出入. 早期的Java只提供了File类来访问文件,其功能有限且性能不高,NIO.2提供了Path接口以及Paths和Files工具类来访问文件系统. 1.P

使用摘要流获取文件的MD5

摘要流是过滤流的一种,使用它可以再读取和写入流时获取流的摘要信息(MD5/SHA). 使用摘要流包装流时,需要额外传递一个MessageDigest对象, MessageDigest md=MessageDigest.getInstance("MD5"); DigestInputStream dis=new DigestInputStream(in, md); 摘要流复写了流的read.write方法,方法内部调用MessageDigest对象的upate()来更新摘要信息 publi

ifstream?流?判断文件是否结尾的函数eof(.xml

pre{ line-height:1; color:#800080; font-size:16px;}.sysFunc{color:#627cf6;font-style:italic;font-weight:bold;} .selfFuc{color:#800080;} .bool{color:#d2576f;} .condition{color:#000080;font-weight:bold;} .key{color:#000080;} .var{color:#800000;font-sty

java使用字节流和字符流实现文件复制

大家在Java开发中都会遇到文件复制的文件,众所周知,需要通过文件输入输出流实现. 那究竟该怎么做那,话不多说,直接上代码: 一,使用字节流复制文件 public class FileByteCopy {public static void main(String[] args) { FileByteCopy f= new FileByteCopy(); try { f.copy("d:/File/1.txt","d:/CopyFile/1.txt"); } cat

java io流(字符流) 文件打开、读取文件、关闭文件

java io流(字符流) 文件打开 读取文件 关闭文件 //打开文件 //读取文件内容 //关闭文件 import java.io.*; public class Index{ public static void main(String[] args) throws Exception{ //打开文件 //字符流方式打开 //字符流每次按一个字符读取 FileReader wj = new FileReader("D:/java/kj/javanew/src/Index.java"

java io流 创建文件、写入数据、设置输出位置

java io流 创建文件 写入数据 改变system.out.print的输出位置 //创建文件 //写入数据 //改变system.out.print的输出位置 import java.io.*; public class Index{ public static void main(String[] args) throws Exception{ /** * 存储为二进制,给计算机看的 */ //创建文件 DataOutputStream sjl = new DataOutputStrea

转载 - C++ - 关于ifstream/fstream流 判断文件是否结束eof()的问题

出处:http://blog.csdn.net/shuilan0066/article/details/4669451 在做实验的时候遇到这个问题,找原因的时候发现出处除了讲明原因,还举了例子,所以记下来. 其实在循环判断文件是否结束的时候可以直接就流输入放在循环条件那里,但是这里补充使用eof()的一些细节问题.其实这是关于到底什么时候标志位才会变化的问题.总结起来就是只有使用一次流变量来输入输出,标志位才会更新一次. 正文: fstream流的eof()  判断有点不合常理 按正常逻辑来说,