流
流(stream)是程序输入输出的一个连续的数据序列,它实际上是文件输入输出的一种状态形式。因此,一个C文件就是一个字节流或二进制流。在C语言中,所有的流均以文件形式出现,包括设备文件,这种文件又称为流式文件。
由于相对于内存储器而言,磁盘是慢速设备。在C语言的文件操作中,如果每向磁盘写入一个字节或读出一个字节的数据,都要启动磁盘操作,将会大大降低系统的效率,而且还会对磁盘驱动器的使用寿命带来不利影响。为此在文件系统中往往使用缓冲技术,即系统在内存中为每一个正在读写的文件开辟一个“缓冲区”, 利用缓冲区完成文件读写操作。当从磁盘文件读数据时,应用程序并不直接从磁盘文件读取数据,而是先由系统将一批数据从磁盘取入内存“输入缓冲区”中,然后再由应用程序的读操作从缓冲区依次将数据送给程序中的接收变量,供程序处理。其处理过程如图所示。
在向磁盘文件写入数据时,先将程序中有关变量或表达式的值送到“输出缓冲区”中,待缓冲区装满后,才由系统将缓冲区的数据一次写入磁盘文件中,如上图所示。
这种利用缓冲区将对磁盘文件的频繁逐次访问变为批量访问的做法称为标准文件操作,对应的磁盘文件系统称为缓冲文件系统,也称标准文件系统或高层文件系统。不使用缓冲区的磁盘文件系统成为非缓冲文件系统,也称非标准文件系统或低层文件系统。
标准文件系统功能强,使用方便,由系统代替用户做了许多事情,提供了许多方便。非标准文件系统则直接依赖于操作系统,通过操作系统的功能直接对文件进行操作,因而被称为低层文件系统。
用C语言编程时,若使用非标准文件系统,要求编程者熟悉操作系统,善于利用系统的功能,编程难度大一些,但程序的执行效率高,占用内存资源较少。新的ANSI标准推荐使用标准文件系统。C语言中,无论是使用标准文件系统还是非标准文件系统,都是利用I/O库函数完成文件操作的。
C文件系统可以与各式各样的设备一起使用,缓冲文件系统可以将每个设备转换成逻辑设备,即所谓的流,所有的流工作方式类似。有两种类型的流
文本流
文本流是一系列字符,可以由许多行构成,每行由一个换行符终止。在文本流中,可以根据情况需要对某些字符进行转换。写(或读)的字符与外部设备上的字符可能不是一对一的关系。同样,由于可能发生转换,写(或读)的字符数可能与外部设备上的字符数不同。
二进制流
二进制流是一系列字节,并与外部设备上的字节一一对应。写(或读)的字节数与外部设备上的字节数一样。文件流是一系列独立的字节,没有任何标记用来指明文件的结束或记录的结束。文件的结束是由文件大小来决定的
标准流
每当 C 程序在 DOS 下开始执行时,操作系统将自动打开 5 个专门的流
标准输入流 (stdin)
标准输出流 (stdout)
标准错误流 (stderr)
标准打印流 (stdprn)
标准辅助流 (stdaux)
C++流库简介
C++提供了一系列流类来实现各种输入和输出功能。所以C++的输入和输出功能比C要强大得多,是C输入和输出功能的发展。C++流库中常见的类有:
类名 | 作用 | 所在头文件 |
ios | 是流库中所有类的抽象基类 | iostream |
istream | 输入流的基类 | iostream |
ostream | 输出流的基类 | iostream |
iostream | 输入输出流的基类 | iostream |
ifstream | 文件输入流的基类 | fstream |
ofstream | 文件输出流的基类 | fstream |
iofstream | 文件输入输出流的基类 | fstream |
Istrstream | 字串输入流的基类 | strsstream |
Ostrstream | 字串输出流的基类 | Strsstream |
strstream | 字串输入输出流的基类 | strsstream |
C++标准输入和输出
C++的标准输入和输出及文件输入和输出是通过对象来实现的,这些对象放在标准C++流库中。标准输入功能由C++流库的全局对象cin来实现,该对象重载了一系统流操作符>>,用于各种数据的输入,例子:
intx;
cin>>x;
实现了从标准输入设备上输入一个整型数。该重载运算可以连写,以便输入多种数据,例子:
intx;
doubley;
floatz;
cin>>x>>y>>z;
可以输入三各数,放到相应的变量,上述代码的运行中,操作员如何区别各个输入变量呢?通过回车銉,每输入一种数据,按回车,代码就知道要输入下一个数据,也可以通过空格来区别,即每输入一个数据,按一个空格銉。
标准输出功能由C++流库的全局对象cout来实现。例子:
cout<<”HelloWorld”<<endl;
表示向标准输出流输出一个字串,endl表示一个格式控制符,表示将流内部的缓冲区刷新输出。cout支持的格式控制符有:
dec 设置以十进制输出整数
hex 设置以十六进制输出整数
oct 设置以八进制输出整数
setbase(n) 设置以给定的数制输出数据
setfill(c) 设置填充字符为c
setprecision(n) 设置实数的显示精度为n位
setw(n) 设置字段宽度为n
setiosflags(ios::fixed) 设置实数以固定的小数位输出
setiosflags(ios::scientific) 设置实数以科学计数法显示
setiosflags(ios::left) 设置数据左对齐
setiosflags(ios::right) 设置数据右对齐
setiosflags(ios::skipws) 与setfill(c)相反,取消前导的字符
setiosflags(ios::uppercase) 将输出的字符转为大写
setiosflags(ios::showpos) 输出正数时显示+
resetflags() 取消已设置的输出格式状态
由于cout是一个全局对象,所以我们也可以通过对象的成员函数来控制输出的格式状态,上述有参数的格式字符可以用相似的成员函数来替换。如:precision(n),width(n) ,fill(n),setf(),unsetf()。格式控制符中流格式状态在类ios中定义:
控制对齐状态:
ios::left
ios::right
ios::internal
控制数制输出:
ios::dec
ios::hex
ios::oct
控制显示状态
ios::showbase
ios::showpoint
ios::uppercase
ios::showpos
控制计数法
ios:: scientific
ios::fixed
特殊控制
ios:: unitbuf每次输出后,刷新的所有的流
ios:: stdio 每次输出后,清除stdout,stderr
w+b 创建二进制文件进行读/写操作
a+b 追加二进制文件进行读/写操作
除上述标准流外,C++还提供了其它的标准流对象:cerr, clog, 它们的使用方法与cout完全相同,只是用途不同。一个是标错误流,另一个是标准注册流。
文件输入和输出
C++中文件的概念与C中完全相同,只是文件操作是通过对象来实现其功能的。
文本文件的操作
文件的打开和关闭
实际是建立一个文件流对象,例子:
Ofstreamof(“f1.dat”,ios::out);
或者:
Ofstreamof;
Of.open(“f1.dat”,ios::out)
第一个参数表示文件名称,第二个参数表示文件的输入输出方式见表。
文件操作方式 | 该操作方式的详细说明 |
ios::in | 以输入方式打开文件 |
ios::out | 以输出的方式打开文件,这是默认的方式,如果以有该文件名称,则将文件有的内容全部删除 |
ios::app | 以输出的方式打开文件,并将写入的数据加到文件款尾 |
ios::ate | 打开一个已有的文件,并将文件指针指向文件未尾 |
ios::trunc | 打开一个文件,如果文件已在,则删除其全部内容,如果文件不在,则建立新文件。如果已指定了ios::out, 未指定ios::app ios::ate, ios::in,则同时使用默认方式 |
ios::binary | 以二进制方式找开文件,如不指定,默认方式是ASCII方式,文本方式 |
ios::nocreate | 打开一个已有文件,文件不在则失败 |
ios::noreplace | 打开一个文件,文件不在则新建,文件在则失败 |
ios::in|ios::out | 以输入输出方式打开文件,文件可读写 |
ios::out|ios::binary | 以二进制方式打开一个文件输出 |
ios::in|ios::binary | 以二进制方式打开一个文件输入 |
文件的关闭
调文件流对象的相应成员函数实现
Of.close()
对文本文件的操作
C++中文本文件的定义和C中完全相同,但其操作更简单。在C++中文本文件的操作有两种
通过流的输入输出运算符实现文本文件的读写C++定义的一系列流的输入运算符>>,和流的输出运算符<<,可以用运算来实现文本文件的读写。其使用方法和我们在上面讲的cout和cin中运算符完全相同。例子
ofstream of;
of.open(“f1.dat”,ios::out)
of<<”Hello World”<<endl;
of.close();
ifstream iff;
iff.open(“f1.dat”,ios::out)
char* str[100];
if>>str;
if.close();
通过流对象的成员函数实现
主要调用成员函数put, get getline putline
二进制文件的操作
Read/write函数读写二进制文件
istream&read(char* buffer, int len)
ostream&write(const char* buf, int len)
文件指针操作函数
成员函数 | 作用 |
gcount() | 最后一次输入所读入的字节数 |
tellg() | 文件指针的当前位置 |
Seekg(位置) | 将输入文件中的指针移到指定的位置 |
tellp() | 返回输入文件中的当前指的位置 |
seekp(位置) | 将输出文件中的指针移到指定位置 |
seekp(移位量,参考位置) | 以参照位置为基,移动多个字节,参考位置有: ios:beg----文件开头 ios:cur――文件当前位置 ios:end――文件结束 |
|
随机访问二进制文件
上述两组函数的结合使用。就可实现文件的随机访问读写。
字符流的操作
字串流对象有三种,输入字串流,输出字串流,输入输出字串流。字串流不以文件作为输入输出对象,而是以一个内存缓冲区或者一个内存数据为对象来进行输入和输出,所以字串流也称内存流。字串流也有相应的流缓冲区。在开始建立流对象时,流缓冲区是空的。当向内存输入和输出数据时,流对象先将数据置于流缓冲区中,只有当流缓冲区満了,或遇到了一个换行符,流对象才会将流缓冲区中的数据存入内存。
建立字串流对象
语法:
ostrstream::Ostrstream(char* buffer, int n, int mode = ios::out)
istrstream::istrstream(char* buffer, int n)
strstream::strstream(char* buffer, int n, int mode = ios::out)
其中第一个参数定义一个内存冲区,并将字串流与该内存关联起来。第二个参数定义流缓冲区的大小,一般定义为与内存的大小一致。第三个参数定义该流对象的操作模式。
对字串流的读写
如果建立了字串流对象,对其的操作将和一般的标准流操作是相同的,例子:
char MYstr[100];
ostrstreamos(MYstr,100)
os<<”HelloWOrld”<<endl;
os.close();
cour<<MYstr<<endl;
实例分析
实例1 实现一个自定义的可变参函数
目的和功能:
功能:模拟printf实现一个简化的格式化数据输出函数
完整代码:
#include<iostream>
usingnamespace std;
#include<stdio.h>
#include<stddef.h>
#include<stdarg.h>
void minprintf(char* fmt, ...)
{
va_list ap;
char *p, *sval;
int ival;
double dval;
va_start(ap,fmt);
for (p=fmt; *p;p++)
{
if (*p !='%')
{
putchar(*p);
continue;
}
switch (*++p)
{
case 'd':
ival = va_arg(ap,int);
printf("%d",ival);
break;
case 'f':
dval = va_arg(ap,double);
printf("%f",dval);
break;
case 's':
for (sval =va_arg(ap,char*);*sval;sval++)
putchar(*sval);
break;
default:
putchar(*p);
break;
}
}
va_end(ap);
}
我们发现系统函数printf是一个可变参的函数,因为当要输出多个数据时,参数都可通过printf来输出。如何实现我们自己的可变参函数呢?在C语言提供了标准的方法。该方法涉及的内容放地头文件中#include <stdarg.h>,自定义可变参函数实现步骤如下:
可变参函数的函数原型定义
如:void minprintf(char* fmt, ...)
va_list
是标准C语言提供的一个数据结构,它是一个列表,用来存放可变参的参数
如何将函数中定义的可变长参数存入该数据结构中
va_list ap
调用系统函数va_start(ap,fmt),可将参数fmt中的可变参参数置入列表中
如何读取一个可变参参数的实参
在列表中是通过顺序读取的,每一个参数还要指定其类型。如下:
intival
ival= va_arg(ap,int);
最后可变参函数实现完成后,要清除列表数据结构va_list
使用系统函数可完成该功能va_end(ap)
上述例子将可变参参数用va_start置入va_list表中后,通过一个for循环来访问格式串中的每一个字符,当遇到%符号时,我们就知道要操作第一个可变参数,我们就进一步分析其后的数据类型字符,来决定用va_arg函数将相应类型的参数取出来使用。
实例2 实现一个随机数的产生
目的和功能:实现一个随机数的产生并显示之
完整代码:
#include<iostream>
usingnamespace std;
#include<stdio.h>
#include<stddef.h>
#include<stdlib.h>
#definefrand() ((double)rand())/(RAND_MAX+1.0)
intmain()
{
int randnum[100];
double dnum[100];
long ii = RAND_MAX; //32767
for (int i=0;i<100;i++)
{
randnum[i] = rand();
dnum[i] = frand();
printf("intrand%d is %d\n", i,randnum[i]);
printf("doublerand%d is %f\n",i, dnum[i]);
}
return 0;
}
在C语言中,经常要进行随机数的生成和读写。C语言提供了float rand(void)随机数生成函数,该函数返回一个0-RAND_MAX之间的一个随机实数。RAND_MAX值为32767。可以使用该函数实现任意种类的随机数。要实现一个在0-1之间的随机数据,可以通过表达式rand())/(RAND_MAX+1.0)来得到。
实例3 编写程序,将一个浮点数写入磁盘文件后,再将其读出显示出来
目的和功能:将一个浮点数写入磁盘文件后,再将其读出显示出来
完整代码:
#include<stdio.h>
main()
{
FILE*fp8,fp9
float fdata1=25.68,fdata2;
if((fp8=fopen("datafile8","wb"))==NULL)
//打开文件
{
printf("cannot open thefile.\n");
//出错退出
exit(1);
}
//写文件
fwrite(&fdatal,sizeof(float),1,fp8);
//关闭文件
fclose(fp8);
if((fp9=fopen("datafile8","rb"))==NULL)
{
printf("cannot open thefile.\n");
exit(1);
}
//读文件中的数据到变量中
float f;
fwrite(&f, sizeof(float),1,fp9);
//显示读出的变量结果
printf("从文件中读出的变量值为:%f.\n", f);
}
文件打开函数返一个文件结构指针FILE*,记录着文件打开的信息,以便于对于文件进行操作,在函数fopen可以定各种打开文件的选项,以控制对文件的操作。将上述代码键入计算机,验证一下输入的值是否和输出的值一致。