c++工程实践——实际工程中的文件读取和日期处理的小问题

一、问题

在实际开发中遇到了两个小问题,一个是文件流的读写中的长度和结尾判断;另外一个是C++11库std::chrono::duration的数据类型的问题。这两个问题导致了两个结果:
1、流结尾判断不准确,多读一帧导致长度判断恒为正确,文件不加载
2、对标准库的时间库认知不统一,导致单位不同出现错判
这样说可能不好理解,在下面分别进行代码说明。

二、文件问题解决

先看一下文件处理的原始代码:

#include <fstream>
#include <iostream>int main() {std::ifstream f(filename);if (!f.good()) {return 0;}char buf[1024];static std::streamsize count = 1024;while (!f.eof()) {size_t c = f.getline(buf, count).gcount();//可正常比较// size_t c = metaFileStream.gcount();//可正常比较std::cerr << "----" << f.gcount() << std::endl;auto dlen = f.gcount();//下面的判断进入,导致文件错误if (dlen < 3/*f.gcount()<3*/) {//问题主要在这里,原始的代码是注释掉的部分,但此处二者结果一致std::cerr << "dlen is:" << dlen << std::endl;}if (static_cast<size_t>(f.gcount()) < 3) {//可正常比较std::cerr << "static_cast count compare!" << std::endl;return 0;}}return 1;
}

这段代码本来是没有问题的,后面有一个长度判断的,但后来更改增加了不能执行的部分后,程序基本就无法运行了。因为只要有运行就需要加载文件,而在此处则无法运行下去,直接报文件读取的数据太少,也就是上面注释里的那段无法执行的代码返回的。
后来根据代码来要看,怀疑是不是getline函数对gcount函数有类似清除的机制,但查看官方文档后发现没有这个问题,只是提供了对长度的变化的影响,这反而是程序需要的。后来就怀疑到是不是不同类型的比较导致的,毕竟在很早以前就被有符号和无符号的类型整过。查看文档中gcount()返回的数据类型是streamsize,而它的定义是:

#define __PTRDIFF_TYPE__ long int
typedef __PTRDIFF_TYPE__ ptrdiff_t;

这应该没有问题的。继续分析,打印发现起始都是正常的,但读到最后会打印一个长度为0的读取缓冲,所以进入了文件错误判断的分支中。忽然想起了,文件流读取最后一帧后是需要再读取一帧才能将f.eof()转为false。也就是说,正常读取完成后,其结果仍然是True,只有继续读取出现错误时,才会置为False。所以会出现一个空读取,导致进入异常判断。知道问题原因后就比较好解决了:

#include <fstream>
#include <iostream>int main() {std::ifstream f(filename);if (!f.good()) {return 0;}char buf[1024];static std::streamsize count = 1024;while (f.peek()!=EOF) {size_t c = f.getline(buf, count).gcount();//正常if (c < 3) {std::cerr << "dlen is:" << dlen << std::endl;}if (static_cast<size_t>(f.gcount()) < 3) {std::cerr << "static_cast count compare!" << std::endl;return 0;}}return 1;
}

不过在最近的开发中遇到过几次定义完全相同的类型,在被反复重新定义后就无法进行转换。比如在某个库里定义了Uint16,而程序中有uint16_t,这两种类型本质都是一种类型,但在实际编译中报错,无法通过编译。这里面的原因很多,不做细的分析,只是提醒开发者要注意这方面的细节。

三、时间问题解决

时间问题的处理主要原因在于对时间问题的掌握还是不系统,没有从根本上明白时间库的定义,先看类似原始代码:

int main(){//求运行时间1auto t1 = std::chrono::steady_clock::now();dowork();auto t2 = std::chrono::steady_clock::now();auto escape1 = t2 - t1;//这种情况单位是什么std::cerr << "escape1 is:" << escape1.count() << std::endl;//求运行时间2auto t3 = std::chrono::steady_clock::now();dowork();auto t4 = std::chrono::steady_clock::now();std::chrono::duration<double> escape2 = t4 - t3;//这种情况单位是什么std::cerr << "escape1 is:" << escape2.count() << std::endl;//如果出现运行结果中的科学计数方法可使用下面的方式打印std::cerr << std::fixed << std::setprecision(9) << "escape1 is:" << escape2.count() << std::endl;//求运行时间3auto start = std::chrono::high_resolution_clock::now();dowork();auto end = std::chrono::high_resolution_clock::now();std::chrono::microseconds escape3 = std::chrono::duration_cast<std::chrono::microseconds>(end - start);//此处单位是微秒std::cerr << "escape1 is:" << escape3.count() << std::endl;return 0;
}

运行结果:

cape1 is:2532329
escape1 is:0.00255415    //有可能出现类似escape1 is:3.82e-07的科学计数
escape1 is:2559

有没有发现,上面的打印都没有带单位,这也这次的一个主要问题。特别是std::chrono::duration竟然一时没反应过来,后来查看文档说明才知道,其定义为:

template<class Rep,class Period = std::ratio<1>
> class duration;
template<std::intmax_t Num,std::intmax_t Denom = 1
> class ratio;

其实重点是要明白ratio的定义,duration等价于duration<double,std::ratio<1>>,又等价于duration<double,std::ratio<1,1>>即以秒为基准单位。对照上面的显示,可以知道,三个打印结果的单位是纳秒、秒和微秒。再根据现实生活中时间的定义,ratio<60>(ratio<60,1>)代表一分钟,ratio<6060>(ratio<6060,1>)代表一小时,ratio<1, 1000>代表一毫秒,ratio<1, 1000000>代表一微秒,ratio<1, 1000000000>代表一纳秒。
所以在标准库中定义了一些常用的时间:

typedef duration <Rep, ratio<3600,1>> hours;
typedef duration <Rep, ratio<60,1>> minutes;
typedef duration <Rep, ratio<1,1>> seconds;
typedef duration <Rep, ratio<1,1000>> milliseconds;        
typedef duration <Rep, ratio<1,1000000>> microseconds;       
typedef duration <Rep, ratio<1,1000000000>> nanoseconds;     

在C++20后又出现了周(week)、月(months)和年(years)的单位,而在某些场景下可能会以某些特定的时间量为单位,所以才会有ratio<N,N>的用法。而duration也可以是duration等,其实就是表示数据的类型,浮点可能精度更高一些。
这个问题之所以出现,主要是程序中的时间太小,出现了科学计算法,同时,单位的不统一导致口算时明显数据不正确。以前基本都是统一的时间单位控制,而这块代码因为与库中的代码进行兼容,所以才出现了计算上的差异。std::chrono::duration_cast的机制与分析大略相同。
无它,终究还是基础知识不扎实导致。
提一个问题:"auto t1 = std::chrono::steady_clock::now();"返回的 time_point的Rep类型是什么类型?它从哪里来?或者说std::chrono::steady_clock的默认的Rep是什么类型?

四、总结

万丈高楼平地起,基础的重要性已经反复说得都麻了。但不得不说,往往都明白基础的重要性,可其实并未真正时刻挂在心上。上面的例子就说明了学习态度中的一些瑕疵,与诸君共同努力改正吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/27370.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

你认为这个项目的难点和亮点是什么?

你认为这个项目的难点和亮点是什么&#xff1f; 好的&#xff0c;面试官&#xff0c;这个项目对于我来说还是有一定的挑战性的&#xff0c;在此过程中&#xff0c;我也成长和学习到了不少。亮点和难点方面我主要从三个方面来阐述&#xff0c;一个是业务方面&#xff0c;一个是整…

若依微服务Docker部署验证码出不来怎么办?

最近,有许多人反馈在使用 Docker 部署若依微服务项目时,遇到验证码无法显示的问题。本文将重点介绍解决该问题的注意事项以及整个项目的部署流程。之前我们也撰写过微服务部署教程,本文将在此基础上进行优化和补充。你也可以参考我之前写的部署教程:https://yang-roc.blog.…

AI来帮助我使用inno项目的配置打包win运行文件。

同时使用了chatgpt和文心一言。实测结果&#xff0c;chatgpt更好些&#xff0c;文心一言也有特点。贴图&#xff1a; ChatGpt: Chatgpt 感觉更了解你要的。 文心一言&#xff0c;要描述更清楚些。&#xff08;测试了几遍&#xff09; 最终我的结构是这样的&#xff1a; 具体怎…

【Java面试】十九、并发篇(下):线程池

文章目录 1、为什么要使用线程池2、线程池的执行原理2.1 七个核心参数2.2 线程池的执行原理 3、线程池用到的常见的阻塞队列有哪些4、如何确定核心线程数开多少个&#xff1f;5、线程池的种类有哪些&#xff1f;6、为什么不建议用Executors封装好的静态方法创建线程池7、线程池…

C++青少年简明教程:C++的指针入门

C青少年简明教程&#xff1a;C的指针入门 说到指针&#xff0c;就不可能脱离开内存。了解C的指针对于初学者来说可能有些复杂&#xff0c;我们可以试着以一种简单、形象且易于理解的方式来解释&#xff1a; 首先&#xff0c;我们可以将计算机内存想象成一个巨大的有许多格子的…

快速开发的UI框架:效率蹭蹭提高!!【送源码】

不知道各位用uniapp 开发移动端小程序或者网页&#xff0c;是否用UI框架。 我一般就用官方自带的&#xff0c;近期一个项目 用了uView, 感觉整体还不错&#xff0c;类似蚂蚁的风格。 特此推荐下&#xff0c;可以收藏一下&#xff0c;需要的时候记得来取哦&#xff01; 介绍 …

Linux 线程控制

&#x1f493;博主CSDN主页:麻辣韭菜&#x1f493;   ⏩专栏分类&#xff1a;Linux初窥门径⏪   &#x1f69a;代码仓库:Linux代码练习&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多Linux知识   &#x1f51d; 目录 前言 1.线程现象 2.线程等待 3.线程…

git配置2-不同的代码托管平台配置不同的ssh key

1. 配置单个ssh key 1.1. 原理1.2. 生成 ssh key1.3. 代码托管平台配置公钥 2. 配置多个ssh key 2.1. 应用场景2.2. 生成两个不同的key2.3. 修改config文件2.4. 配置代码托管平台2.5. 测试是否成功 1. 配置单个ssh key 1.1. 原理 使用ssh命令行工具&#xff08;git安装成功…

【APP移动端自动化测试】第四节.元素操作的API

文章目录 前言一、点击&输入&清空操作 1.1 点击元素 1.2 输入&清空元素二、获取文本内容&位置&大小操作 2.1 获取文本内容 2.2 获取位置&大小三、根据属性名获取属性值操作四、滑动和拖拽操作 4.1 _swipe 4.2 _scroll …

博瓦科技产品亮相湖北安博会啦!!!

6月12日&#xff0c;第二十三届2024中国&#xff08;武汉&#xff09;社会公共安全产品暨数字城市产业展览会&#xff08;简称&#xff1a;湖北安博会&#xff09;在武汉国际会展中心隆重开幕。作为行业内最具影响力的展会之一&#xff0c;此次盛会将汇聚来自全球的顶尖企业、专…

Github 2024-06-08 开源项目日报Top10

根据Github Trendings的统计,今日(2024-06-08统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量TypeScript项目4Python项目2Swift项目1Svelte项目1Jupyter Notebook项目1非开发语言项目1Go项目1Vue项目1Lua项目1Visual Studio Code - 开源项…

G6 - CycleGAN实战

&#x1f368; 本文为[&#x1f517;365天深度学习训练营](https://mp.weixin.qq.com/s/0dvHCaOoFnW8SCp3JpzKxg) 中的学习记录博客&#x1f356; 原作者&#xff1a;[K同学啊](https://mtyjkh.blog.csdn.net/) 目录 理论知识CycleGAN能做什么 模型结构损失函数 模型效果总结与…

Vue小记——如何理解 $nextTick( ) ?

1、如何理解 $nextTick() 是一个非常重要的方法&#xff0c;用于在数据变动后&#xff0c;但DOM更新完成之前&#xff0c;执行特定的回调函数。这个方法确保了代码在DOM元素实际被更新之后才运行。 这是用来处理Vue的异步更新队列的。在Vue中&#xff0c;当你改变数据时&#…

OSINT技术情报精选·2024年6月第1周

OSINT技术情报精选2024年6月第1周 2024.6.11版权声明&#xff1a;本文为博主chszs的原创文章&#xff0c;未经博主允许不得转载。 1、经合组织&#xff1a;《2024数字经济展望&#xff1a;第1卷,拥抱技术前沿》 经合组织近日发布《2024数字经济展望》报告第一卷&#xff0c;…

每天五分钟深度学习:逻辑回归算法完成m个样本的梯度下降

本文重点 上节课程我们学习了单样本逻辑回归算法的梯度下降,实际使用中我们肯定是m个样本的梯度下降,那么m个样本的如何完成梯度下降呢? m个样本的损失函数定义为: 我们定义第i个样本的dw、db为: dw和db为损失J对w和b的偏导数,因为m个样本的代价函数J是1到m个样本总损失…

适合各行各业在线预约的自定义小程序源码系统 前后端分离 带完整的安装代码包以及代搭建教程

系统概述 这款自定义小程序源码系统是为了适应不同行业的预约需求而设计的。它具有高度的灵活性和可扩展性&#xff0c;可以根据不同用户的需求进行定制化开发&#xff0c;满足各种复杂的业务场景。 系统的前端采用了先进的小程序技术&#xff0c;为用户提供了简洁、直观的操…

【Windows】Topaz Gigapixel AI(人工智能图片放大工具)软件介绍和安装教程

软件介绍 Topaz Gigapixel AI是一款由Topaz Labs开发的先进图像放大软件&#xff0c;利用人工智能&#xff08;AI&#xff09;技术来放大图像&#xff0c;同时保持或甚至增强图像的细节和清晰度。这款软件特别适用于需要高质量图像放大的摄影师、设计师以及其他视觉内容创作者…

四六级考前突击之同义词替换

最常见的十组同义词替换 新的 Novel-new-fresh-most recent 奇怪的 Strange-odd-unusual-uncommon-Unfamiliar-peculiar odds之后的odds变成了机会的意思,同义词有chance-opportunity 传统 Convention-tradition convention还有大会和集会的意思 形容词为Conventional…

图片based64编码解码python代码

图片based64编码解码python代码 import base64 from PIL import Imagedef image_to_base64(image_path):# 打开图片文件image Image.open(image_path)# 将图片转换为二进制数据image_bytes Nonewith open(image_path, rb) as image_file:image_bytes image_file.read()# 将二…

【C++】模板及模板的特化

目录 一&#xff0c;模板 1&#xff0c;函数模板 什么是函数模板 函数模板原理 函数模板的实例化 推演(隐式)实例化 显示实例化 模板的参数的匹配原则 2&#xff0c;类模板 什么是类模板 类模板的实例化 二&#xff0c;模板的特化 1&#xff0c;类模板的特化 全特化…