c++中的输入输出流(标准IO,文件IO,字符串IO)
目录
(1)I/O概述
I/O分类
不同I/O的继承关系
不同I/O对应的头文件
(2)iostream 标准I/O流
iostream头文件中的IO流对象
iostream头文件中重载了<<和>>
缓冲区示意图
标准输入流 cin用法
cin:按空格或者换行符分隔
cin.get():一个一个字符处理
cin.getline 按换行符分隔
cin.ignore 消除缓冲区前n个字符
cin.peek() 判断缓冲区是否有数据
cin.putback(mychar) 将mychar放入缓冲区的最左边
标准输出流 cout 用法
cout.put(mychar) 输出一个字符
cout.write 将字符串的指定长度输出到屏幕上
格式化输出
(3)fstream 文件I/O流
1.文件流类和对象
2.文件读写操作
打开文件
文件打开模式
二元或运算符"|"
读不存在的文件时不会报错,直接跳过
is_open() 检查文件能否打开
good()方法
关闭文件
操作文件例子
3.两种类型文件的读写
C++对ASCII文件的读写操作
C++对二进制文件的读写操作
(4)sstream字符串流用法
1.stringstream
stringstream对象的构造与基础操作
用stringstream和getline分割字符串
用stringstream进行数据类型转换
2.istringstream、ostringstream
3.getline()与cin.getline()函数用法详解
getline()函数
cin.getline用法
4.string字符串类用法
(5)综合应用例子:读取配置文件
(1)I/O概述
I/O分类
C++输入输出包含以下三个方面的内容:
- 标准I/O:对系统指定的标准设备的输入和输出。一般从键盘输入数据,输出到显示器屏幕。这种输入输出称为标准的输入输出,简称标准I/O。
- 文件I/O:以外存磁盘文件为对象进行输入和输出,即从磁盘文件输入数据,数据输出到磁盘文件。以外存文件为对象的输入输出称为文件的输入输出,简称文件I/O。
- 串I/O:对内存中指定的空间进行输入和输出。通常指定一个字符数组作为存储空间(实际上可以利用该空间存储任何信息)。这种输入和输出称为字符串输入输出,简称串I/O。
不同I/O的继承关系
在C语言中,用scanf和printf进行输入输出,往往不能保证所输入输出的数据是可靠的安全的。
在C++的输入输出中,编译系统对数据类型进行严格的检查,凡是类型不正确的数据都不可能通过编译。因此C++的I/O操作是类型安全(type safe)的。
C++通过I/O类库来实现丰富的I/O功能。C++编译系统提供了用于输入输出的iostream类库。i-o-stream,意为输入输出流。
ios是抽象基类,由它派生出istream类和ostream类,两个类名中第1个字母i和o分别代表输入(input)和输出(output)。 istream类支持输入操作,ostream类支持输出操作, iostream类支持输入输出操作。iostream类是从istream类和ostream类通过多重继承而派生的类。
类的继承关系如下:
C++对文件的输入输出需要用ifstream和ofstream类,两个类名中第1个字母i和o分别代表输入和输出,第2个字母f代表文件 (file)。ifstream支持对文件的输入操作, ofstream支持对文件的输出操作。
不同I/O对应的头文件
- iostream 用于标准I/O操作。
- fstream 用于文件I/O操作。
- strstream 用于字符串流I/O。
- stdiostream 用于混合使用C和C + +的I/O机制时,例如想将C程序转变为C++程序。
- iomanip 在使用格式化I/O时应包含此头文件。
(2)iostream 标准I/O流
iostream头文件中的IO流对象
在 iostream 头文件中定义的类有 ios,istream,ostream,iostream,istream _withassign, ostream_withassign,iostream_withassign 等。
在iostream头文件中不仅定义了有关的类,还定义了4种流对象
对象 | 含义 | 对应设备 | 对应的类 | c语言中相应的标准文件 |
cin | 标准输入流 | 键盘 | istream_withassign | stdin |
cout | 标准输出流 | 屏幕 | ostream_withassign | stdout |
cerr | 标准错误流 | 屏幕 | ostream_withassign | stderr |
clog | 标准错误流 | 屏幕 | ostream_withassign | stderr |
在iostream头文件中定义以上4个流对象用以下的形式(以cout为例):
ostream cout(stdout);
在定义cout为ostream流类对象时,把标准输出设备stdout作为参数,这样它就与标准输出设备(显示器)联系起来,如果有
cout << 3;
就会在显示器的屏幕上输出3。
cout流对象
cout是console output的缩写,意为在控制台(终端显示器)的输出。强调几点。
- cout不是C++预定义的关键字,它是ostream流类的对象,在iostream中定义。
- 用“cout <<”输出基本类型的数据时,可以不必考虑数据是什么类型,系统会判断数据的类型,并根据其类型选择调用与之匹配的运算符重载函数。而不像C语言中用prinf函数输出不同类型的数据,必须分别指定相应的输出格式符。
- cout流在内存中对应开辟了一个缓冲区,用来存放流中的数据。当向cout流插入一个endl时,不论缓冲区是否已满,都立即输出流中所有数据,然后插入一个换行符, 并刷新流(清空缓冲区)。注意如果只插人一个换行符”\n“(如cout<<a<<"\n"),则只输出和换行,而不刷新cout 流。
cerr流对象
cerr流对象是标准错误流,cerr流已被指定为与显示器关联。cerr的 作用是向标准错误设备(standard error device)输出有关出错信息。cerr与标准输出流cout的作用和用法差不多。但有一点不同:cout流通常是传送到显示器输出,但也可以被重定向输出到磁盘文件,而cerr流中的信息只能在显示器输出。当调试程序时,往往不希望程序运行时的出错信息被送到其他文件,而要求在显示器上及时输出,这时应该用cerr。cerr流中的信息是用户根据需要指定的。
clog流对象
clog流对象也是标准错误流,它是console log的缩写。它的作用和cerr相同,都是在终端显示器上显示出错信息。区别:cerr是不经过缓冲区,直接向显示器上输出有关信息,而clog中的信息存放在缓冲区中,缓冲区满后或遇endl时向显示器输出。
cout与cerr区别
- cerr是非缓冲输出流,通过它输出的数据,是不会被缓冲的,也就是你传送一个数据给它,它立即输出,不会延迟。可能是因为这个属性,它常常被用于输出出错信息。也就说错误消息可以直接发送到显示器,而无需等到缓冲区或者新的换行符时,才被显示。一般情况下不被重定向
- cout流在输出可能会对数据进行缓冲,有时可能还需flush()强制它立即输出数据。cout经过缓冲后输出,默认情况下是显示器。这是一个被缓冲的输出,是标准输出,并且可以重新定向。
iostream头文件中重载了<<和>>
在iostream中只对"<<"和">>"运算符用于标准类型数据的输入输出进行了重载,但未对用户声明的类型数据的输入输出进行重载。如果用户声明了新的类型,并希望用"<<"和">>"运算符对其进行输入输出,按照重运算符重载来做(重载<<参考:【c++】cpp运算符重载_cpp重载运算符-CSDN博客)。
<<和>>本来在C++中是被定义为左位移运算符和右位移运算符的,由于在iostream头文件中对它们进行了重载, 使它们能用作标准类型数据的输入和输出运算符。所以,在用它们的程序中必须用#include命令把iostream包含到程序中。
#include <iostream>
- >> a表示将数据放入a对象中。
- << a表示将a对象中存储的数据拿出。
缓冲区示意图
标准输入流 cin用法
cin:按空格或者换行符分隔
调用cin后,首先判断输入缓冲区是否有数据,初始情况下是没有的,如果没有数据,则用户需要输入数据,输入内容后,按回车结束输入。然后输入内容被放在输入缓冲区。
然后cin将从输入缓冲区中取数据时,遇到空格或者换行符就停止。
#include <iostream>
using namespace std;void test1()
{int myInt;long myLong;char mybuf[1024];cin >> myInt; // 输入数据到缓冲区,然后从缓冲区读取数据给myIntcin >> myLong; // 缓冲区如果还有数据,直接读取数据给myLongcin >> mybuf; // 缓冲区如果还有数据,直接读取数据给mybufcout << "myInt:" << myInt << ",myLong:" << myLong << ",mybuf:" << mybuf << endl;/*输入:123 654478 hello world输出:myInt:123,myLong:654478,mybuf:hello*/
}int main()
{// cin基本使用test1();/*判断能否输入成功if (cin >> x) {}一般能成功,所以这里没判断*/return 0;
}
cin.get():一个一个字符处理
示例1:输入一个字符串,读取前面部分字符
#include <iostream>
using namespace std;void test21()
{char a, b, c;cin.get(a);cin.get(b);cin.get(c);cout << a << b << c;
}int main()
{test21();return 0;
}
运行结果
PS D:\BaiduSyncdisk\cpptest> ./main
hello world
hel
PS D:\BaiduSyncdisk\cpptest> ./main
示例2:不断输入一个字符串,循环读取全部字符。如果读到'q',则退出
#include <iostream>
using namespace std;void test22()
{char ch;while ( (ch=cin.get()) != 'q' ){cout << ch;}
}int main()
{// cin.get(): 一次读取一个字符test22();return 0;
}
注意:回车带来的这个换行符也被当做输入的一部分
cin.getline 按换行符分隔
#include <iostream>
using namespace std;// cin.getline(buf, size)函数可以接受空格
void test3()
{char buf1[256];char buf2[256];cin >> buf1;cin.getline(buf2, 256);cout << "buf1:" << buf1 << ",buf2:" << buf2 << endl;
}int main()
{// cin.getline函数可以接受空格test3();return 0;
}
运行结果
PS D:\BaiduSyncdisk\cpptest> g++ main.cpp -o main
PS D:\BaiduSyncdisk\cpptest> ./main
hello world
buf1:hello,buf2: world
PS D:\BaiduSyncdisk\cpptest>
我们用键盘输入了一个"hello world",首先cin >> buf1;这条语句将空格前的"hello"输入到了buf1,然后剩余部分" world"被输入到了buf2
cin.ignore 消除缓冲区前n个字符
#include <iostream>
using namespace std;// cin.ignore 忽略缓冲区的个数
void test4()
{char buf1[256];char buf2[256];// 请输入一个字符串 含有多个空格 "aa bb \n cc dd"cin >> buf1;// 输入"aa bb \n cc dd"后,"aa"被输入到了buf1,缓冲区还剩下" bb \n cc dd"cin.ignore(2); // 忽略缓冲区的前2个字符,则" bb \n cc dd"变为"bb \n cc dd"cin.getline(buf2, 256); // 然后将缓冲区的剩余数据提取到buf2cout << "buf1:" << buf1 << endl; // buf1:aacout << "buf2:" << buf2 << endl; // buf2:bb \n cc dd}int main()
{test4();return 0;
}
cin.peek() 判断缓冲区是否有数据
判断缓冲区是否有数据,若有数据则返回第一个字符
若没有数据则等待用户输入字符串,字符串进入缓冲区,同时返回缓冲区第一个字符
注意缓冲区第一个字符不会被取走,只是返回第一个字符的拷贝
#include <iostream>
using namespace std;void test5()
{char buf1[256];char buf2[256];char mychar;// 请输入一个字符串 含有多个空格 "aa bb \n cc dd"cin >> buf1; // 输入"aa bb \n cc dd"后,"aa"被输入到了buf1,缓冲区还剩下" bb \n cc dd"cin.ignore(2); // 忽略缓冲区的前2个字符," bb \n cc dd"变为"bb \n cc dd"// 查看缓冲区是否有数据mychar = cin.peek();cout << "mychar:" << mychar << endl; // mychar:bcin.getline(buf2, 256); // 然后将缓冲区的剩余数据提取到buf2cout << "buf1:" << buf1 << endl; // buf1:aacout << "buf2:" << buf2 << endl; // buf2:bb \n cc dd// 再次查看缓冲区是否有数据,没有会提示输入mychar = cin.peek();cout << "mychar:" << mychar << endl; // mychar:h}int main()
{test5();return 0;
}
运行结果
PS D:\BaiduSyncdisk\cpptest> g++ main.cpp -o main
PS D:\BaiduSyncdisk\cpptest> ./main
aa bb \n cc dd
myint:b
buf1:aa
buf2:bb \n cc dd
hello world
myint:h
PS D:\BaiduSyncdisk\cpptest>
cin.putback(mychar) 将mychar放入缓冲区的最左边
cin.putback(mychar): 将mychar放入缓冲区的最左边
例子:根据输入内容的第一个字符判断是读取数还是字符串
#include <iostream>
using namespace std;void test6()
{cout << "Please, enter a number or a word: ";char c = std::cin.get(); // 输入字符串进缓冲区,然后从缓冲区拿第一个字符// 输入的整数和字符串 分开处理if ( (c >= '0') && (c <= '9') ) {int n; //整数不可能 中间有空格 使用cin >> ncin.putback(c);// cin.putback('9');cin >> n;cout << "You entered a number: " << n << '\n';} else {string str;cin.putback(c);//cin.getline(str);getline(cin, str); // 字符串 中间可能有空格 使用 cin.getline();cout << "You entered a word: " << str << '\n';}}int main()
{test6();return 0;
}
运行结果
PS D:\BaiduSyncdisk\cpptest> g++ main.cpp -o main
PS D:\BaiduSyncdisk\cpptest> ./main
Please, enter a number or a word: 123
You entered a number: 123
PS D:\BaiduSyncdisk\cpptest> ./main
Please, enter a number or a word: hello world
You entered a word: hello world
PS D:\BaiduSyncdisk\cpptest>
标准输出流 cout 用法
cout.put(mychar) 输出一个字符
// cout.put(mychar): 输出一个字符
void test1()
{// cout.put(mychar) 会返回一个cout对象cout.put('h').put('e').put('l');
}int main()
{test1();// 输出:helreturn 0;
}
cout.write 将字符串的指定长度输出到屏幕上
#include <iostream>
#include <cstring>
using namespace std;// cout.write: 将字符串的指定长度输出到屏幕上
void test2()
{char p[128] = "hello itcast";cout.write(p, strlen(p)) << endl;cout.write(p, strlen(p) - 4) << endl;cout.write(p, strlen(p) + 4) << endl;
}int main()
{test2();return 0;
}
运行结果
hello itcast
hello it
hello itcast
格式化输出
例子1
#include <iostream>
#include <cstring>
using namespace std;
#include <iomanip> // ios::showbase需要// cout.write: 将字符串的指定长度输出到屏幕上
void test3()
{// 1.普通的coutcout << "hello world" << endl;// 2.使用类成员函数:对下一次的cout进行格式化输出cout.width(10); // 设置输出的宽度是30cout.fill('*'); // 多余位置用*填充cout.setf(ios::showbase); // 显示数进制的基准,比如0x表示16进制cout.setf(ios::internal); // 将填充符号从中间填充cout << hex << 123; // 将123按照10进制输出// 输出效果:0x******7b,确实是10个宽度cout << endl << endl;cout << hex << 123; // 普通输出效果:0x7bcout << endl << endl;// 3.使用控制符:对下一次的cout进行格式化输出cout << "<Start>" << setw(10) << setfill('*') << setiosflags(ios::showbase) //基数<< setiosflags(ios::internal)<< hex<< 123<< "<End>\n"<< endl;// 输出效果:<Start>0x******7b<End>,其中0x******7b占用10个宽度
}int main()
{test3();return 0;
}
例子2
#include <iostream>
#include <cstring>
using namespace std;
#include <iomanip>int main()
{int a;cout << "input a:";cin >> a;cout << "dec:" << dec << a << endl; //以十进制形式输出整数cout << "hex:" << hex << a << endl; //以十六进制形式输出整数acout << "oct:" << setbase(8) << a << endl; //以八进制形式输出整数achar pt[128] = "China"; //pt指向字符串"China"cout << setw(10) << pt << endl; //指定域宽为,输出字符串cout << setfill('*') << setw(10) << pt << endl; //指定域宽,输出字符串,空白处以'*'填充double pi=22.0/7.0; //计算pi值cout << setiosflags(ios::scientific) << setprecision(8); //按指数形式输出,8位小数cout << "pi=" << pi << endl; //输出pi值cout << "pi=" << setprecision(4) << pi << endl; //按指数形式输出,4位小数cout << "pi=" << setiosflags(ios::fixed) << pi << endl; //改为小数形式输出return 0;
}
运行结果
input a:123
dec:123
hex:7b
oct:173China
*****China
pi=3.14285714e+00
pi=3.1429e+00
pi=0xc.9249249249248p-2
(3)fstream 文件I/O流
1.文件流类和对象
和文件有关系的输入输出类主要在fstream.h这个头文件中被定义,在这个头文件中主要被定义了三个类,由这三个类控制对文件的各种输入输出操 作,他们分别是ifstream、ofstream、fstream,其中fstream类是由iostream类派生而来,他们之间的继承关系见下图所 示。
- ifstream类,它是从istream类派生的,用来支持从磁盘文件的输入。
- ofstream类,它是从ostream类派生的,用来支持向磁盘文件的输出。
- fstream类,它是从iostream类派生的,用来支持对磁盘文件的输入输出。
由于文件设备并不像显示器屏幕与键盘那样是标准默认设备,所以它在fstream.h头文件中是没有像cout这样的预先定义的全局对象,所以我们必须自己定义一个该类的对象。
2.文件读写操作
打开文件
有两种形式
第一种:先创建输出文件流类对象,然后调用该对象的open函数打开文件
ofstream outfile; //定义ofstream类(输出文件流类)对象outfile
outfile.open("f1.dat", ios::out); //使文件流与f1.dat文件建立关联
第2行是调用输出文件流的成员函数open打开磁盘文件f1.dat,并指定它为输出文件, 文件流对象outfile将向磁盘文件f1.dat输出数据。ios::out是I/O模式的一种,表示以输出方式打开一个文件。或者简单地说,此时f1.dat是一个输出文件,接收从内存输出的数据。
第二种:在定义文件流对象时指定参数进行构造
在声明文件流类时定义了带参数的构造函数,其中包含了打开磁盘文件的功能,像下面这样。一般多用此形式,比较方便。
ostream outfile("f1.dat",ios::out);
文件打开模式
文件打开模式是决定如何使用文件的设置。openmode 类型定义在一个名为 ios 的流相关类中。这种类型的值是 ios 类的静态常量成员。每个这样的值表示一个标志或一个可以在文件打开时设置的选项。
文件模式标志 含 义
- ios::app 追加:输出将始终发生在文件的末尾。如果只设置了app则默认也指定out。需trunc没被设定。
- ios::ate 打开文件后立即定位到文件末尾
- ios::binary 二进制:读取或写入文件的数据是二进制形式的
- ios::in 输入:文件将允许输入操作。如果文件不存在,打开将失败。不能是ofstream
- ios::out 输出:文件将允许输出操作。如果文件不存在,则创建一个给定名称的空文件。不能是ifstream。默认会同时trunc。
- ios::trunc 截断:如果打开的文件存在,其内容将被丢弃,其大小被截断为零。必须设置了out。
二元或运算符"|"
二元或运算符 | 可以用来结合两个或更多标志的效果。例如,以下打开模式将导致打开的文件既可以输入也可以输出,并且输出最初在文件的末尾进行:
fstream inOutFile;
outFile.open("inout.txt",ios::in | ios::out 丨 ios::ate);
读不存在的文件时不会报错,直接跳过
以下例子运行将不会提示错误
// file: main.cpp#include <iostream>
#include <fstream>
#include <string>using namespace std;int main()
{string filepath = "./xxxxxxxxxxxx.csv";fstream outfile;outfile.open(filepath, ios::in);string line;while (getline(outfile, line)){cout << line << endl;}outfile.close();return 0;
}
is_open() 检查文件能否打开
功能:返回一个布尔值,表示文件流当前是否关联到一个已打开的文件。
返回值:
- true:文件成功打开。
- false:文件未打开(可能是路径错误、权限不足、文件不存在等原因)。
ifstream srcFile("../config/src.json", ios::binary);if (!srcFile.is_open()) {cout << "Fail to open src.json" << endl;return;}
good()方法
在C++的<fstream>库中,ifstream是用于从文件中读取数据的输入文件流。good()方法是istream(ifstream继承自istream)的一个成员函数,用于检查流的状态是否良好。
具体来说,good()函数会返回一个布尔值,表示流是否处于“良好”状态。在以下情况下,流会被认为是“良好”的:
- 没有到达文件末尾(EOF)。
- 没有遇到任何其他导致流状态为“坏”或“失败”的情况。
和is_open() 的区别:
- 检查内容:
- is_open() 仅检查文件是否成功打开。
- good() 检查文件流是否处于无错误状态(包括文件结束、读写失败等)。
- 使用场景:
- is_open() 通常在文件打开后立即调用,确认文件是否可用。
- good() 通常在文件操作过程中调用,确认流是否仍可正常读写。
#include <iostream>
#include <fstream>int main()
{std::ifstream inputFile("example.txt");if (inputFile.is_open()) {std::cout << "文件打开成功。" << std::endl;std::string line;while (inputFile.good()) { // 检查流是否处于良好状态std::getline(inputFile, line);if (inputFile.good()) { // 确保读取成功后再处理std::cout << line << std::endl;}}inputFile.close();} else {std::cerr << "文件打开失败!" << std::endl;}return 0;
}
关闭文件
在对已打开的磁盘文件的读写操作完成后,应关闭该文件。关闭文件用成员函数close。如
outfile.close(); //将输出文件流所关联的磁盘文件关闭
所谓关闭,实际上是解除该磁盘文件与文件流的关联,原来设置的工作方式也失效,这样,就不能再通过文件流对该文件进行输入或输出。此时可以将文件流与其他磁盘文件建立关联,通过文件流对新的文件进行输入或输出。
操作文件例子
读写csv(csv的本质是英文逗号","隔开分列,换行符"\n"分行的字符串)
①文件的写ofstream
#include <iostream>
using namespace std;#include <string>
#include <fstream>int main()
{string csvPath = "./out.csv";ofstream csvfile;csvfile.open(csvPath, ios::out); //打开模式 用app是追加写入// 或者一步到位// ofstream csvfile(csvPath, ios::out);csvfile << "name,age,grade\n";csvfile << "tom," << 12 << "," << 88 << "\n";csvfile << "bob," << 22 << "," << 60 << "\n";csvfile.close();return 0;
}
②文件的读ifstream
#include <iostream>
using namespace std;#include <string>
#include <fstream>int main()
{string csvPath = "./out.csv";// ofstream csvfile;// csvfile.open(csvPath, ios::out);// csvfile << "name,age,grade\n";// csvfile << "tom," << 12 << "," << 88 << "\n";// csvfile << "bob," << 22 << "," << 60 << "\n";// csvfile.close();ifstream inFile("out.csv", ios::in); string lineStr; while (getline(inFile, lineStr)) // 一行一行读取{// 打印整行字符串 cout << lineStr << endl;}inFile.close();/*// 一个字符一个字符的读char ch;while (inFile.get(ch)) {cout <<ch ;}*/return 0;
}
3.两种类型文件的读写
C++对ASCII文件的读写操作
如果文件的每一个字节中均以ASCII代码形式存放数据,即一个字节存放一个字符,这个文件就是ASCII文件(或称字符文件)。程序可以从ASCII文件中读入若干个字符,也可以向它输出一些字符。
#include <iostream>
using namespace std;
#include "fstream"
#include <cstring>void test1()
{ // 写文件char fname[128] = "./test.txt";ofstream fout(fname, ios::app); //建立一个输出流对象 和文件关联if (!fout){cout << "open " << fname << " error" << endl;return;}fout << "hello....111" << endl;fout << "hello....222" << endl;fout << "hello....333" << endl;fout.close();// 读文件ifstream fin(fname); //建立一个输入流对象 和文件关联char ch;while (fin.get(ch)) // 一个字符一个字符的读{cout <<ch ;}fin.close();}int main()
{test1();return 0;
}
C++对二进制文件的读写操作
二进制文件不是以ASCII代码存放数据的,它将内存中数据存储形式不加转换地传送到磁盘文件,因此它又称为内存数据的映像文件。因为文件中的信息不是字符数据,而是字节中的二进制形式的信息,因此它又称为字节文件。
在打开时要用ios::binary指定为以二进制形式传送和存储。
对二进制文件的读写主要用istream类的成员函数read和write来实现。这两个成员函数的原型为
istream& read(char *buffer,int len);
ostream& write(const char * buffer,int len);
字符指针buffer指向内存中一段存储空间的首地址。len是读写的字节数,具体读多少字节取决于对象的大小,比如结构体等。调用的方式为:
a.write(p1, 50);
b.read(p2, 30);
上面第一行中的a是输出文件流对象,write函数将字符指针p1所给出的地址开始的50个字节的内容不加转换地写到磁盘文件中。
在第二行中,b是输入文 件流对象,read 函数从b所关联的磁盘文件中,读入30个字节(或遇EOF结束),存放在字符指针p2所指的一段空间内。
例子
#include <iostream>
using namespace std;
#include "fstream"
#include <cstring>class Teacher
{
public:Teacher(){age = 0;strcpy(name, "");}Teacher(int _age, char *_name){age = _age;strcpy(name, _name);}void printT(){cout << "age:" << age << ",name:" << name <<endl;}
protected:
private:int age;char name[32];
};void test2()
{ // 二进制文件的写char fname[128] = "./test.dat";ofstream fout(fname, ios::binary); //建一个 输出流对象 和文件关联; if (!fout){cout << "open " << fname << " error" << endl;return ;}Teacher t1(31, (char*)"t31");Teacher t2(32, (char*)"t32");fout.write((char *)&t1, sizeof(Teacher));fout.write((char *)&t2, sizeof(Teacher));fout.close();// 二进制文件的读ifstream fin(fname); //建立一个输入流对象 和文件关联Teacher tmp;fin.read((char*)&tmp, sizeof(Teacher));tmp.printT();fin.read( (char*)&tmp, sizeof(Teacher));tmp.printT();fin.read((char*)&tmp, sizeof(Teacher));tmp.printT();fin.read((char*)&tmp, sizeof(Teacher));tmp.printT();fin.close();// 读取正确个数cout << "----------------" << endl;ifstream fread(fname); //建立一个输入流对象 和文件关联Teacher temp;while (fread.read((char*)&temp, sizeof(Teacher))){temp.printT();}fread.close();}int main()
{test2();return 0;
}
(4)sstream字符串流用法
1.stringstream
stringstream对象的构造与基础操作
#include <iostream>
#include <string>
#include <sstream> // stringstream 头文件using namespace std;int main()
{string str = "1,2,3,4,5";// 1.构造字符串流对象stringstream ss(str);cout << ss.str() << endl; // 输出字符串// 2.用多个字符串放入mysstream中stringstream mysstream;mysstream << "first"<< " "<< "second";mysstream << " third times";cout << mysstream.str() << endl; // first second third times// 清空mysstream.str("");// 再次输入mysstream << "one more time";cout << mysstream.str() << endl; // one more timereturn 0;
}
用stringstream和getline分割字符串
#include <iostream>
#include <string>
#include <sstream> // stringstream 头文件
#include <vector>
#include <queue>using namespace std;int main()
{string str = "1,2,3,4,5";cout << str << endl;// 1.将字符串str弄到字符串流ss中stringstream ss(str);// 2.定义一个临时字符串用来不断保存分割后的子串string item;// 3.分割,每调用一次getline就分割一次while (getline(ss, item, ',')) // 用','作为行的分隔符{cout << item << " ";// 对字符串item还可以继续用字符串流操作}return 0;
}
用stringstream进行数据类型转换
#include <iostream>
#include <string>
#include <sstream> // stringstream 头文件using namespace std;int main()
{int a = 865;string sa = "age";// 将一个整形变量转化为字符串,存储到string类对象中stringstream s;s << a;s >> sa;cout << sa << endl; // 865string strValue1;strValue1 = s.str();cout << strValue1 << endl; // 865s.str(""); // 将stringstream底层管理的string对象设置为""// 否则多次转化时,会将结果全部累积在底层string对象中s.clear(); // 清空s, 不清空会转化失败double d = 12.34;s << d;s >> sa;cout << sa << endl; // 12.34string strValue2;strValue2 = s.str();cout << strValue2 << endl; // 12.34return 0;
}
有时需要判断能否输出成功,比如可能会将一个不是整数形式的字符串输出到一个int类型,像下面这样
#include <iostream>
#include <sstream>
#include <string>
using namespace std;int main()
{int x = 10;// 将整数字符串输出到整数类型string line = "xxx";stringstream ssm(line);if (ssm >> x) //判断能否输出成功!!!{/**/}return 0;
}
2.istringstream、ostringstream
C++还引入了ostringstream、istringstream这两个类,要使用他们创建对象就必须包含这个头文件 <sstream>。
- istringstream类用于执行C++风格的串流的输入操作。istringstream对象可以绑定一行字符串,像输入缓冲区那样。
- ostringstream类用于执行C风格的串流的输出操作。有时候,我们需要格式化一个字符串,但通常并不知道需要多大的缓冲区。为了保险常常申请大量的缓冲区以防止缓冲区过小造成字符串无法全部存储,这时我们可以考虑使用ostringstream类,该类能够根据内容自动分配内存,并且其对内存的管理也是相当的到位。
#include <iostream>
#include <sstream>
#include <string>
using namespace std;void main01()
{// 1.按空格分隔符,一个一个输出{string str = "h e l l o";istringstream is(str);string s;while (is >> s)cout << "[" << s << "]" << endl;// 输出结果// [h]// [e]// [l]// [l]// [o]}// 2.按空格分隔符,一次性输出完{string str2 = "h e l l o";istringstream is2(str2);string a, b, c, d, e;is2 >> a >> b >> c >> d >> e;cout << a << b << c << d << e << endl; // hello}// 3.自定义分隔符{string _oline = "EntrustNo,RefNo,StockCode,ExchangeType";istringstream read_l(_oline);string put_l;while (getline(read_l, put_l, ',')){cout << put_l << endl;}// 输出结果// hello// EntrustNo// RefNo// StockCode// ExchangeType}}void main02()
{ostringstream ostr1; // 构造方式1ostr1 << "hello " << 2012 << endl; // 格式化,此处endl也将格式化进ostr1中cout << ostr1.str(); // hello 2012
}int main()
{// istringstream使用方法main01();// ostringstream使用方法main02();return 0;
}
3.getline()与cin.getline()函数用法详解
getline()函数
getline()函数可以让我们很方便的输入一串字符串。getline()不仅简单,而且安全,因为全局函数 getline() 会帮你处理缓冲区用完之类的麻烦。常见的getline()函数语法有两条:
istream& getline (istream& src, string& buffer, char delim = ‘\n’);
其中的src、buffer、delim的意思分别为
- src: 进行读入操作的输入流
- buffer 存储读入的内容
- delim 终结符,默认 '\n'(换行符)
功能:
- 将输入流src中读到的字符存入buffer中,直到遇到终结符delim才结束。
- 函数在输入流src中遇到文件结束符(EOF)或者在读入字符的过程中遇到错误都会结束。
- 在遇到终结符delim后,delim会被丢弃,不存入line中。在下次读入操作时,将在delim的下个字符开始读入。
例子:输入:get?line() 输出:get
#include<iostream>
#include<string>
using namespace std;int main()
{string line;getline(cin, line, '?');cout<<line;return 0;
}
这里可以不断输入,遇到?才结束输入,即使在?之前随便敲回车都不会结束输入,反而是把回车所代表的换行符当做了输入的内容。
cin.getline用法
getline也可以作为成员函数使用:
cin.getline(char *cha, int num, char f);
向cha中输入num个字符,输入过程中达到num-1个数或者提前遇到f字符,输入结束。
例子:输入hello w?orld 输出hello w;输入hello worl?d 输出hello wor
#include<iostream>
#include<string>
using namespace std;int main()
{char line[100];cin.getline(line, 10, '?');cout<<line;return 0;
}
4.string字符串类用法
参考:C++ stl容器之string(字符串类)-CSDN博客
(5)综合应用例子:读取配置文件
配置文件ClientInfo.csv,每一行用第一个逗号分隔为两部分内容,前一个表示变量名,后一个为值
Appid,562111210,
pwd,123456,
entrust_way,7,
qry_mode,1,
user_mac,MAC:48dabcd2298,
user_ip,IIP:10.56.253.69;LIP:10.34.17.69,
main.cpp
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
using namespace std;struct ClientInfo
{string account;string password;int qryMode;char entrust_way;string user_mac;string user_ip;
};ClientInfo CI;int s2int(string s)
{stringstream ss1;int d;ss1 << s;ss1 >> d;return d;
}void client_data_receive(string s, struct ClientInfo &CI) // one line
{int i = 0;istringstream read_l(s);string put_l;string tag;string c;while (getline(read_l, put_l, ',')){if (i == 0){tag = put_l; } else if (i == 1) { c = put_l;} // 赋值if(tag == "account") {CI.account = c;} else if(tag == "password") {CI.password = c;} else if(tag == "qry_mode") {CI.qryMode = s2int(c); } else if(tag == "entrust_way") {CI.entrust_way = c[0]; } else if (tag == "user_mac") {CI.user_mac = c;} else if (tag == "user_ip") {CI.user_ip = c;}i++;}
}void Read_Client_Info()
{char filePath[100] = { "\0" };sprintf(filePath, "%s", "./ClientInfo.csv");ifstream infile;string _oline;infile.open(filePath, ios::in);while (getline(infile, _oline)){client_data_receive(_oline, CI); // 提取每行数据中需要的信息}}int main()
{// 获取客户信息Read_Client_Info();cout << "[Read_Client_Info] account:" << CI.account << endl;cout << "[Read_Client_Info] user_mac:" << CI.user_mac << endl;cout << "[Read_Client_Info] user_ip:" << CI.user_ip.c_str() << endl;return 0;
}
end