程序设计与算法(三)第七周 输入输出与模板
程序员文章站
2024-03-26 09:09:59
...
1 、输入输出流及相关的类
#include <iostream>
using namespace std;
int main(){
int x, y;
cin>>x>>y;
//将标准输出重定向到test中
freopen("/home/CLionProjects/4_28_mooc/test", "w", stdout)
if(y==0)//除数为0则打印错误信息到屏幕
cerr<<"error."<<endl;
else
cout<<x/y;
return 0;
}
#include <iostream>
using namespace std;
int main()
{
double f;int n;
freopen("/home/CLionProjects/4_28_mooc/test", "r", stdin);
cin>>f>>n;
cout<<f<<n<<endl;
return 0;
}
istream类的成员函数
istream & getline(char*buf, int bufSize);从输入流中读取bufSize-1个字符到缓冲区buf,或读到碰到”\n”为止。
istream &getline(char*buf, int bufSize, char delim);从输入流中读取bufSize-1个字符到缓冲区buf,或碰到delim字符为止。
两个函数逗号自动在buf中读入数据的结尾添加’\0’。’\n’或delim都不会被读入buf,单会被从输入流中取走。如果输入流中’\n’或delim之前的字符个数达到或超过了bufSize个,就导入读入失败,结果是,虽然本次读入已完成,但是之后的读入就会失败了。
可以用if(!cin.getline(…))判断输入是否结束
bool eof();判断输入流是否结束
int peek(); 返回下一个字符,但不从流中去掉
istream & putback(char c) 将字符ch放回输入流
istream & ignore(int nCount=1, int delim=EOF);从流中删掉最多nCount个字符,遇到EOF时结束
流操纵算子
整数流的基数:流操纵算子dec,oct,hex,setbase
浮点数的精度(precision, setprecision)
设置域宽(setw, width)
用户自定义
使用include< iomanip>
#include <iostream>
#include <iomanip>
using namespace std;
int main(){
int n=10;
cout<<n<<endl;
cout<<hex<<n<<"\n";
cout<<dec<<n<<"\n";
cout<<oct<<n<<"\n";
return 0;
}
10
a
10
12
控制浮点数精度的流操纵算子
precision, setprecision。非定点方式输出
precision是成员函数,其调用方式为cont.precision(5)
setprecision是流操纵算子,其调用方式是 cout<
他们功能相同。指定输出浮点数的有效位数(非定点方式输出时)指定输出浮点数的小数点后的有效位(定点方式输出时)
定点方式:小数点必须出现在各位数后面
#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 << "\t" << y << "\t" << n << "\t" << m<<endl;
//以小数点位置固定的方式输出
cout << setiosflags(ios::fixed) << setprecision(6) << x << "\t" << y << "\t" << n << "\t" << m << endl;
//非定点方式输出,保留6位有效数字
cout<<resetiosflags(ios::fixed)<<n<<endl;
}
1.23457e+06 12.3457 1234567 12
1234567.890000 12.345670 1234567 12
1.23457e+06
设置域宽的流操纵算子
设置域宽(setw, width) 两者功能相同,一个是成员函数,另一个是流操纵算子,调用方式不同
cin>>setw(4)或者cin.setw(5) ; cout<
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
int w=4;
char string[10];
cin.width(5);
//宽度设置有效性是一次性的,在每次读入和输出之前都要设置宽度
while(cin>>string){
cout.width(w++);
cout<<string<<endl;
cin.width(5);
}
}
1234567890
1234
5678
90
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
int n = 141;
//十六进制,十进制、八进制
cout<<"1) "<<hex<<n<<" "<<dec<<n<<" "<<oct<<n<<endl;
double x=1234567.89, y = 12.34567;
//保留5为有效数字
cout<<"2) "<<setprecision(5)<<x<<" "<<y<<" "<<endl;
//保留小数点后面5位
cout<<"3) "<<fixed<<setprecision(5)<<x<<" "<<y<<" "<<endl;
//科学计数法输出,保留小数点后5位
cout<<"4) "<<scientific<<setprecision(5)<<x<<" "<<y<<endl;
//非负数要显示正号, 输出宽度为12字符,宽度不足用+填补
cout<<"5) "<<showpos<<fixed<<setw(12)<<setfill('+')<<12.1<<endl;
//非负数不显示正号,输出宽度为12字符,宽度不足则用右边填充字符填充
cout<<"6) "<<noshowpos<<setw(12)<<left<<12.1<<endl;
//输出宽度为12字符,宽度不足用左边字符填充
cout<<"7) "<<setw(12)<<right<<12.1<<endl;
//宽度不足时,负号和数值分列左右,中间用填充字符填充
cout<<"8) "<<setw(12)<<internal<<-12.1<<endl;
cout<<"9) "<<12.1<<endl;
return 0;
}
1) 8d 141 215
2) 1.2346e+06 12.346
3) 1234567.89000 12.34567
4) 1.23457e+06 1.23457e+01
5) ++++12.10000
6) 12.10000++++
7) ++++12.10000
8) -+++12.10000
9) 12.10000
#include <iostream>
#include <iomanip>
using namespace std;
ostream &tab(ostream &output){
return output<<"\t";
}
int main()
{
cout<<"aa"<<tab<<"bb"<<endl;
return 0;
}
aa bb
用户自定义流操纵算子
因为iostream里对<<进行了重载(成员函数)
ostream&operator<<(ostream&(*p)(ostream&)); 该函数内部会调用p所指向的函数,并且以 *this作为参数,hex, dex, oct, 都是函数
文件读写
#include <fstream>
#include <iostream>
using namespace std;
int main()
{
ofstream outFile("clients.data", ios::out|ios::binary);
ofstream fout;
fout.open("test.out", ios::out|ios::binary);//可以为绝对路径或相对路径
if(!fout){
cout<<"File open error!"<<endl;
}
}
/*
clients.data:文件名
ios::out:文件打开方式
ios::out:输出到文件,删除原有内容
ios::app:输出到文件,保留原有内容,总在尾部添加
ios::binary:以二进制文件格式打开文件
*/
*/
文件的读写指针
对于输入文件,有一个读指针
对于输出文件,有一个写指针
对于输入输出文件,有一个读写指针
标识文件操作的当前位置,该指针在哪里,读写操作就在那里进行。
#include <fstream>
#include <iostream>
using namespace std;
int main()
{
ofstream fout("/home/baobao/CLionProjects/4_28_mooc/test", ios::app);
// ifstream fin("/home/baobao/CLionProjects/4_28_mooc/test", ios::ate); // 打开文件,定位文件指针到文件尾
long location = fout.tellp();//获得写指针的位置
location = 10;
fout.seekp(location);
fout.seekp(location, ios::beg);//从头数偏移量
fout.seekp(location, ios::cur);//从当前数,可以为负数
fout.seekp(location, ios::end);//从尾部数location,这里是location是个负数
}
字符文件的读写
文件本身是流,流的成员函数和操作算子页同样使用于文件
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
using namespace std;
int main(){
vector<int > v;
ifstream srcFile("/home/baobao/CLionProjects/4_28_mooc/test");
int x;
while(srcFile>>x)
{
cout<<"there";
cout<<x<<endl;
v.push_back(x);
}
srcFile.close();
ofstream desFile("/home/baobao/CLionProjects/4_28_mooc/test");
sort(v.begin(), v.end());
for (int i = 0; i < v.size(); ++i) {
cout<<v[i]<<endl;
desFile<<v[i]<<" ";
}
desFile.close();
return 0;
}
// test内容:1 234 9 45 6 879
二进制文件的读写
二进制写文件
#include <iostream>
#include <fstream>
using namespace std;
struct Student{
char name[20];
int score;
};
int main()
{
Student s;
ofstream OutFile("/home/baobao/CLionProjects/4_28_mooc/student.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;
ifstream inFile("/home/baobao/CLionProjects/4_28_mooc/student.dat", ios::in|ios::binary);
if(!inFile){
cout<<"error"<<endl;
return 0;
}
while(inFile.read((char*)&s, sizeof(s))){
int readedBytes = inFile.gcount();
cout<<s.name<<" "<<s.score<<" "<<readedBytes<<endl;
}
inFile.close();
return 0;
}
二进制文件的改
#include <iostream>
#include <fstream>
#include <cstring>
using namespace std;
struct Student{
char name[20];
int score;
};
int main()
{
Student s;
fstream iofile("/home/baobao/CLionProjects/4_28_mooc/student.dat", ios::in|ios::out|ios::binary);
if(!iofile){
cout<<"error"<<endl;
return 0;
}
iofile.seekp(2*sizeof(s), ios::beg);//定位写指针到第三个记录
iofile.write("Mike", strlen("Mike")+1);
iofile.seekg(0, ios::beg);//定位读指针到文件开头
while(iofile.read((char*)&s, sizeof(s)))
cout<<s.name<<" "<<s.score<<endl;
iofile.close();
return 0;
}
二进制文件复制
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char *argv[])
{
if(argc != 3){
cout<<"Fine mane 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 运行方式:
g++ main.cpp 这句话产生的是a.out
./a.out ./student.dat ./student1.dat
二进制文件和文本文件区别
- 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下打开文件,不用的话,则:
- 读取文件时,所有的’\r\n’会被当作一个字符’\n’处理,即少读了一个字符’\r’
- 写入文件时,写入单独的’\n’ 系统会自动在前面加一个’\r’,多写了一个’\r’
函数模板和类模板
交换两个数的模板
#include <iostream>
using namespace std;
template <class T>
void Swap(T &x, T &y)
{
T tmp = x;
x = y;
y = tmp;
}
int main()
{
int n=1, m=2;
Swap(n, m);
double f = 1.2, g=2.3;
Swap(f,g);
cout<<n<<" "<<m<<endl;
cout<<f<<" "<<g<<endl;
return 0;
}
//2 1
//2.3 1.2
求数组最大元素的MaxElement函数模板
#include <iostream>
using namespace std;
template <class T>
T MaxElement(T a[], int size){
T tempMax = a[0];
for (int i = 1; i < size; ++i) {
if(tempMax<a[i]) tempMax = a[i];
}
return tempMax;
}
int main()
{
int a[100];
int n;
cin>>n;
for (int i = 0; i < n; ++i) {
cin>>a[i];
}
int t = MaxElement(a, n);
cout<<t;
return 0;
}
//5
//2 3 4 2 3
//4
//不通过参数实例化函数模板
#include <iostream>
using namespace std;
template <class T>
T Inc(T n)
{
return 1+n;
}
int main()
{
cout<<Inc<double>(4)/2;
return 0;
}
//2.5
函数模板的重载
函数模板可以重载,只要他们的形参表或类型参数标不同即可
//函数模板重载
#include <iostream>
using namespace std;
template <class T1, class T2>
void print(T1 arg1, T2 arg2){
cout<<arg1<<" "<<arg2<<endl;
};
template <class T>
void print(T arg1, T arg2)
{
cout<<arg1<<" "<<arg2<<endl;
}
template <class T, class T2>
void print(T arg1, T arg2)
{
cout<<arg1<<" "<<arg2<<endl;
};
函数模板和函数的次序
在有多个函数和函数模板名字相同的情况下,编译器如下处理一条函数调用语句
- 先找参数完全匹配的普通(非由模板实例化而得的函数)
- 再找参数完全匹配的模板函数
- 再找实参经过自动类型转换后能够匹配的普通函数
- 上面都找不到,则报错
//函数模板重载
#include <iostream>
using namespace std;
template <class T>
T Max(T a, T b)
{
cout<<"TemplateMax"<<endl;
return 0;
}
template <class T, class T2>
T Max(T a, T2 b)
{
cout<<"TemplateMax2"<<endl;
return 0;
};
double Max(double a, double b){
cout<<"MyMax"<<endl;
return 0;
}
int main()
{
int i=4, j=5;
Max(1.2, 3.4);
Max(i, j);
Max(1.2, 3);//模板实例化
return 0;
}
//MyMax
//TemplateMax
//TemplateMax2
#include <iostream>
using namespace std;
template <class T, class Pred>
void Map(T s, T e, T x, Pred op)
{
for(; s!=e; ++s, ++x)
{
*x = op(*s);
}
};
int Cube(int x){return x*x*x;}
double Square(double x){return x*x;}
int a[5] = {1,2,3,4,5}, b[5];
double d[5]={1.1,2.1,3.1,4.1,5.1}, c[5];
int main()
{
Map(a, a+5, b, Square);
//上面的实例化出了下面的函数,那个Square被实例化成了函数指针
// void Map(int *s, int *e, int *x, double (*op)(double)){
// for(;s!=e;++s,++x){
// *x=op(*s);
// }
// }
for (int i = 0; i < 5; ++i) {
cout<<b[i]<<",";
}
cout<<endl;
Map(a, a+5, b, Cube);
for (int i = 0; i < 5; ++i) {
cout<<b[i]<<",";
}
cout<<endl;
Map(d, d+5, c, Square);
for (int i = 0; i < 5; ++i) {
cout<<c[i]<<",";
}
cout<<endl;
return 0;
}
//1,4,9,16,25,
//1,8,27,64,125,
//1.21,4.41,9.61,16.81,26.01,
类模板:为了多快好省的定义一批相似的类,可以定义类模板,然后有类模板生成不同的类
#include <iostream>
using namespace std;
template <class T1, class T2>
class Pair{
public:
T1 key;
T2 value;
Pair(T1 k, T2 v):key(k), value(v){};
bool operator<(const Pair<T1, T2>&p)const ;
};
template <class T1, class T2>
bool Pair<T1, T2>::operator<(const Pair<T1, T2> &p) const {
return key<p.key;
}
int main()
{
Pair<string, int> student("Tom", 19);
//实例化一个类,这个类名是:Pair<string, int>
cout<<student.key<<" "<<student.value;
return 0;
}
// Tom 19
编译器由类模板生成类的过程叫做类模板的实例化。由类模板实例化得到的类,叫模板类
同一个类模板的两个模板类不兼容
函数模板作为类模板成员
#include <iostream>
using namespace std;
template <class T>
class A{
public:
template <class T2>
void Func(T2 t){cout<<t;}
};
int main(){
A<int> a;
a.Func('K');
a.Func("hello");
return 0;
}
//Khello
类模板与非类型参数
类模板的“<类型参数表>”中可以出现非类型参数:
template <class T, int size>
class CArray{
T array[size];
public:
void Print(){
for (int i = 0; i < size; ++i) {
cout<<array[i]<<endl;
}
}
};
//这里a2和a3属于不同的类
CArray<double, 40> a2;
CArray<int, 50> a3;
类模板与派生
类模板与继承
类模板从类模板派生
#include <iostream>
using namespace std;
template <class T1, class T2>
class A{
T1 v1;
T2 v2;
};
template <class T1, class T2>
class B:public A<T2, T1>
{
T1 v3; T2 v4;
};
template <class T>
class C:public B<T, T>{
T v5;
};
int main()
{
B<int, double> obj1;
C<int> obj2;
return 0;
}
类模板从模板类派生
template <class T1, class T2>
class A{
T1 v1;T2 v2;
};
template <class T>
class B:public A<int, double>{
T v;
};
int main()
{
B<char> obj1; //自动生成两个模板类 A<int, double>和B<char>
return 0;
}
类模板从普通类派生
class A{
int v1;
};
template <class T>
class B:public A{
T v;
};
int main(){
B<char> obj1;
return 0;
}
普通类从模板类派生
template <class T>
class A{
T v1;
int n;
};
class B:public A<int>{
double v;
};
int main()
{
B obj1;
return 0;
}
类模板与友元
- 函数、类、类的成员函数作为类模板的友元
void Func1(){}
class A{};
class B{
public:
void Func(){}
};
template <class T>
class Tmp1{
friend void Func1();
friend class A;
friend void B::Func();
};
- 函数模板作为类模板
#include <iostream>
#include <string>
using namespace std;
template <class T1, class T2>
class Pair{
private:
T1 key;
T2 value;
public:
Pair(T1 k, T2 v):key(k),value(v){}
bool operator<(const Pair<T1, T2> &p) const;
template <class T3, class T4>
friend ostream &operator<<(ostream &o, const Pair<T3, T4>&p);
};
template <class T1, class T2>
bool Pair<T1, T2>::operator<(const Pair<T1, T2> &p) const {
return key<p.key;
}
template <class T1, class T2>
ostream &operator<<(ostream &o, const Pair<T1, T2> &p)
{
o<<"("<<p.key<<","<<p.value<<")";
return o;
};
int main()
{
Pair<string, int> student("Tom", 29);
Pair<int, double> obj(12, 3.14);
cout<<student<<" "<<obj;
return 0;
}
//(Tom,29) (12,3.14)
//任意从template<class T1, class T2>
//ostream &operator<<(ostream& o, const Pair<T1, T2> &p>
//生成的函数都是pair模板类的友元
- 函数模板作为类的友元
#include <iostream>
using namespace std;
class A{
int v;
public:
A(int n):v(n){}
template <class T>
friend void Print(const T &p);
};
template <class T>
void Print(const T&p)
{
cout<<p.v;
}
int main()
{
A a(4);
Print(a);
return 0;
}
//4
// 所有从template<class T>
// void Print(const T&p)
//生成的函数,都成为A的友元
//但是自己写的函数void Print(int a){}
// 不会成为A的友元
- 类模板作为类模板的友元
#include <iostream>
using namespace std;
template <class T>
class B{
T v;
public:
B(T n):v(n){}
template <class T2>
friend class A;
};
template <class T>
class A{
public:
void Func(){
B<int> o(10);
cout<<o.v<<endl;
}
};
int main()
{
A<double >a;
a.Func();
return 0;
}
//10
//A<double>类,成了B<int>类的友元。
//任何从A模板实例化出来的类,都是任何B实例化出来的类的友元
类模板与静态成员变量
#include <iostream>
using namespace std;
template <class T>
class A{
private:
static int count;
public:
A(){count++; }
~A(){count--;}
A(A&){count++;}
static void PrintCount(){
cout<<count<<endl;
}
};
template <> int A<int>::count=0;
template <> int A<double>::count=0;
int main(){
A<int> ia;
A<double>da;
ia.PrintCount();
da.PrintCount();
return 0;
}
//1
//1
推荐阅读
-
程序设计与算法(三)第七周 输入输出与模板
-
mooc程序设计与算法(三)第二周 类和构造函数基础
-
程序设计实习MOOC / 程序设计与算法(三)第04周测验(2020春季) 002:看上去好坑的运算符重载
-
【程序设计与算法(三)第3周测验(2019秋季)】005:魔兽世界之一:备战
-
北京大学MOOC C++程序设计 程序设计与算法(三)第三周测验
-
北京大学MOOC 程序设计与算法(三)魔兽世界之二:装备
-
北京大学MOOC 程序设计与算法(三)魔兽世界三(开战)
-
第七章 1.输入输出与模板
-
20162311 2016-2017-2 《程序设计与数据结构》第七周学习总结
-
听课笔记---程序设计与算法(三)C++面向对象程序设计(郭伟老师)---第一周