海会网络建设网站,百度推广关键词优化,长春网络公司,建筑行业网站运营方案文章目录 IO流概述iostream 的标准对象C流和C标准库I/O函数的同步 sync_with_stdiofstream 文件流文件流的打开标志二进制读写二进制读写的浅拷贝问题文本读写 字符串流注意 IO流概述 流是指数据的有序传输序列,路表示数据从一个地方流向另一个地方的过程,流可以是输入流也可以… 文章目录 IO流概述iostream 的标准对象C流和C标准库I/O函数的同步 sync_with_stdiofstream 文件流文件流的打开标志二进制读写二进制读写的浅拷贝问题文本读写 字符串流注意 IO流概述 流是指数据的有序传输序列,路表示数据从一个地方流向另一个地方的过程,流可以是输入流也可以是输出流,具体取决于数据的流动方向; 输入流 数据从外部设备(文件,键盘等)流入程序中; 输出流 数据从程序流向外部设备(如显示器,文件等); IO流是指用于处理输入输出操作的流,C中的IO流用于程序与外部环境(用户,文件系统,网络等)之间交换数据的机制; IO流通过标准库中的一组类和对象实现允许程序员以统一的方式处理不同类型的输入输出设备; ios ios是C中的一个基础头文件,包括了所有输入输出流类的功能; 其并不是一个具体的六类型,而是提供了一些基类,如ios_base和ios(basic_ios); 是所有输入输出流类的基石; ios中的类定义了流对象的状态,格式化标志,异常处理等通用功能; istream 该头文件定义了istream类及相关的输入流操作; 流类型是输入流; 其作用为istream类用于处理从输入设备(键盘,文件等)读取数据的操作; 标准输入流对象cin就是istream类的实例; iostream 这个头文件包含了输入输出流的基本类,包括istream和ostream; 流类型是输入输出流,其既包含了输入流也包含了输出流; iostream类是一个组合类,继承自istream和ostream,可以同时进行输入和输出操作,是一个被棱形继承的类; 该头文件通常用来处理标准输入输出流的操作(如cin和cout); ostream 这个头文件定义了处理输出操作的ostream类; 流类型是输出流; ostream类用于将数据输出到输出设备,如屏幕,文件等; 标准输出流对象cout,标准错误流对象cerr,标准日志流对象clog均为该类的一个实例; streambuf 这个头文件定义了一个streambuf类,这是所有流类的基础缓冲区类; 流类型是流缓冲区; streambuf是iostream,ifstream,ofstream等类的核心组成部分,负责具体的数据存取操作; streambuf通常由更高层次的流类,如istream,ostream使用,但它也可悲用户自定义以实现特殊流的缓冲区操作,如自定义的文件格式或网络流操作; fstream 这个头文件定义了处理文件输入输出的流类,包括ifstream,ofstream和fstream; 流类型是文件流; ifstream是输入文件流类,用于从文件读取数据; ofstream是输出文件流类,用于向文件写入数据; fstream是读写文件流类,其与iostream相同既具有读也具有写的功能; sstream 这个头文件定义了用于操作字符串的流类,包括istringstream,ostringstream和stringstream; 流类型是字符串流; istringstream用于从字符串读取数据,ostringstream用于将数据写入字符串,stringstream则用于同时进行字符串的读写操作; 这一系列的库用于对标C语言的一系列操作,一个面向过程一个面向对象; iostream主要对标C语言中的printf,scanf等接口,用于处理从输入设备读取数据的操作与处理需要写入至输出设备的数据; fstream对标C语言中的fprintf,fscanf等接口,用于处理向文件内写入与处理从文件中读取的数据; sstream对标C语言中的sprintf和sscanf等接口,用于处理字符串输入输出的流类; iostream 的标准对象 在C标准库中iostream头文件定义了一些常用的标准流对象,这些对象在全局范围内可用,用于处理常见的输入输出任务; cin 标准输入流对象; 用于从标准输入设备中读取数据(例如键盘); cin是istream类型的一个实例,支持多种输入操作,可以从输入缓冲区中读取字符,整数,浮点数,字符串等不同类型的数据; cin的输入操作是通过提取运算符来完成的,这个运算符也被称为 “流提取” ; cin提供了一系列的接口(具体参考 istream - C Reference (cplusplus.com)): 但最常用的接口还是operator ; arithmetic types (1)
istream operator (bool val);
istream operator (short val);
istream operator (unsigned short val);
istream operator (int val);
istream operator (unsigned int val);
istream operator (long val);
istream operator (unsigned long val);
istream operator (long long val);
istream operator (unsigned long long val);
istream operator (float val);
istream operator (double val);
istream operator (long double val);
istream operator (void* val);
stream buffers (2)
istream operator (streambuf* sb );
manipulators (3)
istream operator (istream (*pf)(istream));
istream operator (ios (*pf)(ios));
istream operator (ios_base (*pf)(ios_base));这个操作符用于从输入流中提取数据并存储到对应的变量中; 操作符会根据变量的类型自动进行推导,这种操作可以针对多种基本数据类型(内置类型,布尔值,容器等)以及一些特殊的类型(指针,streambuf对象等)进行处理; 除此之外cin作为istream的实例,还提供了其他的成员函数,如getline(),ignore(),get()等; #include iostreamint main() {int num;char ch;std::cout Enter a number: ;std::cin num;// 输出读取到的数字std::cout You entered the number: num std::endl;// 忽略输入缓冲区中的下一个字符通常是换行符std::cin.ignore(1, \n);std::cout Enter a character: ;std::cin.get(ch);std::cout You entered the character: ch std::endl;return 0;
}/*运行结果为:$ ./mytest Enter a number: 42You entered the number: 42Enter a character: aYou entered the character: a*/在这段代码中使用流提取读取一个整型数据,调用cin.ignore()成员函数忽略一个字符,即\n; 忽略该字符后该字符不会被下面的cin.get()读取从而程序不会出现不符合需求的现象,否则\n将会被cin.get()读取; 通常情况下这些函数在使用时查看对应的文档即可; 使用cin进行循环输入 在一些OJ题目中需要循环进行输入,这种情况下可以使用while(operator (std::cin,variable))的方式进行输入; int main() {string s1;
while (cins1) {
// while (operator(cin, s1)) {cout s1 endl;}return 0;
}/*$ ./mytest hellohelloworldworld^Z[1] Stopped ./mytest
*/这里的operator其原型为: istream operator (istream is , string str);其返回的是istream的输入流类型引用; 这里返回的输入流类型引用可以作为判断条件的原因是该类有一个operator bool()的重载,来判断流是否出现错误; 同时,istream还有一个operator!(),它返回一个布尔值,指示流是否处于错误状态; 因此当输入成功时,条件为true,循环继续; 当输入失败时,条件为false,循环终止; cout 标准输出流对象; 用于将数据输出至输出设备中(如显示器); cout是ostream类型的一个实例,支持多种输出和写入操作,可以将缓冲区中的整数,浮点数,字符串等不同类型的数据输出(写入)至对应的设备或文件中; cout的输出操作是通过插入运算符来完成的,这个运算符也被称为 “流插入” ; cout提供了一系列的接口(具体参考ostream - C Reference (cplusplus.com)): 与cin相同该对象最常用的成员函数为operator ; arithmetic types (1)
ostream operator (bool val);
ostream operator (short val);
ostream operator (unsigned short val);
ostream operator (int val);
ostream operator (unsigned int val);
ostream operator (long val);
ostream operator (unsigned long val);
ostream operator (long long val);
ostream operator (unsigned long long val);
ostream operator (float val);
ostream operator (double val);
ostream operator (long double val);
ostream operator (void* val);
stream buffers (2)
ostream operator (streambuf* sb );
manipulators (3)
ostream operator (ostream (*pf)(ostream));
ostream operator (ios (*pf)(ios));
ostream operator (ios_base (*pf)(ios_base));该运算符被称为 “流插入” 运算符,将数据从右侧的表达式插入到左侧的输入流中,即std::cout; 同样的操作符会根据变量的类型自动进行推导,这种操作可以针对多种基本数据类型(内置类型,布尔值,容器等)以及一些重载了operator函数的自定义类型; cout是ostream的实例,继承了其原生的一些成员和接口,如刷新缓冲区的内容的flush(),设置浮点数的输出精度的precision()等; int main() {std::cout.precision(3);std::cout 3.14159265 std::endl;std::cout Processing... std::flush;// 一些耗时的操作std::cout Done! std::endl;return 0;
}
/*运行结果为:$ ./mytest 3.14Processing...Done!
*/但通常情况下由于C与C 兼容,编写C程序时常常使用C与C混编的方式,如在使用精度控制的时候就可以使用C语言的精度控制,若是遇到需要使用这些接口的场合查看文档即可; int main() {double d 3.1415626;printf(d %.2f\n, d);return 0;
}
/*运行结果为:$ ./mytest d 3.14
*/cerr,clog cerr,clog与cout一样都是ostream类的一个实例; 其继承了ostream的各个成员及接口; 本质上cerr与clog分别都是用于打印错误信息与打印日志信息的; cerr cerr是不带缓冲的,这意味着当使用cerr打印一个错误信息时该错误信息将被直接打印而不会被存储在缓冲区中,使使用者能够在发生error时第一时间发现对应的错误信息; std::cerr Error: Something went wrong! std::endl;clog clog与cerr不同,默认带缓冲,这意味着当使用clog打印日志信息时日志信息将先被存储在缓冲区中; 直到缓冲区被写满或是显示调用刷新缓冲区才会将对应的日志信息进行打印; std::clog Log: Application started std::endl;cerr与clog默认都是绑定到标准错误输出流stderr的,因此通常会直接被显示在终端中; 如果需要也可以调用其成员函数rebuf()将输出重定向到文件或是其他输出设备; #include iostream
#include fstreamint main() {std::ofstream error_file(errors.log); // 实例化对应的 ofstream 对象std::ofstream log_file(logs.log);std::cerr.rdbuf(error_file.rdbuf()); // 重定向 cerr 到 error_file std::clog.rdbuf(log_file.rdbuf()); // 重定向 clog 到 log_filestd::cerr Error: This will go to errors.log std::endl;std::clog Log: This will go to logs.log std::endl;return 0;
}C流和C标准库I/O函数的同步 sync_with_stdio sync_with_stdio是C标准库中用于控制C流和C标准库I/O函数(如printf,scanf)之间同步行为的函数; 默认情况下C流(如cin,cout)与C标准库的I/O函数是同步的,意味着每次进行输入或是输出操作时C流都会刷新其内部缓冲区并与C标准库的I/O缓冲区同步; 这种同步操作是为了确保使用 C流 和 C标准库 I/O函数时,输出结果的一致,但这种同步操作也会带来一定的性能开销,特别是在需要频繁进行I/O操作的情况下; 可以使用std::ios::sync_with_stdio(false)来关闭 C流 和 C标准库 I/O函数之间的同步; 关闭同步后,C流不再与其内部缓冲区和C标准库I/O缓冲区进行同步; 这可以提高I/O操作的效率,特比是对于大量数据的输入和输出; 注意事项 关闭同步之后,如果在程序中混合使用 C流 和 C标准库 I/O函数时需要小心,以确保不会出现数据竞争或者输出顺序混乱的问题; 一旦关闭同步后就无法再重新开启同步; int main() {// 关闭 C 流和 C 标准库 I/O 函数之间的同步std::ios::sync_with_stdio(false);int age 25;// 使用 C 流输出提示信息std::cout 请输入您的年龄: ;// 使用 C 标准库函数读取用户输入scanf(%d, age);// 使用 C 流输出用户输入的年龄std::cout 您的年龄是: age std::endl;return 0;
}
/*运行结果为:$ ./mytest 27请输入您的年龄: 您的年龄是: 27
*/在这个例子中再关闭同步之后先使用std::cout输出提示信息,然后使用std::scanf读取用户输入; 由于关闭了同步,std::cout的输出缓冲区可能没有被立即刷新到屏幕上; 因此当程序执行到scanf时用户可能看不到提示信息导致程序行为异常; fstream 文件流 C提供了一个文件流用于处理文件输入输出的对象,可以从文件中读取数据到程序中或者将程序中的数据写到文件中; C标准库提供了三种主要的文件流类: ofstream 用于向文件写入数据(输出); ifstream 用于从文件读取数据(输入); fstream 用于同时进行文件的读写操作; 这三个流类存在对应的函数get/read/与put/write/分别对标C语言的fputc/fwrite/fprintf和fgetc/fread/fscanf; 提供三种主要的文件流类主要是支持C的面向对象,即可以支持将类对象中对应的数据写入至文件当中,C语言只能支持将内置类型写入文件内; 流插入和流提取能够更好的支持内置类型和自定义类型; 文件流的打开标志 在C中打开文件时通常使用fstream,ifstream或ofstream来打开一个文件流,本质上是定义一个文件流对象; 以fstream为例; 文件流对象可以通过open成员函数传入一个const char*字符串或是const string类型作为文件名打开; 也可以使用构造函数传入一个const char*字符串或是const string对象作为文件名将文件打开;
/* open 成员函数声名 */
void open (const char* filename,ios_base::openmode mode ios_base::in | ios_base::out);
void open (const string filename,ios_base::openmode mode ios_base::in | ios_base::out);/* 构造函数 */
explicit fstream (const char* filename,ios_base::openmode mode ios_base::in | ios_base::out);
explicit fstream (const string filename,ios_base::openmode mode ios_base::in | ios_base::out);其中mode表示打开文件时的模式,可以使用一些标志来控制文件流的行为; 这些标志与Linux底层的文件打开接口相似,以二进制标志位的方式标明文件的打开模式,即mode a | b | c; 常见的标识有: in 以输入模式打开(读取); out 以输出模式打开文件(写入),如果文件不存在将创建文件; 如果文件存在则清空文件内容(除非另有其他标志影响,如app); binary 以二进制模式打开文件,这意味着数据将以字节流的形式读写,不进行任何格式的转换; ate 打开文件后,将文件指针定位到文件末尾,但可以在文件的任意位置读写数据; app 以追加模式打开文件,数据写入时会自动定位到文件末尾,不会覆盖文件中的现有内容; trunc 如果文件存在,打开时会清空其内容,这是out模式的默认行为; 关闭文件时调用其close成员函数即可; 打开文件后进行读写时同样调用对应的成员函数,write与read; write ostream write (const char* s, streamsize n);这里的const char*类型并不表示将其作为字符串读取,而是取到该数据的地址后将其以二进制的形式写至文件中,写入大小为streamsize n; read istream read (char* s, streamsize n);这里的char *类型同样的是以二进制的形式写进并写至对应的内存中,streamsize n表示需要读取文件的大小; 二进制读写 二进制读写操作是指以二进制格式从文件中读取或向文件中写入数据; 这种方式直接操作数据的内存表示,更加高效的且没有数据格式的转换,特别适合处理如图像,音频,视频文件等需要保持精度数据格式的文件; 在二进制模式下,数据是以字节流的形式直接读写的,这意味着数据从内存传输到文件或者从文件传输到内存时不会进行任何转换; 但由于不经过任何格式的转换且在内存中数据具有类型而在磁盘/硬盘中数据不存在类型所以写入后无法在磁盘中直接进行查看(数据表示不同);
// Date类的定义包含友元函数声明和私有成员变量
class Date {// 友元函数声明允许运算符访问Date类的私有成员friend ostream operator(ostream out, const Date d);// 友元函数声明允许运算符访问Date类的私有成员friend istream operator(istream in, Date d);public:// 构造函数默认值为1年1月1日Date(int year 1, int month 1, int day 1): year_(year), month_(month), day_(day) {}// bool类型转换函数当year_为0时返回false否则返回trueoperator bool() {if (year_ 0) {return false;}return true;}// 打印操作 void Print(){printf(year %d ,month %d ,day %d\n,year_,month_,day_);}private:// 年份int year_;// 月份int month_;// 日期int day_;
};// 重载运算符用于将Date对象输出到ostream
ostream operator(ostream out, const Date d) {out d.year_ - d.month_ - d.day_ endl;return out;
}// 重载运算符用于从istream输入流中读取Date对象
istream operator(istream in, Date d) {in d.year_ d.month_ d.day_;return in;
}// 定义一个结构体testClass包含成员变量_d1,_d2和_date
struct testClass {char _s1[32]; // 字符数组作为字符串用于存储某些数据double _d2; // 双精度浮点类型变量用于存储某些数据Date _date; // Date类型变量用于存储日期
};class BinIO {public:// 构造函数 传入一个 const char* 类型作为文件名BinIO(const char* filename) : filename_(filename) {}// 写void Write(const testClass wt) {// fstream 实例化 一个对象ofs用于打开文件(以二进制的形式)用于写入操作fstream ofs(filename_, fstream::out | fstream::binary);// 调用write成员函数进行写入ofs.write((const char*)wt,sizeof(wt));}// 读void Read(testClass rt) {// fstream 实例化 一个对象ifs用于打开文件(以二进制的形式)用于读取操作fstream ifs(filename_, fstream::in | fstream::binary);// 调用read成员函数进行读取ifs.read((char*)rt,sizeof(rt));}private:string filename_; // 文件名
};这段代码定义了一个用于处理日期的Date类型和一个包含日期及其他数据的结构testClass,并实现了一个用于二进制文件读写的类BinIO; 其中Date类重载了运算符和运算符使得Date对象可以与流(如cout和cin)直接交互; BinIO用于文件的二进制读写操作; 写 int main() {// 创建一个 testClass 对象 t1初始化 _d1 为 hello world// _d2 为 3.14_date 为 2001年1月1日testClass t1{hello world, 3.14, {2001, 1, 1}};// 创建一个 BinIO 对象 b初始化文件名为 log.txtBinIO b(log.txt);// 将 testClass 对象 t1 以二进制形式写入到 log.txt 文件中b.Write(t1);return 0; // 返回 0表示程序正常结束
}这段代码的主要功能是将一个 testClass 对象以二进制形式写入到文件 log.txt 中。 首先使用初始化列表创建了一个 testClass 对象 t1; t1 的 _d1 成员变量为字符串 hello world; _d2 为 3.14_date 成员为 2001年1月1日; 然后创建了一个 BinIO 对象 b,并将文件名 log.txt 传递给它,准备进行文件操作; 接着调用 BinIO 类的 Write 函数,将 t1 的数据以二进制的形式写入到 log.txt 文件中; 运行程序数据以二进制的方式被写至文件当中; 读 int main() {// 创建另一个 testClass 实例 t2用于从文件中读取数据testClass t2;// 从文件 log.bin 中读取数据到 t2 对象b.Read(t2);// 输出读取到的字符串和 double 类型数据cout s1 : t2._s1 d1 : t2._d1 endl;// 输出读取到的 Date 类型数据cout date : t2._date endl;return 0;
}
/*运行结果为:$ ./mytest s1 : hello world d1 : 3.14date :2001 - 1 - 1
*/在这段代码中创建了另一个testClass类型对象t2用于将文件数据进行读取; 读取后依次将t2对象中的内容进行打印; 二进制读写的浅拷贝问题 在进行二进制读写的时候可能会出现浅拷贝问题导致在对数据进行读取的时候出现内存错误; 其他代码与上述无异;
struct testClass {string _s1; // 字符数组作为字符串用于存储某些数据double _d2; // 双精度浮点类型变量用于存储某些数据Date _date; // Date类型变量用于存储日期
};假设testClass类中的_s1是string类型; 由于string是一个容器,其自行将在内存的堆中开辟空间并进行管理; 在管理过程中该string对象被写入至文件中,被写入至文件内的实际上还有该对象在内存中的空间指针(内容可能并未被拷贝); 当分为两个进程对这个文件进行读写操作时(两次执行,一次进行写一次进行读),在进行读的时候原本的string对象的指针会被读取进新的_s1成员中,但指针所指向的空间已经被销毁从而导致野指针问题;
$ ./mytest
Segmentation fault在同一个进程中对文件进行读写操作,可能数据一样会被写入至对应的string对象中但仍会因为野指针出现问题,故在进行二进制的文件读写操作时需要尽可能避免使用容器,否则需要手动将其序列化; 文本读写 文本读写与二进制读写不同,文本读写需要将所有的内容转换为字节流才能写入至文件当中; 对于内置类型而言可以使用C中的to_string将其转换为字符串,在进行读取的时候可以调用C中的stoi,stod函数将其转换为内置类型; C语言可以使用sprintf与sscanf将内置类型转换为自定义类型(尤其是double这种较为复杂的内置类型) 其余代码不变: // 定义一个结构体testClass包含成员变量_s1,_d2和_date
struct testClass {string _s1;double _d1; // 双精度浮点类型变量用于存储某些数据Date _date; // Date类型变量用于存储日期
};class TextIO {public:// 构造函数 传入一个 const char* 类型作为文件名TextIO(const char* filename) : filename_(filename) {}// 写void Write(const testClass wt) {fstream ofs(filename_, fstream::out | fstream::trunc);// 不同的编译器/平台实现不同// 有些平台可以直接使用默认打开方式if (!ofs) {cerr Error opening file for writing! endl;return;}ofs wt._s1 endl;ofs wt._d1 endl;ofs wt._date endl; // 这里调用了 Date 类的 operator 流插入// 调用的本质是因为 fstream 继承于 iostream// 这里将对应的数据写入至ofs文件流中}// 读void Read(testClass rt) { fstream ifs(filename_);ifs rt._s1; // 调用内置类型的流提取ifs rt._d1;ifs rt._date; // 调用Date重载的流提取}private:string filename_; // 文件名
};这段代码其他代码与上文例子相同不变,定义了一个TextIO的文本读写操作,对应的testClass类中的_s1成员的类型换成string类型(); 写 写入操作直接使用流插入操作符即可; 对于内置类型而言会去调用库中的operator流插入; 对于自定义类型而言会去调用在Date中重载的operator流插入; // 重载运算符用于将Date对象输出到ostream
ostream operator(ostream out, const Date d) {out d.year_ - d.month_ - d.day_ endl;return out;
}其中fstream能够调用ostream的本质原因是fstream是由ostream派生出来的; int main() {testClass t1{hello world, 3.14, {2001, 1, 1}};TextIO b(log.txt);b.Write(t1);return 0;
}在这段代码中定义了一个testClass类型对象t1,并创建了一个TextIO类型的对象b类作为文本文件读写操作; 调用Write成员函数,运行结果文件被写入至log.txt文件中; hello world
3.14
2001 - 1 - 1读 对于读操作也是相同,直接调用对应的operator流提取即可; int main() {testClass t;TextIO b(log.txt);b.Read(t);cout t._s1 endl;cout t._d1 endl;cout t._date endl;return 0;
}这里创建一个t用于接收数据,创建一个b对象用于文件的读写; 读写后打印出t的数据; $ ./mytest
hello
0
1 - 1 - 1发现文件读取错误; 这里读取错误的原因有两个; 流插入读取字符串 在流插入读取字符串时,当字符串与字符串之间存在空格时默认该空格为分隔符; 此处的_s1类型为string,内容为hello world; 为避免这个问题可以使用getline单独读取该字符串; void Read(testClass rt) {fstream ifs(filename_);getline(ifs, rt._s1);ifs rt._d1 rt._date;}同样的这里的getline传入的是ifs文件流,其类型fstream是IO流中的子类; 运行结果为: $ ./mytest
hello world
3.14
2001 - 0 - 1Date类写入时格式控制不正确 同样的在进行读取的时候为避免使用流提取的时候不增加过多的操作,在进行文件写入时就需要对格式进行控制; Date类在重载operator时内容为: out d.year_ - d.month_ - d.day_;同样的在进行流提取时,当出现多个空格时空格会被视作分隔符; 因此在进行写入的时候就应该进行格式控制; ostream operator(ostream out, const Date d) {out d.year_ d.month_ d.day_;return out;
}修改完上述两处问题后重新进行写入再进行读取; $ make # 写入文件
g -o mytest Main.cc -g -Wall -stdc11
$ ./mytest # 文件内容正确
$ cat log.txt
hello world
3.14
2001 1 1# 代码修改为读取代码后重新编译
$ make
g -o mytest Main.cc -g -Wall -stdc11
$ ./mytest
hello world
3.14
2001 1 1
# 结果正确同样的可以使用getline,其中getline可以自定义分隔符;
istream getline (istream is, string str, char delim);
istream getline (istream is, string str, char delim);字符串流 C字符串流stringstream是C标准库中的一种流类,用于再内存中处理字符串,属于标准库中的sstream; 字符串流允许我们像处理文件流一样处理字符串,它将字符串作为数据源或数据目的地,使得再内存中进行字符串流更加灵活方便; 文件流是以 内存-磁盘 的方式,字符串流则是以 内存-内存 的方式(内存中数据具有类型); C中的字符串流主要有三种类型: std::istringstream(输入字符串流) 用于从字符串中提取数据,相当于字符串作为输入源; std::ostringstream(输出字符串流) 用于将数据格式化为字符串并存储,相当于将字符串作为输出目的地; std::stringstream(输入/输出字符串流) 可以同时进行输入和输出操作; 这三种类型实际上是对标C语言中的sprintf和sscanf接口,提供了对自定义类型的操作与安全性; 字符串流通常应用于以下几种场景: 字符串的格式化输出 将不同类型的字符串合并成一个字符串; 字符串的解析 将字符串分解成不同的部分(如解析逗号分割的值等); 类型转换 在不同数据类型之间进行安全转换; 假设需要生成一条SQL语句; 利用C语言则是调用sprintf,即:
int main() {char sql[128];char name[24];scanf(%s, name);sprintf(sql, select * from t_test name %s , name);printf(%s\n, sql);return 0;
}
/*运行结果为:$ ./mytest 张三select * from t_test name 张三
*/此处查找的为内置类型,但若是需要查找自定义类型则需要更复杂的操作; C中的字符串流则是可以解决对于自定义类型的操作问题,本质上是重载自定类型的operator和operator,即流插入和流提取操作,而stringstream流则是所有IO流的最底层类,即最后的派生类,可以调用父类封装的成员函数来进行操作;
// Date类的定义包含友元函数声明和私有成员变量
class Date {// 友元函数声明允许运算符访问Date类的私有成员friend ostream operator(ostream out, const Date d);// 友元函数声明允许运算符访问Date类的私有成员friend istream operator(istream in, Date d);public:// 构造函数默认值为1年1月1日Date(int year 1, int month 1, int day 1): year_(year), month_(month), day_(day) {}// bool类型转换函数当year_为0时返回false否则返回trueoperator bool() {if (year_ 0) {return false;}return true;}void Print() {printf(year %d ,month %d ,day %d\n, year_, month_, day_);}private:// 年份int year_;// 月份int month_;// 日期int day_;
};// 重载运算符用于将Date对象输出到ostream
ostream operator(ostream out, const Date d) {out d.year_ / d.month_ / d.day_;return out;
}// 重载运算符用于从istream输入流中读取Date对象
istream operator(istream in, Date d) {in d.year_ d.month_ d.day_;return in;
}int main() {Date d {2023, 12, 1}; // 初始化日期对象string sql;stringstream st;st d; // 将日期对象格式化为字符串sql select * from t_data date ;sql st.str(); // 插入格式化后的日期sql ;cout sql endl; // 输出SQL语句return 0;
}/*运行结果为:$ ./mytest select * from t_test name 2023/12/1
*/ 在这段代码中创建了一个Date类,初始化为{2023, 12, 1}; 创建了一个string对象sql作为sql语句的存储地; 定义了一个stringstream字符串流st用于自定义类型Date的转换; 当使用流插入时将去调用对应的operator将自定义类型中的数据转换为字符串并存储在stringstream字符串流的缓冲区中; 最后使用string类的将字符串进行组装; stringstream的序列化和反序列化 序列化是指将对象的状态转换为一种可存储或传输的格式的过程; 这样可以将对象保存到文件,数据库,或者通过网络传输; 序列化之后的数据可以在以后反序列化(也称为反序列化或解串)回原来的对象,从而恢复对象的状态; stringstream支持一些较为简单的序列化和反序列化; 本质上就是调用自定义类型重载的operator和operator,即流插入与流提取; 但由于stringstream不能很好的支持复杂类型,如标准库的容器等等; 通常情况下复杂内容的序列化和反序列化可以使用第三方库如Json等; 注意 本文中所有代码测试均在Linux-centOS7上进行