项目二
某无线通信设备ODU设备, 具有以下功能:
查看发射功率, 设置发射功率,
查看发射频率, 设置发射频率,
查看带宽, 修改带宽,
查看设备概述(各指标的值).
ODU.h
#pragma once
#include <string>
class ODU {
public:
ODU();
~ODU();
float getTxPower(); // 发射功率
int getTxFreq(); // 发射频率
float getBrandWidth(); // 带宽
bool setTxPower(float power); // 设置发射功率
bool setTxFreq(int frequency); // 设置发射频率
bool setBandWidth(float bandWidth); // 设置
std::string description();
protected:
float txPower; // 发射功率
int txFreq; // 发射频率
float bandWidth; // 带宽
};
ODU.cpp
#include "ODU.h"
#include <sstream>
using namespace std;
ODU::ODU(){}
ODU::~ODU(){}
float ODU::getTxPower() {
return txPower;
}
bool ODU::setTxPower(float power) {
txPower = power;
return true;
}
int ODU::getTxFreq() {
return txFreq;
}
float ODU::getBrandWidth() {
return bandWidth; // 带宽
}
bool ODU::setTxFreq(int frequency) {
txFreq = frequency;
return true;
}
bool ODU::setBandWidth(float bandWidth) {
bandWidth = bandWidth;
return true;
}
std::string ODU::description() {
stringstream ss;
ss << "ODU: txPower=" << txPower << ", txFreq=" << txFreq << ", bandWidth=" << bandWidth;
return ss.str();
}
后来对该产品做了升级, 研发了ODU330产品:
这个产品, 新增加了以下功能:
查看当前的误码率.
查看误码率告警门限
设置误码率告警门限
ODU330.h
#pragma once
#include <string>
#include "ODU.h"
class ODU330 : public ODU {
public:
ODU330();
~ODU330();
float getWarnThreshold();
bool setWarnThreshold(float threshold);
float getBER(); //获取当前误码率
std::string description();
private:
float warnThreshold;
};
ODU330.cpp
#include "ODU330.h"
#include "sstream"
ODU330::ODU330() {}
ODU330::~ODU330(){}
float ODU330::getWarnThreshold() {
return warnThreshold;
}
bool ODU330::setWarnThreshold(float threshold) {
warnThreshold = threshold;
return true;
}
float ODU330::getBER() {
return 0.00005f; //模拟值
}
std::string ODU330::description() {
std::stringstream ret;
ret << "发射功率: " << txPower << "\t发射频率: " << txFreq
<< "\t带宽: " << bandWidth << "\t误码率: " << getBER()
<< "\t告警门限: " << warnThreshold;
return ret.str();
}
main.cpp
#include <iostream>
#include "ODU.h"
#include "ODU330.h"
int main() {
ODU odu1;
odu1.setBandWidth(500);
odu1.setTxFreq(114000);
odu1.setTxPower(45);
std::cout << odu1.description() << std::endl;
ODU330 odu2;
odu2.setBandWidth(600);
odu2.setTxFreq(119000);
odu2.setTxPower(48);
odu2.setWarnThreshold(0.0001);
std::cout << odu2.description() << std::endl;
system("pause");
return 0;
}
项目-持久化存储
用户数据不能永久保存, 程序关闭后, 数据消失.
解决方案: 把数据保存在文件中.
设备:
- 文件
- 控制台
- 特定的数据类型(stringstream)
c++中,必须通过特定的已经定义好的类, 来处理IO(输入输出)
fstream
_EXPORT_STD template <class _Elem, class _Traits>
class basic_fstream : public basic_iostream<_Elem, _Traits> { // input/output stream associated with a C stream
public:
using _Mybase = basic_iostream<_Elem, _Traits>;
using _Myfb = basic_filebuf<_Elem, _Traits>;
using _Myios = basic_ios<_Elem, _Traits>;
using char_type = _Elem;
using traits_type = _Traits;
using int_type = typename _Traits::int_type;
using pos_type = typename _Traits::pos_type;
using off_type = typename _Traits::off_type;
sstream
_EXPORT_STD template <class _Elem, class _Traits, class _Alloc>
class basic_stringstream
: public basic_iostream<_Elem, _Traits> { // input/output stream associated with a character array
public:
using _Mybase = basic_iostream<_Elem, _Traits>;
using char_type = _Elem;
iostream
_EXPORT_STD extern "C++" __PURE_APPDOMAIN_GLOBAL _CRTDATA2_IMPORT istream cin;
_EXPORT_STD extern "C++" __PURE_APPDOMAIN_GLOBAL _CRTDATA2_IMPORT ostream cout;
_EXPORT_STD extern "C++" __PURE_APPDOMAIN_GLOBAL _CRTDATA2_IMPORT ostream cerr;
_EXPORT_STD extern "C++" __PURE_APPDOMAIN_GLOBAL _CRTDATA2_IMPORT ostream clog;
extern "C++" __PURE_APPDOMAIN_GLOBAL _CRTDATA2_IMPORT istream* _Ptr_cin;
extern "C++" __PURE_APPDOMAIN_GLOBAL _CRTDATA2_IMPORT ostream* _Ptr_cout;
extern "C++" __PURE_APPDOMAIN_GLOBAL _CRTDATA2_IMPORT ostream* _Ptr_cerr;
extern "C++" __PURE_APPDOMAIN_GLOBAL _CRTDATA2_IMPORT ostream* _Ptr_clog;
istream
_EXPORT_STD extern "C++" template <class _Elem, class _Traits>
class basic_istream : virtual public basic_ios<_Elem, _Traits> { // control extractions from a stream buffer
public:
using _Myios = basic_ios<_Elem, _Traits>;
using _Mysb = basic_streambuf<_Elem, _Traits>;
using _Iter = istreambuf_iterator<_Elem, _Traits>;
using _Ctype = ctype<_Elem>;
using _Nget = num_get<_Elem, _Iter>;
ios
_STD_BEGIN
_EXPORT_STD extern "C++" template <class _Elem, class _Traits>
class basic_ios : public ios_base { // base class for basic_istream/basic_ostream
public:
using _Myos = basic_ostream<_Elem, _Traits>;
using _Mysb = basic_streambuf<_Elem, _Traits>;
using _Ctype = ctype<_Elem>;
using char_type = _Elem;
using traits_type = _Traits;
读写文件:文件流
文件流: 对文件进行读写操作
头文件: <fstream>
类库:
ifstream 对文件输入(读文件)
ofstream 对文件输出(写文件)
fstream 对文件输入或输出
对文本文件进行读写
模式标志 |
描述 |
ios::in |
读方式打开文件 |
ios:out |
写方式打开文件 |
ios::trunc |
如果此文件已经存在, 就会打开文件之前把文件长度截断为0 |
ios::app |
尾部最加方式(在尾部写入) |
ios::ate |
文件打开后, 定位到文件尾 |
ios::binary |
二进制方式(默认是文本方式) |
以上打开方式, 可以使用位操作 | 组合起来
写文本文件
#include <fstream>
#include <iostream>
using namespace std;
int main() {
string name;
int age;
ofstream outfile;
//也可以使用fstream, 但是fstream的默认打开方式不截断文件长度
// ofstream的默认打开方式是, 截断式写入 ios::out | ios::trunc
// fstream的默认打开方式是, 截断式写入 ios::out
// 建议指定打开方式
outfile.open("person.txt", ios::out | ios::trunc);
while (1) {
cout << "请输入姓名(输入ctul+z退出程序): ";
cin >> name;
if (cin.eof()) break;
outfile << name << "\t";
cout << "请输入年龄: ";
cin >> age;
if(cin.eof()) break;
outfile << age << endl;
}
outfile.close();
cout << "数据已保存到文件 person.txt" << endl;
return 0;
}
上面的年龄如果输入的是非数字,会报错,所以我们需要加一个if(cin.fali()) break;
读文本文件
#include <iostream>
#include <fstream>
using namespace std;
int main() {
string name;
int age;
ifstream fin("person.txt");
while (1) {
fin >> name;
if (fin.eof()) break;
cout << name << "\t";
fin >> age;
cout << age << endl;
}
fin.close();
return 0;
}
二进制文件读写
思考:
文本文件和二进制文件的区别?
文本文件: 写数字1, 实际写入的是 ‘1’
二进制文件:写数字1, 实际写入的是 整数1(4个字节,最低字节是1, 高3个字节都是0)
写字符‘R’实际输入的还是‘R’
二进制文件写
#include <iostream>
#include <fstream>
using namespace std;
int main() {
string name;
int age;
ofstream outfile;
outfile.open("user.dat" , ios::out | ios::trunc | ios::binary);
while (1) {
cout << "Enter name (or 'q' to quit): " << endl;
cin >> name;
if (cin.eof()) break;
outfile << name << "\t";
cout << "Enter age: " << endl;
cin >> age;
if (cin.fail()) break;
outfile.write((char*)&age, sizeof(age));
//outfile << age << endl; //会自动转成文本方式写入
}
outfile.close();
return 0;
}
二进制文件读
#include <iostream>
#include <fstream>
using namespace std;
int main() {
string name;
int age;
ifstream infile;
infile.open("user.dat" , ios::out | ios::trunc | ios::binary);
while (1) {
infile >> name;
if (infile.eof()) break;
cout << name << "\t";
char tmp;
infile.read(&tmp, sizeof(tmp)); // 跳过中间制表符
//infile >> age; //从文本文件中读取整数, 使用这个方式
infile.read((char*)&age, sizeof(age)); // 从二进制文件中读取整数
cout << age << endl;
}
infile.close();
return 0;
}
对文件流按格式读写取数据
使用stringstream
文件流写数据
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
int main() {
string name;
int age;
ofstream fout("person.txt", ios::out | ios::trunc);
while (1) {
cout << "Enter name(or eof to quit): ";
cin >> name;
if (cin.eof()) break;
cout << "Enter age: ";
cin >> age;
stringstream ss;
ss << "name=" << name << " age=" << age << endl;
fout << ss.str();
}
fout.close();
return 0;
}
文件流读数据
没有优雅的C++解决方案, 使用C语言的sscanf
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
int main() {
char name[32];
int age;
string line;
ifstream fin("person.txt");
while (1) {
getline(fin, line);
if (fin.eof()) break;
sscanf_s(line.c_str(), "name=%s age=%d", name , static_cast<unsigned int>(sizeof(name)), &age);
cout << "姓名:" << name << " 年龄:" << age << endl;
}
fin.close();
return 0;
}
文件流的状态检查
s.is_open( )文件流是否打开成功,
s.eof( ) 流s是否结束
s.fail( ) 流s的failbit或者badbit被置位时, 返回true
failbit: 出现非致命错误,可挽回, 一般是软件错误
badbit置位, 出现致命错误, 一般是硬件错误或系统底层错误, 不可挽回
s.bad( ) 流s的badbit置位时, 返回true
s.good( ) 流s处于有效状态时, 返回true
s.clear( )流s的所有状态都被复位
项目精讲-随机读写:文件流的定位
seekg
seekg( off_type offset //偏移量 , ios::seekdir origin ); //起始位置
作用:设置输入流的位置
参数1: 偏移量
参数2: 相对位置
beg 相对于开始位置
cur 相对于当前位置
end 相对于结束位置
demo
读取当前程序的最后50个字符
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(void) {
ifstream infile;
infile.open("ODU330.cpp");
if (!infile.is_open()) {
return 1;
}
infile.seekg(-50, infile.end);
while (!infile.eof()) {
string line;
getline(infile, line);
cout << line << endl;
}
infile.close();
system("pause");
return 0;
}
tellg
返回该输入流的当前位置(距离文件的起始位置的偏移量)
获取当前文件的长度
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(void) {
ifstream infile;
infile.open("ODU330.cpp");
if (!infile.is_open()) {
return 1;
}
infile.seekg(0, infile.end);
int len = infile.tellg();
cout << len << endl;
infile.close();
system("pause");
return 0;
}
seekp
设置该输出流的位置
demo
先向新文件写入:“123456789”
然后再在第4个字符位置写入“ABC”
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(void) {
ofstream out("output.txt");
if (!out.is_open()) {
return 1;
}
out << "Hello, world!";
out.seekp(4, out.beg);
out << "C++";
out.close();
return 0;
}
常见错误总结
12节 常见错误总结
- 文件没有关闭
文件没有关闭, close(),可能导致写文件失败
- 文件打开方式不合适
- 在VS2015的部分版本中,当sscanf和sscanf_s的格式字符串中含有中文时,可能会读取失败。在vs2019中未发现该类问题。
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(void) {
char name[32];
int age;
sscanf_s("汤姆 25", "%s %d", name, 32, &age);
cout << "Name: " << name << endl;
cout << "Age: " << age << endl;
return 0;
}
vs2022也没有问题
项目练习(暂未完成)
文件数据库类Database的设计
需求: 要使用文件来保存用户信息
分析: 设计一个类, 来实现信息的保存功能
Database 数据库
功能:
init() //初始化, 从文件中读取数据信息, 来初始化用户数据
autoPair() //自动配对
print() // 打印该数据库中的所有用户信息
数据:
vector<Boy> boys; //所有的单身男信息
vector<Girl> girls; //所有单生女信息
项目练习1
为这个项目增加功能:
添加单个用户信息, 并输出该用户的配对结果.
项目练习2
输入任意多个整数, 把这些数据保存到文件data.txt中.
如果在输入的过程中, 输入错误, 则提示用户重新输入.
指导用户输入结束(按ctrl + z)
[每行最多保存10个整数]
cin.ignore(count, c);
从输入流中提取并丢弃字符,直到遇到下列三种情况
1.提取的字符达到了参数count指定的数量
2.在输入序列中遇到文件结束(EOF)
3.输入序列中的下一个字符为参数c指定的字符(这个字符会被提取并丢弃)
项目练习3
从练习2中的num.txt文件读取各个整数, 打印出最大值和最小值, 以及平均值.