『 C++ 』IO流
文章目录
- IO流概述
- iostream 的标准对象
- C++流和C标准库I/O函数的同步 sync_with_stdio
- fstream 文件流
- 文件流的打开标志
- 二进制读写
- 二进制读写的浅拷贝问题
- 文本读写
- 字符串流
- 注意
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 <iostream>int 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 (cin>>s1) { // 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 <fstream>int 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.txt"BinIO 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 - 1
-
Date
类写入时格式控制不正确同样的在进行读取的时候为避免使用流提取的时候不增加过多的操作,在进行文件写入时就需要对格式进行控制;
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 -std=c++11 $ ./mytest # 文件内容正确 $ cat log.txt hello world 3.14 2001 1 1# 代码修改为读取代码后重新编译 $ make g++ -o mytest Main.cc -g -Wall -std=c++11 $ ./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上进行 |
相关文章:

『 C++ 』IO流
文章目录 IO流概述iostream 的标准对象C流和C标准库I/O函数的同步 sync_with_stdiofstream 文件流文件流的打开标志二进制读写二进制读写的浅拷贝问题文本读写 字符串流注意 IO流概述 流是指数据的有序传输序列,路表示数据从一个地方流向另一个地方的过程,流可以是输入流也可以…...

enhanced Input Action IA_Look中Action value引脚没有分割结构体引脚的选项
UE5系列文章目录 文章目录 UE5系列文章目录前言二、使用步骤解决办法 前言 据说,unreal engine5中准备废弃“项目设置”中“输入”,操作映射,轴映射。取而代之的是: 使用增强的输入动作(Enhanced Input Actions&#…...

C# NetworkStream、ConcurrentDictionary、Socket类、SerialPort、局域网IP 和广域网IP
一、NetworkStream 什么是NetworkStream? NetworkStream 是 .NET Framework 中用于在网络上进行数据传输的流类。它属于System.Net.Sockets 命名空间,并提供了通过网络连接进行读写操作的功能。NetworkStream 主要用于处理从网络套接字(Soc…...

大模型--学习范式
1. 自监督学习(Self-Supervised Learning) 概念:自监督学习是一种特殊形式的监督学习,其中标签不是由人工标注的,而是从输入数据本身自动生成的。模型利用这些自动生成的标签进行训练。自监督学习通常用于预训练阶段&…...

Hadoop-yarn-unauthorized 未授权访问漏洞
一、漏洞描述: Hadoop是一款由Apache基金会推出的分布式系统框架,它通过著名的 MapReduce 算法进行分布式处理,Yarn是Hadoop集群的资源管理系统。YARN提供有默认开放在8088和8090的REST API(默认前者)允许用户直接通过…...

工作流调度系统(DolphinScheduler、Azkaban、Airflow、Oozie 和 XXL-JOB)
文章目录 1. DolphinScheduler1.1 DolphinScheduler介绍1.2 DolphinScheduler优点 2. Azkaban2.1 Azkaban介绍2.2 Azkaban优点 3. Airflow3.1 Airflow介绍3.1 Airflow优点 4. Oozie4.1 Oozie 介绍4.2 Oozie 优点 5. XXL-JOB5.1 XXL-JOB介绍5.2 XXL-JOB优点 6. 总结 1. DolphinS…...

PyTorch 基础学习(5)- 神经网络
系列文章: PyTorch 基础学习(1) - 快速入门 PyTorch 基础学习(2)- 张量 Tensors PyTorch 基础学习(3) - 张量的数学操作 PyTorch 基础学习(4)- 张量的类型 PyTorch 基础学…...

CI/CD 自动化:最大限度地提高极狐GitLab 群组的“部署冻结”影响
极狐GitLab 是 GitLab 在中国的发行版,专门面向中国程序员和企业提供企业级一体化 DevOps 平台,用来帮助用户实现需求管理、源代码托管、CI/CD、安全合规,而且所有的操作都是在一个平台上进行,省事省心省钱。可以一键安装极狐GitL…...

单元训练10:定时器实现秒表功能-数组方式
蓝桥杯 小蜜蜂 单元训练10:定时器实现秒表功能-数组方式 /** Description:* Author: fdzhang* Email: zfdcqq.com* Date: 2024-08-15 21:58:53* LastEditTime: 2024-08-16 19:07:27* LastEditors: fdzhang*/#include "stc15f2k60s2.h"#define LED(x) …...

国外项目管理软件最佳实践:选型与应用
国内外主流的10款国外项目管理软件对比:PingCode、Worktile、Asana、Trello、Monday.com、ClickUp、Wrike、ProofHub、Zoho Projects、Hive。 在寻找适合的国外项目管理软件时,你是否感到不知从何选择?市场上琳琅满目的选项往往令人眼花缭乱&…...

Angular组件概念
Angular 是一个由 Google 维护的开源前端框架,用于构建单页面应用(SPA)和移动Web应用。Angular 应用由多个组件(Components)组成,这些组件是 Angular 应用构建块的基本单位。 1. Angular 组件的基本概念 …...

嵌入式人工智能ESP32(4-PWM呼吸灯)
1、PWM基本原理 PWM(Pulse-width modulation)是脉冲宽度调制的缩写。脉冲宽度调制是一种模拟信号电平数字编码方法。脉冲宽度调制PWM是通过将有效的电信号分散成离散形式从而来降低电信号所传递的平均功率的一种方式。所以根据面积等效法则,…...

继承 (上)【C++】
文章目录 继承的定义继承的语法继承权限和继承到子类后父类成员的访问限定符的变化继承到子类后父类成员的访问限定符的变化 子类继承到了父类的什么?继承中的作用域子类和父类之间的赋值转换子类对象可以直接赋值给父类对象,但是父类对象不能直接赋值给…...

WPF打印控件内容
当我们想打印控件内容时,如一个Grid中的内容,可以用WPF中PrintDialog类的PrintVisual()方法来实现 界面如下: XAML代码如下 <Grid><Grid.ColumnDefinitions><ColumnDefinition/><ColumnDefinition Width"300"…...

[C++][opencv]基于opencv实现photoshop算法图像剪切
【测试环境】 vs2019 opencv4.8.0 【效果演示】 【核心实现代码】 //图像剪切 //参数:src为源图像, dst为结果图像, rect为剪切区域 //返回值:返回0表示成功,否则返回错误代码 int imageCrop(InputArray src, OutputArray dst,…...

四十、大数据技术之Kafka3.x(3)
🌻🌻 目录 一、Kafka Broker1.1 Kafka Broker工作流程1.1.1 Zookeeper 存储的Kafka信息1.1.2 Kafka Broker 总体工作流程1.1.3 Broker 重要参数 1.2 生产经验——节点服役和退役1.2.1 服役新节点1.2.2 退役旧节点 1.3 Kafka 副本1.3.1 副本基本信息1.3.2…...

redis——基本命令
什么是Reids(REmote Dictionary Server) Redis是现在主流的数据库之一,是一个使用ANSI C编写的开源、包含多种数据结构、支持网络的、基于内存、可选持久性的键值对存储数据。 特性 1.速度快 :Redis的数据全部存储瑜内存中。 …...

pytorch实现单层线性回归模型
文章目录 简述代码重构要点 数学模型、运行结果数据构建与分批模型封装运行测试 简述 python使用 数值微分法 求梯度,实现单层线性回归-CSDN博客 python使用 计算图(forward与backward) 求梯度,实现单层线性回归-CSDN博客 数值微分…...

智能小家电能否利用亚马逊VC搭上跨境快车?——WAYLI威利跨境助力商家
智能小家电行业在全球化背景下,正迎来前所未有的发展机遇。亚马逊为品牌商和制造商提供的一站式服务平台,为智能小家电企业提供了搭乘跨境快车、拓展国际市场的绝佳机会。 首先,亚马逊VC平台能够帮助智能小家电企业简化与亚马逊的合作流程&am…...

顺丰科技25届秋季校园招聘常见问题答疑及校招网申测评笔试题型分析SHL题库Verify测评
Q:顺丰科技2025届校园招聘面向对象是? A:2025届应届毕业生,毕业时间段为2024年10月1日至2025年9月30日(不满足以上毕业时间的同学可以关注顺丰科技社会招聘或实习生招聘)。 Q:我可以投递几个岗…...

深入理解 Kibana 配置文件:一份详尽的指南
Kibana 是一个强大的数据可视化平台,它允许用户通过 Elasticsearch 轻松地探索和分析数据。Kibana 的配置文件 kibana.yml 是定制和优化 Kibana 行为的关键。在这篇博客中,我们将深入探讨 kibana.yml 文件中的各个配置项,并提供示例说明。 服…...

算法的学习笔记—链表中倒数第 K 个结点(牛客JZ22)
😀前言 在编程过程中,链表是一种常见的数据结构,它能够高效地进行插入和删除操作。然而,遍历链表并找到特定节点是一个典型的挑战,尤其是当我们需要找到链表中倒数第 K 个节点时。本文将详细介绍如何使用双指针技术来解…...

聊聊场景及场景测试
在我们进行测试过程中,有一种黑盒测试叫场景测试,我们完全是从用户的角度去理解系统,从而可以挖掘用户的隐含需求。 场景是指用户会使用这个系统来完成预定目标的所有情况的集合。 场景本身也代表了用户的需求,所以我们可以认为…...

Spring Web MVC入门(中)
1. 请求 访问不同的路径, 就是发送不同的请求. 在发送请求时, 可能会带⼀些参数, 所以学习Spring的请求, 主要 是学习如何传递参数到后端以及后端如何接收. 传递参数, 咱们主要是使⽤浏览器和Postman来模拟; 1.1 传递单个参数 接收单个参数,在Spring MV…...

Django后端架构开发:后台管理与会话技术详解
🌟 Django后端架构开发:后台管理与会话技术详解 🔹 后台管理:自定义模型类 Django的后台管理系统提供了强大的模型管理功能,你可以通过自定义模型类来控制模型在后台管理界面的显示和操作。自定义模型类通过继承admin…...

挑战Infiniband, 爆改Ethernet(2)
挑战Infiniband, 爆改Ethernet之物理层 前面说过UE为了挑战Infiniband在AI集群和HPC领域的优势地位,计划爆改以太网技术,以适应AI和HPC集群对高性能、可扩展网络的需求。正如UE联盟关于愿景的说明中宣称的:”提供一个完整的架构,通…...

Postman文件上传接口测试
接口介绍 返回示例 测试步骤 1.添加一个新请求,修改请求名,填写URL,选择请求方式 2.将剩下的media参数放在请求body里,选择form-data,选择key右边的类型为file类型,就会出现选择文件的按钮Select Files&a…...

stm32入门学习14-电源控制
有时候我们的程序中有些触发执行条件,有时这些触发频率很少,我们的程序就一直在循环,这样就很浪费电,我们可以通过PWR电源控制来实现低功耗模式,即只有在触发时才执行程序,其余时间可以关闭一些没必要的设备…...

[C++][opencv]基于opencv实现photoshop算法色相和饱和度调整
【测试环境】 vs2019 opencv4.8.0 【效果演示】 【核心实现代码】 HSL.hpp #ifndef OPENCV2_PS_HSL_HPP_ #define OPENCV2_PS_HSL_HPP_#include "opencv2/core.hpp" using namespace cv;namespace cv {enum HSL_COLOR {HSL_ALL,HSL_RED,HSL_YELLOW,HSL_GREEN,HS…...

Github 2024-08-16Java开源项目日报 Top10
根据Github Trendings的统计,今日(2024-08-16统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Java项目10TypeScript项目1Ruby项目1Apache Dubbo: 高性能的Java开源RPC框架 创建周期:4441 天开发语言:Java协议类型:Apache License 2.0St…...