更多参考其他文档菜鸟文档W3C微软C++文档

输入输出和模板

输入输出流相关的类

  • ios 基类
    • istream 用于输入的流类,cin是该类对象
      • ifstream 从文件读取数据操作
      • iostream 既能输入又能输出的类
        • fstream 既能读文件,又能写文件的类
    • ostream 用于输出的流类,cout是该类对象
      • ofstream 向文件写入数据的类
      • iostream

标准流对象

  • 输入流对象:cin 与标准输入设备相连

  • 输出流对象:cout 与标准输出设备相连

  • cerr 与标准错误输出设备相连

  • clog 与标准错误输出设备相连

      			 
    //缺省情况下,相同
    cerr <<"Hello,world"<<endl;
    clog <<"Hello,world"<<endl;
    cout <<"Hello,world"<<endl;
  • cin对应于标准输入流,用于从键盘读取数据,也可以被重定向为从文件中读取数据。

  • cout对应于标准输出流,用于向屏幕输出数据,也可以被重定向为向文件写入数据。

  • cerr对应于标准错误i输出流,用于向屏幕输出错误信息

  • clog对应于标准错误输出流,用于向屏幕输出错误信息

  • cerr和clog的区别在于cerr不使用缓冲区,直接向显示器输出信息;而输出到clog中的信息先会被存放在缓冲区,缓冲区满或者刷新时才输出到屏幕

输出重定向

#include <iostream>

using namespace std;

int main(){
int x, y;
cin >> x >> y;
freopen("test.txt", "w", stdout);
//"w" 表示写入
//stdout是标准输出
//将标准输出重定向到test.txt文件
if(y==0)
cerr << "error." << endl; //错误信息将输出到终端
else
cout << x / y;//输出结果到test.txt
return 0;
}

输入重定向

#include <iostream>

using namespace std;

int main(){
double f,n;
freopen("t.txt", "r", stdin);
//输入重定向到 t.txt文件
//标准输入 stdin
//"r" 写入
cin >> f >> n; //这时就会从文件里读取数据
cout << f << "," << n << endl;
return 0;
}

判断输入流结束

//判断数据是否读完

int x;
while(cin>>x) //强制类型运算符转换的重载,把cin对象强制转换位bool类型的值,所以可以判断true和false
{
.....
}

//这里用到了>>位运算符的重载
istream &operator >> (int a)
{
.....
return *this;
}
  • 如果是从文件输入数据,

    freopen("some.txt","r",stdin);//到文件尾部,输入流就算结束 
  • 如果从键盘输入,在单独一行输入Ctrl+Z代表输入流结束

istream类的成员函数

istream & getline(char * buf,int buffSize);
//从输入流中读取bufSzie-1个字符到缓冲区buf,或读到碰到'\n'为止(哪个先到算哪个)
istream & getline(char *buf,int bufSzie,char delim);
//从输入流中读取bufSize-1个字符到缓冲区buf,或读到碰到delim字符为止(哪个先到算哪个)
两个函数都会自动在buf中读入数据的结尾添加\0'
\n或delim之前的字符个数达到或超过了bufSzie个,就导致读入错误,其结果就是:虽然本次读入已经完成,但是之后的读入就都会失败了
//可以用if(!cin.getline(...))判读输入是否结束

bool eof(); //判断输入流是否结束
int peek(); //返回下一个字符,但不从流中去掉
istream & putback(char c); //将字符ch放回输入流
istream & ignore(int nCount = 1int delim = EOF);//从流中删掉最多nCount个字符,遇到EOF时结束。


#include <iostream>
using namespace std;
int main(){
int x;
char buf[100];
cin >> x; //存入12后,空格留在了缓冲区
cin.getline(buf, 90);
cout << buf << endl;
return 0;
}
//输出
12 abcd
abcd

流操纵算子

#include<iomanip>   //必有的头文件
  • 整数流的基数:流操纵算子dec、oct、hex、setbase
  • 浮点数的精度(precision,setprecision)
  • 设置域宽(setw,width)
  • 用户自定义的流操纵算子
//整数流的基数: 流操纵算子 dec ,oct ,hex

int n = 10;
cout << n < endl;
cout << hex << n << "\n"
<< dec << n << "\n"
<< oct << n << endl;

控制浮点数精度的流操纵算子

  • precision是成员函数

    //调用方式为:  cout.precision
  • setprecison 是流操纵算子

    //调用方式为:  cout << setprecision(5);  //可以连续输出,连续起作用
  • 功能都相同

    • 制度输出浮点数的有效位数 非定点方式输出时
    • 指定输出浮点数的小数点后端有效位数(顶点方式输出时)
      • 定点方式:小数点必须出现在个位数后面
#include <iostream>
#include<iomanip>

using namespace std;
int main()
{
double x = 1234567.89,y = 12.34567;
int n = 1234567;
int m = 12;
cout << setprecision(6) << x << endl //以小数蒂娜位置固定的方式输出
<< y << endl
<< n << endl
<< m;
}
//输出
1234567.890000 //小数点后6位,不足补了0
12.345670 //不足也补了0
1234567 //整数就不再补位
12
//如果小数点后面太长,超出0.000001会四舍五入
#include <iomanip>
#include <iostream>

using namespace std;
int main()
{
double x = 1234567.89, y = 12.34567;
int n = 1234567;
int m = 12;
cout << setiosflags(ios::fixed) << setprecision(6) << x << endl //前面以定点方式输出
<< resetiosflags(ios::fixed) << x; //后面改为以非定点方式输出
}

//输出
1234567.890000
1.23457e+06

设置域宽的流操纵算子

  • setw 成员函数
  • width 流操纵算子
  • 两个功能相同,调用方式不同
//是一次性的设置
cin >> setw(4); == cin.width(5)
cout << setw(4); == cout.width(5)
#include <iomanip>
#include <iostream>

using namespace std;
int main()
{
int w = 4;
char string[10];
cin.width(5); //这个域宽包含了'\0'
while (cin >> string)
{
cout.width(w++);//域宽循环依次为4,5,6
cout << string << endl; //输出不足时在左边补空格
cin.width(5); //一次性,循环时需要再定义一次
}
}
//输入
1234567890
//输出
1234
5678
90
//综合例子
#include <iomanip>
#include <iostream>

using namespace std;
int main()
{
int n = 141;
//1 分别以十六进制、十进制、八进制先后输出n
cout << "1 " << hex << n << " " << dec << n << " " << oct << n << endl;

double x = 1234567.89;
double y = 12.34567;
//2 保留5位有效数字
cout << "2 " << setprecision(5) << x << " " << y << " " << endl;
//3 保留小数点后面5位
cout << "3 " << fixed << setprecision(5) << x << " " << y << endl;
//4 科学计数法输出,且保留小数点后面5位
cout << "4 " << scientific << setprecision(5) << x << " " << y << endl;
//5 非负数要显示正好,输出宽度位12字符,宽度不足则用'*'填补
cout << "5 " << showpos << fixed << setw(12) << setfill('*') << 12.1 << endl;
//6 非负数不显示正号,输出宽度为12字符,宽度不足则右边用填充字符填充
cout << "6 " <<noshowpos << setw(12) << left << 12.1 << endl;
//7 输出宽度为12字符,宽度不足则左边用填充字符填充
cout << "7 " << setw(12) << right << 12.1 << endl;
//8 宽度不足时,负号和数值分列左右,中间用填充字符填充
cout << "8 " << setw(12) << internal << -12.1 << endl;
//9 正常输出,保留后面5位一直有效
cout << "9 " << 12.1 << endl;
return 0;
}

用户自定义流操纵算子


ostream &tab(ostream & output)
//自定义了流操纵算子tab,实际上是一个函数
{
return output << '\t';
}
cout << "aa" << tab << "bb" << endl;

//为什么可以这样用?
因为iostream里对 << 进行了重载(成员函数)
ostream & operator << (ostream & (*p)(ostream &));
该函数内部会调用p所指向的函数,且以*this作为参数

hex、dec、oct都是函数

文件和流


#include <iostream>
#include<fstream>
using namespace std;
int main()
{
ofstream outFile("clients.dat", ios::out | ios::binary);
return 0;
}
// - clients.dat 文件名
// - ios::out 文件打开方式
- ios::out 输出到文件,删除原有内容
- ios::app 输出到文件,保留原有内容
// - ios::binary 以二进制文件格式打开文件
  • 创建文件

    //也可以先创建ofstream对象,再用open函数打开
    ofstream fout;
    fout.open("test.out",ios::out|ios::binary);
  • 判断打开是否成功

    if(!fout)
    {
    cout<<"File open error!" << endl;
    }
  • 文件名可以给出绝对路径,也可以给相对路径。没有交代路径信息,就是在当前文件夹下找文件

文件名的绝对路径喝相对路径

  • 绝对路径

    c:\\tmp\\mydir\\some.txt
  • 相对路径

    //当前盘符的根目录下的tmp\dir\some.txt
    \\tmp\\mydir\\some.txt
    //当前文件夹的tmp子文件夹里面的...
    tmp\\mydir\\some.txt
    //当前文件夹的父文件夹下面的tmp子文件夹里面的...
    ..\\tmp\\mydir\\some.txt
    //当前文件夹的父文件夹的父文件夹下面的tmp子文件夹里面的...
    ..\\..\\tmp\\mydir\\some.txt

文件的读写指针

  • 对于输入文件,有读指针
  • 对于输出文件,有写指针
  • 对于输入输出文件,有一个读写指针
  • 标识文件操作的当前位置,该指针在哪里,读写操作就在哪里进行

#include <iostream>
#include<fstream>
using namespace std;
int main()
{
ofstream fout("a1.out,ios::app"); //打开了对应的文件,打开方式是在末尾添加
long location = fout.tellp(); //取得写指针的位置
location = 10;
fout.seekp(location); //把文件写指针移动到第10个字节处
//具体有下面三种形式:
//location为+ 往后数,为-往前数
fout.seekp(location, ios::beg); //从头开始数location个字节
fout.seekp(location, ios::cur); //从当前位置数location个字节
fout.seekp(location, ios::end); //从尾部数location个字节
return 0;
}

//文件指针位置放置文件尾可以读取文件的大小

字符文件读写

  • 因为文件流也是流,所有流的成员函数和流操作算子也同样适用于文件流
  • 写一个程序,将文件in.txt里面的整数排序,输出到out.txt
#include <iostream>
#include<fstream>
#include<vector>
#include<algorithm>

using namespace std;
int main()
{
vector<int> v; //表示v是int的可变长的数组
ifstream srcFile("in.txt", ios::in);
ofstream destFile("out.txt", ios::out);
int x;
while (srcFile >> x)
{
v.push_back(x); //vector的成员函数,表示把x加到v的尾部
}
sort(v.begin(), v.end());
for (int i = 0; i < v.size();i++)
{
destFile << v[i] << " "; //对硬盘的访问,这里destFile相当于cout
}
destFile.close(); //要记得关闭,内存有限,文件可打开的次数是有限的
srcFile.close();
return 0;
}

二进制文件读写

  • 二进制读文件

    • ifstream 和 fstream的成员函数:
    istream& read (char* s,long n);  
    //将文件读指针指向的地方s的n个字节内容
    //读入到内存地址s,然后将文件读指针向后移动n字节
    //以ios::in 方式打开文件时,文件读指针开始指向文件开头
  • 二进制写文件

    • ofstream 和fstream的成员函数:

      istream& write(const char*s , long n);
      //将内存地址s处的n个字节内容
      //写入到文件中写指针指向的位置,然后将文件写指针向后移动n字节
      //以ios::out 方式打开文件时,文件写指针开始指向文件开头
      //以ios::app 方式打开文件时,文件写指针开始指向文件尾部
//文件中写入一个整数

#include<iostream>
#include<fstream>
#include<vector>
#include<algorithm>

using namespace std;
int main()
{
//创建一个ofstream 的对象fout,写指针指向文件的开头,以二进制的形式打开"some.dat"文件
ofstream fout("some.dat", ios::out | ios::binary);
int x = 120;
//将内存地址&x处的int个字节内容写入文件
fout.write((const char *)(&x), sizeof(int));
//关闭文件
fout.close();
//定义一个fin对象,以文件读指针指向文件开头并以二进制打开文件
ifstream fin("some.dat", ios::in | ios::binary);
int y;
//将int个字节的内容读到&y的地址,读出需要地址才能读出
fin.read((char *)&y, sizeof(int));
fin.close();
cout << y << endl;
return 0;
}
  • 从键盘输入几个学生的姓名的成绩,并以二进制文件形式保存

    //二进制文件
    #include <iostream>
    #include<fstream>
    using namespace std;

    struct Student
    {
    char name[20];
    int score;
    };

    int main()
    {
    Student s;
    ofstream OutFile("students.dat", ios::out | ios::binary);
    while (cin>>s.name>>s.score)
    {
    OutFile.write((char *)&s, sizeof(s));
    }
    OutFile.close();
    return 0;
    }

  • 读文件

#include <iostream>
#include<fstream>
using namespace std;

struct Student
{
char name[20];
int score;
};

int main()
{
Student s;
ofstream OutFile("students.dat", ios::in | ios::binary);//打开文件
if(!inFile){
cout << "error" << endl;
} //判断文件是否打开成功

while (inFile.read((char*)&s,sizeof(s)))//读s对象个字节,强制转换为char*
{
int readedBytes = inFile.gcount(); //到文件结尾位数可能不够读,看刚才读了多少字节
cout << s.name << " " << s.score << endl;
}

inFile.close();
return 0;
}

  • 修改文件
#include<iostream>
int main()
{
Student s;
fstream iofile("c:\\tmp\\students.dat",ios::in|ios::binary);
if(!iofile){
cout << "error";
return 0;
}
iofile.seekp(2 * sizeof(s),ios::beg);//定位写指针
iofile.write("Mike",strlen("Mike")+1);//字符串以'\0'结尾需要多一位
iofile.seekg(0,ios::beg);//定位读指针到开头
while(iofile.read((char*)&s,sizeof(s)))
cout<< s.name<<" "<<s.score<<endl;
iofile.close();
return 0;
}
//为什么存二进制?
更节省空间,方便查找
  • 复制文件
/*
用法示例:
mycopy src.dat dest.dat
即 将src.dat 拷贝到dest.dat 如果dest.dat原来就有,则原来的文件就会杯覆盖
*/
#include<iosteream>
#include<fstream>
using namespace std;
int main(int argc,char * argv[])//命令行参数
{
if(argc != 3)
{
cout<<"File name missing!"<<endl;
return 0;
}
ifstream inFile(argv[1],ios::binary|ios::in);//打开文件
if(! inFile){
cout << "Source file open error."<<endl;
return 0;
}
ofstream outFile(argv[2],ios::binary|ios::out);//打开文件用于写
if(!outFile){
cout <<"New file open error."<<endl;
inFile.close(); //打开的文件一定要关闭
return 0;
}
char c;
//字符缓冲区,
while(inFile.get(c)) //每次读取一个字符
outFile.put(c); //每次写入一个字符
outFile.close();
inFile.close();
return 0;
}

二进制文件和文本文件的区别

  • Linux 和 Unix下的换行符号:‘\n’ (ASCII码 : 0x0a)

  • Windows下的换行符号:‘\r\n’ (ASCII码 : 0x0d0a) endl 就是‘\n’

  • Mac OS下的换行符号:‘\r’ (ASCII码 : 0x0d)

  • 不注意就会导致Linux和 Mac OS文本文件在Windows 记事本打开时不换行

  • Unix/Linux 下打开文件,用不用ios::binary 没区别

  • Windows 下打开文件,如果不用ios::binary,则:

    • 读取文件时,所有的‘\r\n’会被当做一个字符‘\n’处理,即少读了一个字符‘\r’
    • 写入文件时,写入单独的’\n’时,系统自动在前面加一个’\r‘,即多写了一个’\r‘

函数模板

  • 能否只写一个Swap
  • 代码重用
  • 容易的生成相像的函数

image-20210515162059563

image-20210515162313743

image-20210515162556241

image-20210515162626705

  • 模板生成函数 是模板函数的实例化

image-20210515164239012

image-20210515165235748

image-20210515165614395

类模板

image-20210515170231583

image-20210515170243894

image-20210515170315784

image-20210515170342740

image-20210515170521114

image-20210515170553458

image-20210515170845494

image-20210515171038469

image-20210516143223419

image-20210516143424092

image-20210516143438504

类模板与友元

image-20210516143545469

image-20210516143712422

image-20210516143855576

image-20210516144004498

image-20210516144026139

image-20210516144137179

类模板与静态成员变量的关系

image-20210516144241442

image-20210516144331341