(五)标准输入函数
1、输入函数。cin<<函数只适用于非格式化输入。下面是另外一些输入函数。注意其中有一些是cin的成员函数!!!getline有两种用法cin.getline(s,num)或者getline(cin,s,num);
成员函数 |
读取直到遇到... |
字符数 |
添加结束符 |
返回 |
get(s,num) |
EOF,不包括EOF |
最多num-1 |
是 |
istream |
get(s,num,t) |
t或EOF,不包括t和EOF |
最多num-1 |
是 |
istream |
getline(s,num) |
包括EOF |
最多num-1 |
是 |
istream |
getline(s,num,t) |
包括t或EOF |
最多num-1 |
是 |
istream |
read(s,num) |
EOF |
num |
否 |
istream |
readsome(s,num) |
EOF |
最多num |
否 |
count |
gcount |
|
|
|
返回上次非格式化读取操作所读入的字符数 |
ignore() |
|
|
|
忽略一个字符 |
ignore(count) |
|
|
|
忽略count个字符 |
ignore(count, delim(单个字符)) |
|
|
|
忽略count个字符直到delim,舍弃delim |
peek() |
|
|
|
获取即将读取的下一个字符,但不会读入缓冲区 |
unget() |
|
|
|
把上一次读取的字符放回stream缓冲区。如果没有错误,下一次可以读取它。 |
putback(char c) |
|
|
|
检查c是否是上一次读取的字符,如果是就执行unget(),不是就设立badbit甚至抛出异常 |
2、get与getline区别不是很大,但一个明显的区别是get遇到 '\n '字符后便返回,这是 '\n '还在缓冲区中,所以下次读出来的将是 '\n ',解决之道是在第一次调用完cin.get()以后再调用一次cin.get()把'\n'符给读取了,可以组合式地写为cin.get(name,SIZE).get();。而getline遇到 '\n '也返回,但它会把 '\n '从缓冲区里移除掉 所以很多时候用getline方便些。
3、read函数,不会自动加结束符号,要手动添加,否则会出问题,例子如下:
char c[5];
c[4] = 0;//必须手动,否则输出末尾会带有乱码!
cin.read(c, 4);//就算强制输入5个字符,也只会放入4个字符。
cout << c <<endl;
4、上面的成员函数适用于char字符串,比>>要安全,因为明确指定了范围。
5、以下操作会舍弃当前这一行剩余部分:
cin.ignore(numeric_limits<std::streamsize>::max(), ‘\n’);
以下操作舍弃cin所有剩余部分:
cin.ignore(numeric_limits<std::streamsize>::max());
6、直接用stream buffer性能比成员函数好,因为不需要构造sentry对象。另一种方法是使用迭代器istreambuf_iterator。相关内容将在后面提到。
(六)标准输出函数
成员函数 |
作用 |
返回。可以获取返回ostream的状态来监视操作是否成功 |
put(char c) |
把c写到stream |
ostream& |
write(const char* str, count) |
将str中的count个字符写入stream,但不会在EOF处停止 |
ostream& |
flush() |
刷新stream缓冲区,把缓冲区数据全部强制写入设备 |
ostream& |
直接使用streambuffer或者迭代器ostreambuf_iterator效率更高。但是要注意多线程环境下的上锁机制。
对比C函数:int getc(FILE *stream);把数据作为int传回。也可直接转为char。
char ch;
ch = getc(stdin);
int getchar(void);相当于getc(stdin);
返回值可能是EOF,这个需要自己手动判断,C++标准函数已经提供了自动判断,所以用C++的要安全。
char *gets(char*) // 可以接受一个string,可以接收空格并输出,需包含“#include<string>”
读入成功,返回与参数buffer相同的指针;读入过程中遇到EOF(End-of-File)或发生错误,返回NULL指针。所以在遇到返回值为NULL的情况,要用ferror或feof函数检查是发生错误还是遇到EOF。本函数可以无限读取,不会判断上限,所以程序员应该确保buffer的空间足够大,以便在执行读操作时不发生溢出。如果溢出,多出来的字符将被写入到堆栈中,这就覆盖了堆栈原先的内容,破坏一个或多个不相关变量的值,为了避免这种情况,我们可以用fgets()来替换gets()。这个事实导致gets函数只适用于玩具程序。在V7的手册(1979年)中说明:为了向后兼容,gets删除换行符,gets并不将换行符存入缓冲区。
(七)操控器
1、直接跟在<<或者>>后面的。
操控器 |
类别 |
作用 |
flush |
basic_ostream |
刷新输出缓冲区,强制写入设备 |
endl |
basic_ostream |
向缓冲区插入换行符号并刷新 |
ends |
basic_ostream |
向缓冲区插入终止符号 |
ws |
basic_istream |
读取时忽略空格 |
2、一般形式:
ostream& ostream::operator<<(ostream& (*op)(ostream&))
{
return(*op)(*this);
}
简单来说就是调用<<后面的函数指针,对自身进行操作。
比如endl:
std::ostream&std::endl(std::ostream& strm)
{
strm.put(‘\n’);
strm.flush();
return strm;
}
3、自定义模板操控器例子:
template<classcharT, class traits>
inlinestd::basic_istream<charT, traits>& ignoreLine(std::basic_istream<charT, traits>& strm)
{
strm.ignore(std::numeric_limits<int>::max(), strm.widen(‘\n’));
return strm;
}
用法:省略两行
std::cin>> ignoreLine >> ignoreLine;
(八)格式标志函数
成员函数 |
作用 |
setf(flag) |
设置格式标志flag,返回所有原始标志 |
setf(flag,mask) |
配合掩码mask设置flag,返回所有原始标志 |
unsetf(flag) |
清除flag |
flags() |
返回所有已经设立的标志 |
flags(flag) |
以参数flag为新标志,并返回旧标志 |
copyfmt(stream) |
从stream中复制所有格式定义 |
std::ios::basefield是数学进制的掩码,例子:
std::cout.setf(std::ios::hex,std::ios::basefield);
清除所有进制标志,并设立hex标志。
一个实际应用:
ios::fmtflags oldFlags = cout.flags()
cout.setf(std::ios::showpos | std::ios ::uppercase);
cout << std::hex << x<<std:: endl;
cout.flags(oldFlags);
另外也可以使用操控器
操控器 |
作用 |
setiosflags(flag) |
相当于setf(flag) |
resetiosflags(mask) |
清除mask标记的一组标志相当于setf(0,mask) |
用法:
#include <iomanip>//使用操控器必须添加
cout <<resetiosflags(std::ios::adjustfield)
<<setiosflags(std::ios::left);
各类操控器和格式化输出函数请查阅P616-626
(九)文件存取
1、C++文件流,可以手动关闭,可以不用关闭(自动关闭)。但是如果使用了指针,就需要自己删除。
2、文件标志
标志 |
意义 |
in |
读取,ifstream的默认模式 |
out |
写入,ofstream的默认模式 |
app |
写入时添加到尾部 |
ate |
读写时,读写位置移动到文件末尾 |
trunc |
将先前的文件内容移除 |
binary |
不替换特殊字符,这样可以保留其他系统文件格式。如果是二进制文件也应该用他 |
与C函数不同,C函数fopen的标志位必须要设置为a或者a+才不会覆盖原始文件。
组合用法 |
意义 |
C函数 |
in |
读取 |
r |
out |
清空然后改写 |
w |
out|trunc |
清空然后改写(和上面意义) |
w |
out|app |
添加 |
a |
in|out |
读写,位置在起点 |
r+ |
in|out|trunc |
先清空再读写 |
w+ |
成员函数
成员函数 |
意义 |
open(name) |
缺省模式打开 |
open(name, flag) |
以flag模式打开 |
close() |
关闭 |
is_open() |
判断是否打开 |
2、多次使用流对象
ifstream file;
for(int i = 0; i <argc; ++i) {
file.open(argv[i]);
char c;
while(file.get(c)) {
cout.put(c);
}
//另外一种输出方式
std::cout << file.rdbuf();//更快!!
file.clear();//这个很重要!!!
file.close();
}
3、随机读取,g表示get, p表示put。注意函数不会检查是否越界!!!
成员函数 |
意义 |
tellg() |
当前读取位置 |
seekg(pos) |
设置绝对读取位置 |
seekg(offset, pos) |
设置相对pos的偏移读取位置 |
|
|
tellp() |
当前写入位置 |
seekp(pos) |
绝对写入位置 |
seekp(offset, pos) |
设置相对pos的偏移写入位置 |
4、位置常数
常数 |
意义 |
beg |
开头 |
cur |
当前位置 |
end |
结尾 |
用法:
file.seekg(20,std::ios::cur);
5、如果想这样干:执行某个流操作,但想同时自动刷新其他某个流的缓冲区。使用tie函数。
成员函数 |
意义 |
tie() |
返回当前连接的strm |
tie(ostream *strm) |
连接strm,自动刷新strm |
tie(0), tie(NULL) |
取消tie |
std::cin.tie(&std::cout);
std::cout << “....”;
std::cin >> x;//自动清空cout缓冲区
std::cin.tie(static_cast<std::ostream*>(0));//取消tie
6、缓冲区紧耦合
成员函数 |
意义 |
rdbuf() |
返回stream缓冲区 |
rdbuf(streambuf*) |
把streambuf缓冲区安装到当前流,并返回当前流的原始缓冲区 |
注意缓冲区的原来的主人析构的时候会连带析构缓冲区,所有记得把自己的缓冲区还原。
streambuf *oldbuffer = strm.rdbuf();
strm.rdbuf(file.rdbuf);
操作
....
strm.rdbuf(oldbuffer);//还原