IO流
C++兼容C,所以C中的输入输出函数依然可以在C++中使用,但是直接把C的那套输入输出搬到C++中肯定无法满足C++的需求。
使用cout进行输出
cout格式化
格式设置完成之后将以对应格式打印,直到格式状态设置为其他的选项为止
cout<<showbase; //设置显示进制前缀
cout << dec << "dec:" << 15 << endl;
cout << oct << "oct:" << 15 << endl;
cout << hex <<"hex:"<< 15 << endl;
另外一种对象设置的方法,了解即可,没上边的方便
cout.setf(ios::showbase); //设置显示进制前缀
cout << "dec" << 15 << endl; //默认10进制
cout.unsetf(ios::dec); //取消10进制 一定要取消
cout.setf(ios::hex); //设置16进制
cout << "hex:" << 15 << endl;
调整字节宽度以及填充字符
头文件:
#include <iomanip>
const char* str = "sirius";
cout.width(10); //设置宽度
cout << str << endl;
cout.width(10); //设置宽度
cout.fill('*'); //设置填充字符
cout << str << endl;
cout.precision(2); //设置精度
cout << 3.1415 << endl;
输出结果:
sirius
****sirius
3.1
setf()相关
常量 | 含义 |
---|---|
ios_base::boolalpha | 输入输出bool值,可以为ture或false |
ios_base::showbase | 显示前缀0x01 |
ios_base::showpoint | 显示末尾的小数点 |
ios_base::showpos | 在正数前加+ |
参数 | 含义 |
---|---|
ios_base::fixed | (默认)使用定点计数法 |
ios_base::scientific | 使用科学计数法 |
ios_base::left | 使用左对齐 |
ios_base::right | (默认)使用右对齐 |
ios_base::internal | 符号或者基数前缀左对齐,值右对齐 |
字符串流
头文件
#include <sstream>
#include <iostream>
#include <sstream>
using namespace std;
int main() {
//类型转换
string res = "100";
int num = 0;
stringstream ss;
ss << res;
ss >> num;
cout << num << endl;
//分割字符串+类型转换
string ip = "192.168.0.1";
stringstream ss1(ip);
int a1, a2, a3, a4;
char ch;
ss1 >> a1 >> ch >> a2 >> ch >> a3 >> ch >> a4;
cout << a1 << " " << a2 << " " << a3 << " " << a4 << " " << ch << endl;
//拼接字符串
stringstream ss2;
num = 3;
char str[] = "14159";
ss2 << num << ch << str;
cout << ss2.str() << endl;
return 0;
}
输出:
100
192 168 0 1 .
3.14159
文件流
头文件:
#include<fstream>
文件流常用函数
函数名 | 描述 |
---|---|
open | 打开文件 |
is_open | 文件是否被打开 |
close | 关闭文件 |
getline | 读取一行 |
read | 输入(读)到对象 |
write | 输出(写)到文件 |
flush | 将缓冲区的数据写出到文件,再清空它 |
tellg | 输入(读)文件指针位置 |
tellp | 输出(写)文件指针位置 |
seekg | 设置输入(读)文件指针位置 |
seekp | 设置输出(写)文件指针位置 |
eof | 是否到了流尾部 |
**C++ **和 C的文件打开模式
**C++**模式 | C模式 | 描述 |
---|---|---|
ios_base::in | “r” | 打开以读取 |
ios_base::out | “w” | 等价于ios_base::out | ios_base::trunc |
ios_base::out | ios_base::trunc | “w” | 打开以写入,如果已存在,则截断文件 |
ios_base::out | ios_base::app | “a” | 打开以写入,只追加 |
ios_base::out | ios_base::in | “r+” | 打开以读写,在文件允许的位置写入 |
ios_base::out | ios_base::in | ios_base::trunc | “w+” | 打开以读写,如果已存在,则截断文件 |
ios_base::binary | “b” | 以二进制的形式打开 |
截断文件:在文件写入之前擦除掉所有数据
open第三个参数
模式 | 描述 |
---|---|
_SH_DENYRW | 拒绝读写 |
_SH_DENYWR | 拒绝写 |
_SH_DENYRD | 拒绝读 |
_SH_DENYNO | 可读写 |
_SH_SECURE | 安全模式,向前兼容 |
示例程序:
#include<iostream>
#include<fstream>
using namespace std;
struct Student
{
char name[20];
int num;
int age;
char gender;
};
int main()
{
fstream inFile;
ofstream outFile;
Student stu[3] = { {"张三",1001,18,'M'},{ "李四",1002,18,'M' } ,{ "王二",1003,18,'M' } },temp;
outFile.open("test.txt", ios_base::out);
inFile.open("test.txt", ios_base::in);
if (!outFile) {
cerr << "open error!" << endl;
abort();
}
if (!inFile) {
cerr << "write error!" << endl;
abort();
}
for (int i = 0; i < 3; i++)
{
outFile.write((char*)&stu[i], sizeof(Student));
//outFile << "\n"; //插入换行符
}
outFile.close();
for (int i = 0; i < 3; i++)
{
inFile.read((char*)&temp, sizeof(Student));
cout << temp.age << temp.gender << temp.name << temp.num << endl;
}
inFile.close();
cin.get();
return 0;
}
打包解包
要想将文件打包生成文件,我们需要一张索引表,索引表里面包括关于文件的信息,例如文件名,文件大小…
索引表之后就是文件内容,假设我有四张图片,需要将四张图片打包生成一个.pack包,并且pack包能够被解开获取里面的内容
首先看一下索引表有什么内容
首先是红色区域,占8个字节,存储索引表的信息,索引表的左边是索引表的大小,右边是文件数量
其次是索引表中包含的文件信息,从左到右依次是文件名大小、文件偏移量,文件大小,文件名
文件偏移量:就是资源文件在打包文件中的位置
然后开始关于C++文件操作的打包解包:
#include<iostream>
using namespace std;
#include <fstream>
//索引表大小:4 文件数量:4 文件1大小 文件1偏移 文件1名大小 文件1名 文件2.。。
struct FileInfo
{
int fileNameSize; //文件名字大小
int fileOffset; //文件的偏移量
int fileSize; //文件的大小
char fileName[20]; //文件名
};
void packFile()
{
FileInfo fileList[4] = {
{ 0,0,0,"1.jpg" },
{ 0,0,0,"2.jpg" },
{ 0,0,0,"3.jpg" },
{ 0,0,0,"4.jpg" }
};
fstream file[4];
int listSize = 0; //索引表大小
int fileNum = 4; //文件数量
for (int i = 0; i < 4; i++)
{
fileList[i].fileNameSize = strlen(fileList[i].fileName) + 1; //1是\0
listSize += fileList[i].fileNameSize + 12; //求索引表的大小
file[i].open(fileList[i].fileName, ios_base::in | ios_base::binary); //打开文件
file[i].seekp(0, ios_base::end); //读指针移动到末尾
fileList[i].fileSize = file[i].tellp(); //获取文件大小
file[i].seekp(0, ios_base::beg); //读指针移动到开头
}
//写文件索引信息
fstream outfile("new.pack", ios_base::out | ios_base::binary);
outfile.write((char*)&listSize, 4);
outfile.write((char*)&fileNum, 4);
//写索引表
for (int i = 0; i < 4; i++)
{
if (i == 0)
fileList[i].fileOffset = 8 + listSize;
else
fileList[i].fileOffset = fileList[i - 1].fileOffset + fileList[i - 1].fileSize;
outfile.write((char*)&fileList[i].fileNameSize, 4);
outfile.write((char*)&fileList[i].fileOffset, 4);
outfile.write((char*)&fileList[i].fileSize, 4);
outfile.write((char*)&fileList[i].fileName, fileList[i].fileNameSize);
}
//写文件
char ch;
for (int i = 0; i < 4; i++)
{
while (ch = file[i].get(),!file[i].eof())
{
outfile.put(ch);
}
file[i].close();
}
outfile.close();
}
void unPackFile()
{
fstream inFile;
inFile.open("new.pack", ios_base::in | ios_base::binary);
int listSize; //索引表大小
int fileNum; //文件数量
//读文件索引信息
inFile.read((char*)&listSize, 4);
inFile.read((char*)&fileNum, 4);
FileInfo* fileList = new FileInfo[fileNum];
fstream *file = new fstream[fileNum];
//读索引表
for (int i = 0; i < fileNum; i++)
{
inFile.read((char*)&fileList[i].fileNameSize, 4);
inFile.read((char*)&fileList[i].fileOffset, 4);
inFile.read((char*)&fileList[i].fileSize, 4);
inFile.read((char*)&fileList[i].fileName, fileList[i].fileNameSize);
file[i].open(fileList[i].fileName, ios_base::out|ios_base::binary);
}
for (int i = 0; i < fileNum; i++)
{
for (int j = 0; j < fileList[i].fileSize; j++)
{
file[i].put(inFile.get());
}
file[i].close();
}
inFile.close();
delete[] fileList;
delete[] file;
}
int main()
{
packFile();
//unPackFile();
return 0;
}
需要注意的是怎么写入的怎么读取
fstream outfile("new.pack", ios_base::out | ios_base::binary);//二进制写入
inFile.open("new.pack", ios_base::in | ios_base::binary); //二进制读取