流就是若干字节组成字节序列,流操作从一个到另一个移动的过程
流中的内容:二进制数据 ASCII码
C++中用类实现所有流类操作
标准的输入输出流
C++格式控制
字符流
文件流
#include //istream ostream
#include //ifstream ofstream
#include //istringstream ostringstream
//注释是前面头文件的子类
using namespace std;
int main()
{
fstream out; 文件操作流(可读可写)
ifstream iin; 文件操作流(只可读)
return 0;
}
重定向: C语言 freopen函数介绍
对象 | 类型 | 作用 |
---|---|---|
cin | 标准输入 | 从键盘读取,可以重定向 |
cout | 标准输出 | 输出到控制台,可以重定向 |
cerr | 标准错误输出 | 输出到控制台,不可以重定向 |
clog | 标准错误输出 | 输出到控制台,可重定向 |
#include
using namespace std;
int main()
{
cout << "标准输出" << endl;
cerr << "标准错误" << endl;
clog << "标准错误" << endl;
return 0;
}
三种标准输出作用是一样的,只是起不同的说明作用
字符和字符串输入
cout成员函数
put() : 输出一个字符
write(): 输出字符串
cin成员函数
get():输入一个字符
getline: 输入一个字符串
#include
using namespace std;
int main()
{
cout << "标准输出" << endl;
cerr << "标准错误" << endl;
clog << "标准错误" << endl;
//字符输入
cout << "字符输入:" << endl;
int userKey = cin.get(); //相当于c语言的getchar()
cout.put(userKey); //相当于c语言的puts()
char str[10] = { "" };
cout << "字符串输入:" << endl;
while (getchar() != '\n');
cin.getline(str, 10); //10个长度包含\0
cout << str << endl;
cout.write(str, 10);
//cin.getline规定的长度n,少输入无所谓,多输入则只会截取n-1个可见字符(还有一个是尾零)
//cout.write规定的长度n,无论字符串保存了多长的长度,只会输出n-1个可见字符(还有一个是尾零)
return 0;
}
C++格式控制
包含头文件: iomanip
通过对象的形式,一种通过成员的函数形式
对象形式 | 实际含义 |
---|---|
setbase(n) | 设置多少进制输出整数(参数是8和16) |
setw(n) | 设置输出数据宽度(默认对齐是右对齐,不足补空格) |
setiosflags(ios::left) | 设置对齐方式: ios::left ,ios::right |
setprecition(n) | 单纯使用是控制有效位数,如果控制小数位数结合fixed |
setfill(n) | 填充字符 |
//进制输出
cout << setbase(16) << 32 << endl;
cout << setbase(8) << 32 << endl;
cout <
//默认右对齐
cout << setw(10) << "姓名" << setw(10) << "年龄" << setw(10) << "编号" << endl;
cout << setw(10) << "小芳" << setw(10) << 17 << setw(10) << 119911 << endl;
cout << setiosflags(ios::left); //设置左对齐
cout << setw(10) << "姓名" << setw(10) << "年龄" << setw(10) << "编号" << endl;
cout << setw(10) << "小芳" << setw(10) << 17 << setw(10) << 119911 << endl;
cout << setprecision(4) << 300.12345 << endl; //直接用控制的是有效位数
cout << fixed << setprecision(4) << 300.12349 << endl; //小数位数
包含头文件: sstream
istringstream
ostringstream
stringstream
一般处理字符流的时候用的是stringstream类型的对象
获取字符流中的stirng
string str(); //获取string
void str(const string& str) //重置流对象中字符串
字符流做什么
数据类型的转换
数据的分割
stringstream stream("ILoveyou");
cout << stream.str() << endl; //不可直接cout(cout<> str; //将stream中的字符流入str中
cout << str << endl;
stream.str(""); //清除
字符流还可以做数据转换,字符串转整数或者整数转字符串
//数据类型转换
//整数转字符串,字符串转数字
string num = to_string(123); //string自带的将整数转为字符串
cout << num << endl;
int inumber = 12123;
char result[20] = { "" };
stringstream buf(result); //开辟一个空间大小和result一样
buf << inumber; //将inumber的整型数字流入buf中
buf >> result; //再将buf中的字符串流出到result中
cout << result << endl; //就可以做到数据类型转换
//字符串转换成整型有溢出错误,需要自己手动处理
stringstream strNum("12345435");
int dataNum = 0;
strNum >> dataNum;
cout << dataNum << endl;
当我了解到这种字符串与整型之间相互转换的时候,突然想到了leetcode的一题,然后拿着试一试的想法去做做看,没想到还真的可以AC
字符流还可以做数据切割
//数据切割(流中默认空格作为单一数据的间隔)
stringstream ip("ip: 192.168.1.1");
char strip[20] = { "" };
ip >> strip; //ip: 拿出来
int ipNum[4]; //存IP地址的四组整型数据
char userKey; //存整型数据之间的符号"."
ip >> ipNum[0];
ip >> userKey;
ip >> ipNum[1];
ip >> userKey;
ip >> ipNum[2];
ip >> userKey;
ip >> ipNum[3];
for (int i = 0; i < 4; i++)
{
cout << ipNum[i] << "\t"; //打印结果
}
还需要注意一点,流在做二次或多次转换的时候,必须调用clear清除处理 因为如果不做清除,会导致字符流中的数据有错误,无法将数据正常流出来
包含头文件: fstream
ofstream: 打开文件只能写操作
ifstream: 打开文件只读操作
一般大家创建一个fstream对象,可读可写
打开文件
构造的方式,带参数构造函数:const char* URL,ios::openmode mode(URL是文件名字,ios::openmode是打开文件方式)
成员函数方式: void open(const char* URL,ios::openmode mode)
判断文件打开是否成功
!is_open()函数判断是否打开成功 ,!is_open()是1的打开失败
!文件对象 .!对象是1打开失败
读写方式 | 作用 |
---|---|
ios::in | 读的方式打开文件(相当于c语言的r) |
ios::out | 写的方式打开文件(相当于c语言的w) |
ios::app | 追加写文件(相当于c语言的a) |
ios::ate | 打开已有文件,指针在文件末位 |
ios::trunc | 文件不存在具有创建方式 |
ios::binary | 二进制打开,默认打开方式ASCII码 |
ios::nocreate | 不创建 |
ios::noreplace | 不替换 |
组合方式: 用位或, 可读可写: ios::in|ios::out
关闭文件
close关闭文件
文件读写
直接采用>> <<符号进行读写
采用成员函数读写:read函数和write成员函数
文件指针移动
ifstream文件指针
ifstream& seekg(long int pos);
ifstream& seekg(long int pos,ios_base::seekdir begin);
ofstream文件指针
ofstream& seekp(long int pos);
ofstream& seekp(long int pos,ios_base::seekdir begin);
begin:
ios::beg 开始位置
ios::cur 当前位置
ios::end 结束位置
//打开文件测试
//fstream file("xxoo.txt",ios::in|ios::out|ios::trunc);
//等效下面两行
fstream file;
file.open("xxoo.txt", ios::in | ios::out | ios::trunc);
if (!file || !file.is_open())
{
cerr << "文件打开失败" << endl;
}
file.close();
class MM
{
public:
MM() {}
MM(string name, int age, int num) :name(name), age(age), num(num) {}
void print()
{
cout << name << "\t" << age << "\t" << num << endl;
}
//采用>> <<
void saveFile(string fileName)
{
fstream file(fileName, ios::in | ios::out | ios::app);
if (!file)
{
cout << "打开文件失败!" << endl;
return;
}
file << name << " " << age << " " << num << endl; //直接将数据流入文件中
file.close();
}
void readFile(string fileName)
{
fstream file(fileName, ios::in);
if (!file)
{
cout << "打开文件失败!" << endl;
return;
}
while (true) //循环的方式将文件的所有数据全部读出来
{
MM temp;
file >> temp.name >> temp.age >> temp.num;
if (file.eof()) //注意退出循环的判断写在这,不然会出错
{
break;
}
temp.print();
}
file.close();
}
protected:
string name;
int age;
int num;
};
int main()
{
MM mm("xxx", 18, 1001);
mm.saveFile("mm.txt");
mm.readFile("mm.txt");
return 0;
}
接下来我们使用ASCII码的方式读取文件操作,测试为将文件一的内容读取出来,保存到文件二中
void asciiRWFile(string readFile, string writeFile)
{
//流的方式
//字符或者字符串的
fstream read(readFile, ios::in);
fstream write(writeFile, ios::out);
while (!read.eof())
{
char userkey = read.get(); //getline()
write.put(userkey); //write()函数
//也可以用字符串的方式读取
}
read.close();
write.close();
}
int main()
{
asciiRWFile("文件1.txt", "文件2.txt");
return 0;
}
如果是中文的话,建议用字符串的方式读取,如果用字符读取可能会出错(我就出现了乱码)
用法和cin.getline与cout.write一样
接下来是二进制读取文件
二进制读取文件涉及到一个文件指针的移动去计算该文件的字节数,我们先来讲读取方式
//二进制读写
void binaryRWFile(string readFile, string writeFile)
{
fstream r(readFile, ios::in | ios::binary);
fstream w(writeFile, ios::out | ios::binary);
while (!r.eof()) //这里二进制读取的判断条件要放在while中,与上面的讲的直接用流操作符来读取文件加以区分
{
char str[1024] = { "" }; //缓冲区 开辟一个大小为1024字节的空间
r.read(str, 1024);
w.write(str, strlen(str)); //长度写多少就写入文件多少
}
r.close();
w.close();
}
int main()
{
binaryRWFile("文件1.txt", "文件2.txt");
return 0;
}
接下来来讲一下文件指针移动,这样在二进制读写就可以准确判断文件的大小了,就不会浪费空间
//文件指针移动
int getSize(string fileName)
{
fstream read(fileName, ios::in | ios::binary); //用二进制的方式打开文件
read.seekg(0, ios::end); //seekg函数可以去百度查看参数作用,这里的含义是,
//使文件指针在文本末尾的位置移动零个字节,也就是文本末尾
//这个偏移量可正可负,正是向后移动,负是向前移动
int size = read.tellg(); //返回文本的长度
read.close();
return size;
}
可以发现,我们的测量是准确的,这样就可以做到二进制读取文件了