您的位置:首页 > 编程语言 > C语言/C++

C++ 文件和流操作(Files and streams)

2014-07-02 16:20 363 查看
大部分情况下, 我们的程序的输入数据(input)仅可以来自于keyboard输入, 程序的输出也只是screen的console,。但是其实我们的program的输入输出

不应该只是限于keyboard 和screen这两种input 和output devices.。 试想一下, 如果我们的程序需要处理大量的数据的时候, 如果仅仅依靠键盘来手写输入,

那么运行的效率将会是何其的低。 同理, 如果我们仅仅将处理后的大量数据送到屏幕上显示, 二不去保存这些大量的数据, 当我们关掉电脑的时候,

所有的结果数据都想会消失(因为我们的数据在内存中)。这或许就是C++ 文件流处理的motivation。

所以 ,为了应对上面关于输入输出的两个challenges, 我们需要我们的数据存放在一些secondary storage device中, 这些device通常是magnetics

tapes, 或者disc(磁盘中)。 这样在必要的时候, 们就可以通过编写program 创建, 修改, 存取(access)我们的数据。 为了实现这些目的, 我们的数据

以文件(files)的数据结构(data structures)的形式 packageed up on storage devices。

我们可以将文件看成是由一个线性字符序列(a linear sequence of characters)的数据结构组织的。 举一个例子, 下面文件(截图)的存储形式可能看成是是如下形式:

存储方式:





Streams

在我们用C++ 处理文件的之前, 我们已经对流有过了解了。 我们可以将streams 想象成数据从senders 到 receivers 的一个通道(channel)。

就目前而言, 我们将只允许我们的流是单方向的运动。 数据可以从程序输出的输出流(例如程序将处理结果从到显示器显示(output stream), 或者是从到磁盘存储起来

等等)。 亦或是数据从键盘, 或者磁盘中送到程序进行处理的输入流(input stream) .。 NOTE: 输入流还是输出流是站在program的角度看的。

举个例子, 程序开始的时候, 标准输入流'cin'连接到键盘等待用户输入。 输出流'cout' 连接到显示器以便将程序的结果显示在屏幕上。



事实上, 输入流cin 和输出流cout 是stream(流类)objects(对象)。所以这也是OOP的一部分。

在处理文件流的时候, 我们需要一个列举出了关于定义了文件的输入流和输出流的一些operations, 该头文件的名字是“fstream”。 所以我们在编写此类程序之前,

必须在程序之前添加如下的指导语句:

#include <fstream>



Creating Streams

当我们想要在程序中使用文件的输入流和输出流, 第一步就是创建文件流。 ifstream class 用于创建输入流的对象, ofstream 用于创建输出流的对象。

如下:

ifstream in_stream;

ofstream out_stream;



Connecting and Disconnecting Streams to files

创建完了流对象之后, 接下来就是使用openc() 成员函数去把文件与流对象连接起来。 成员函数open() 对于ifstreams对象和ofstream的对象的作用效果

是不同, 也就是说openc() function是porlymorphic的(多态的)。

(1)将ifstream 对象in_stream 与文件“Lecture_4” 连接起来。

in_stream.open("Lecture_4");

可以用下图形象的表示:



将输出文件流ofstream的对象“out_stream” 与文件“Lecture_4”连接起来:

out_stream.open(“Lecture_4”);

注意, 尽管将out_stream 和 “Lecture_4”连

接起来, 但是It also delete the previous contents of the file, ready for new content。 用下图形象的表示:







当我们执行完文件流操作时, 我们需要切断ifstream的对象“in_stream” 和文件的联系, 我们需要调用成员函数close:

in_stream.close();

用下图形象的表示:



同理, 对于输出文件流:

假设初始状态:





out_stream.close();

上述语句对于输出文件流对象具有相同的效果, 但是还加上了一个额外的效果, 就是文件的最末尾加上了一个“end-of file” mark 以表明文件的结束。

所以, 当我们的程序没有对文件输入任何数据的时候, 切断连接的时候, 效果如下图:





在这种情况下, 文件“Lecture_4”仍然存在, 只不过是空文件(注意文件和文件夹的区别, 文件的后缀名可能是.doc, .txt, .data等等)



Check for Failures with file command

用程序打开或者关闭文件, 常常会出现错误。 例如文件打不开, 或者无法关闭等等。 为了使得我们的程序更更加的robust, 我们就需要检查这些操作是否

成功了。 如果没有成功, 我们需要编写错误处理程序。 一个很简单的检查机制就是使用成员函数“fail()”, 函数调用如下:

in_stream.fail();

调用上述函数返回的是一个Boolean value。 如果没能成功打开, 返回的是True, 如果文件打开成功, 返回的就是false,.。 没能成功打开, 后面的操作就无法执行了, 此时我们可以退出程序, 通过使用“exit(1)” 命令。 exit(1) 命令来自于库cstdlib; 程序如下:

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

	using namespace std;
	
	int main()
	{
	   ifstream in_stream;
	
	   in_stream.open("Lecture_4");
	   if (in_stream.fail()) {
			cout << "Sorry, the file couldn't be opened!\n";
			exit(1);
	   }
	...




Character Input and OutPut(字符输入和输出)

使用get(..)用于输入字符

假设初始状态为:



当打开一个输入文件(input file时, 我们可以使用成员函数“get(...)" 来extract 或者read文件中 的单个字符。 这个成员函数的参数只有一个, 类型为

“char”的 变量。



语句如下:

in_stream.get(ch);

上述语句具有如下两种效果(参见上原理图):

(1)变量“ch” is assigned the value "4";

(2) ifstream 的对象in_stream 被重新定位(re-positioned)到文件中的下一个输入字符, 也就是“.”。



Output using "put(...)"

初始状态:





我们可以通过ofstream 的对象out_stream的成员函数“put(...)去”input or write single character to a file 。 同样, 这个函数也只有一个类型为“char”

的参数。 具体如下:

out_stream.put('4');

参考如下图:





输入文件流的成员函数“putback(...)”

对于ifstream类而言, 有一个成员函数“putback(....)”。 从表面上肯看, 意思是“put the character back”, 但是事实上, 该函数并没有改变实际的文件。

尽管表现上是这样的(it behaves as if it had)。 例如初始状态为:



执行如下语句后:

in_stream.putback(ch);

得到如下新的状态







Indeed, we can "putback" any character we want to. 语句如下:

in_stream.putback('7');// 将7放到输入流中(7不在输入文件中)

导致如下结果:




Checking for the End of an Input File

检查文件是否读到结尾了

使用“ef()” 检查文件是否结束

Special care has to be taken with input when the end of a file is reached.

许多的C++编译器(包括GNU g++ 和 Microsoft Visual C++) 都incorporate an end-of-file (EOF)的标志(flag)。 if stream 的成员函数“eof()”

可以用于测试这个标志(EOF)被设置成了True 还是 False。

当一个ifstream 的对象与一个文件连接起来的时候。 EOF标志碑设置成了False(即使文件是空的)。 然而, 如果ifstream 的对象“in_stream”的

放置在文件的结尾, 而且EOF flag 是False, 语句:

in_stream.get(ch);

此语句导致变量ch在一个无法预测的状态, 然后设置为EOF标志为True。 一旦EOF标志为True, 然后no attemp should be made to read from the file, since the results

will be unpredicted。

为了形象表示这个过程, 假设我们当前的状态为:



然后执行语句:

in_stream.get(ch);

导致如下结果状态:



再次执行如下数据:

in_stream.get(ch);

最终导致如下状态:





然后执行如下的boolean expression:

in_stream.eof();

此时上述语句的值为True。



运用上面的知识, 下面的一个程序是将文件“Lecture_4”的内容在屏幕上显示出来, 并且将文件copy 到文件“Copy_of_4”:

NOTE: 用C++ 读取.txt, .data(二进制存储的文件)等很容易, 但是读取.doc 文件读出来的是乱码, 不知道问什么。

#include <iostream>
#include <fstream>

using namespace std;

int main() {
   char character;
   ifstream in_stream;
   ofstream out_stream;

   in_stream.open("D:\\demoFileStream.txt");
   out_stream.open("D:\\Copy_of_demoFileStream.txt");

   in_stream.get(character);

   while(!in_stream.eof()) {
      cout << character;
      out_stream.put(character);
      in_stream.get(character);
   }

   out_stream.close();
   in_stream.close();

   return 0;
}


运行结果如下:









另外, 输入输出文件流也可以作为函数的参数, 程序如下:

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

	void copy_to(ifstream& in, ofstream& out);
	
	/* MAIN PROGRAM: */
	int main()
	{
		ifstream in_stream;
		ofstream out_stream;
	
		in_stream.open("Lecture_4");
		out_stream.open("Copy_of_4");
		copy_to(in_stream, out_stream);
		out_stream.close();
		in_stream.close();
	
		return 0;
	}
	/* END OF MAIN PROGRAM */
	
	/* FUNCTION TO COPY A FILE TO ANOTHER FILE AND TO THE SCREEN: */
	void copy_to(ifstream& in, ofstream& out)
	{
		char character;
	
		in.get(character);
		while (!in.eof())
		{
			cout << character;
			out.put(character);
			in.get(character);
		}
	}
	/* END OF FUNCTION */

输入输出使用运算符号“>>” 和“<<”

到目前为止, 我们只是对单个character进行读入或者读出文件到内存中。 在对底层, ofstream 和 ifstream 只能处理问价内容为sequneces of bytes. 所以当数据类型为“double” 或者“int”的类型, 在写进文件的时候吗必须转换为character才能正确的写进。 另外将文件中的数据读入到内存的时候, 也需要将这些字符转换回去才能够

正确的输出。

但是, 这样的一个过程是繁琐的。 幸运的是, 我们有两个算子“<<” 和 “>>”, 能够自动的实现这些转换。

例如下面, 初始状态为:



执行语句:

out_stream << 437 << ' ';

导致如下状态:





上面的空格很有必要。 因为这两个运算符的操作比较底层, 在我们处理完每一笔数据的时候(each item of data),加上了空格(或者new-line character),

我们的数据才能够正确的在文件中分隔开。

我们使用符号“>>”将文件中的数据读入到内存(数据中)。 例如初始状态为:



其中“n" 表示在程序中已经声明了一个数据类型为int 的变量。 执行下一句:

in_stream >> n;

我们得到如下新的状态:




Notice, 操作运算元 ”>>“跳过了437之前的空格”“。 It always does this, no matter waht datatype it has been reading or expects to read。

参看如下程序。 改程序首先创建一个成为Integers的文件, 该文件涵盖了整数51, 52, 53, 54 and 55。

#include <iostream>
#include <fstream>

using namespace std;

int main() {
   char character;
   int number = 51;
   int count = 0;
   ofstream out_stream;
   ifstream in_stream1;   /* Stream for counting integers. */
   ifstream in_stream2;   /* Stream for counting characters. */

   /* Create the file */
   out_stream.open("D:\\Integers.txt");
   for (count = 1 ; count <= 5 ; count++)
      out_stream << number++ << ' ';
   out_stream.close(); //关闭

   /* Count the integers in the file */
   in_stream1.open("D:\\Integers.txt");
   count = 0;
   in_stream1 >> number;
   while (!in_stream1.eof()) {
      count++;
      in_stream1 >> number;
   }
   in_stream1.close();
   cout << "There are " << count << " integers in the file,\n";

   /* Count the non-blank characters */
   in_stream2.open("D:\\Integers.txt");
   count = 0;
   in_stream2 >> character;
   while (!in_stream2.eof()) {
      count++;
      in_stream2 >> character;
   }
   in_stream2.close();
   cout << "represented using " << count << " characters.\n";

   return 0;
	}


运行结果为:





不难看出, operator ”>>“会跳过文件中的空格字符, 这里空格字符分开了五个整数。 而函数get(...)

却不会跳过字符。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: