文章目录

  • 引言
  • 在此之前...
    • Unicode和ASCII
    • C风格字符串的操作函数集合
      • 字符串操作
      • 字符串类型转换
  • Part1:操作文件名和文件路径
  • Part2:单个文件的读写
    • 文件打开的模式
    • TFile的定义
  • Part3:异步文件I/O
    • 异步I/O线程
    • 文件类中的异步方法

引言

为什么会将文件系统放在引擎的底层核心代码呢?对于游戏而言,游戏的本质就是多媒体体验(模型、声音、视频等),而游戏引擎引擎需要在底层实现相关文件的读取作为支撑。

文件系统和资源管理器是两个概念。文件系统的功能是文件的读写,针对的是单文件或文件夹;而资源管理器则是目录级别的增删查改,可以成为引擎文件的简易版数据库。

  • 对于游戏引擎来说,文件系统应该要实现一下几个部分:

    • 操作文件名和文件路径
    • 开、关、读、写单独的文件
    • 处理异步文件输入/输出(IO)请求(做串流之用)

下面我们这几部分进行逐一分析,并展示实现的代码。

在此之前…

对于文件系统。字符串应该是密不可分的。且不说很多文件都与文本(字符串组成),文件路径也是通过字符串来表达的。因此,对字符串的处理对于文件系统来说是十分重要的。幸运的是,C提供了许多对字符串的操作函数,我们需要对它们进行封装整合即可。

Unicode和ASCII

关于使用ASCII还是Unicode,我的建议是尽量两种都实现,毕竟我们不知道仅仅使用一种类型的字符会不会在未来的开发中遇到麻烦,多多益善。

例如:在文件流处理中,我发现了宽字符版本的fputws和fgetws使用时其fopen()不能基于文本,只能基于二进制。而二进制对于要记录文字可读性是非常差的。因此,最好使用ASCII版的 fputs和fgets

而在Windows中字符和字符串的类型有众多定义(天晓得为什么会则么多)。为代码类型统一性,我现在这里做一个规范。在此引擎代码中,我将使用如下字符类型。

字符集 字符类型 常量字符类型 字符指针类型 常量字符串类型
ASCII CHAR const CHAR PSTR PCSTR
Unicode WCHAR const WCHAR PWSTR PCWSTR

C风格字符串的操作函数集合

接下来我们要将C风格字符串的操作函数用自己的命名风格对其进行封装。代码虽然比较多,但不复杂。每一种函数都要写ASCII和Unicode两种脚本。函数按功能分类主要有:

  • 按功能分类

    • 获取字符串长度
    • 字符串复制
    • 字符串拼接
    • 字符串比较
    • 字符串中查找字符(从左往右和从右往左两个版本)
    • 字符格式化
    • CHAR字符串和WCHAR字符串之间的相互转换

字符串操作

//TEString.h
//-------------------------------------------------------------------------
#include <wchar.h>
#include <tchar.h>//获取宽字符串长度
//param:
//  str:计算的字符串
//return:
//  返回字符串长度
inline size_t TStrLen(PCWSTR str)
{return _tcslen(str);
}//获取字符串长度
//param:
//  str:计算的字符串
//return:
//  返回字符串长度
inline size_t TStrLen(PCSTR str)
{return strlen(str);
}//获取[char16_t]字符串长度
//param:
//  str:计算的字符串
//return:
//  返回字符串长度
inline size_t TStrLen(register const char16_t* str)
{if (!str)return 0;register size_t len = 0;while (str[len++]);return len - 1;
}//获取[char32_t]字符串长度
//param:
//  str:计算的字符串
//return:
//  返回字符串长度
inline size_t TStrLen(register const char32_t* str)
{if (!str)return 0;register size_t len = 0;while (str[len++]);return len - 1;
}//宽字符串复制
//param:
//  dest:目标缓存指针
//  destlen:目标缓存大小
//  source:拷贝源字符串
inline errno_t TStrCpy(PWSTR dest, size_t destlen, PCWSTR source)
{return _tcscpy_s(dest, destlen, source);
}//char字符串复制
//param:
//  dest:目标缓存指针
//  destlen:目标缓存大小
//  source:拷贝源字符串
inline errno_t TStrCpy(PSTR dest, size_t destlen, PCSTR source)
{return strcpy_s(dest, destlen, source);
}//宽字符串复制
//param:
//  dest:目标缓存指针
//  destlen:目标缓存大小
//  source:拷贝源字符串
//  cpylen:拷贝字符串长度
inline errno_t TStrCpy(PWSTR dest, unsigned int destlen, PCWSTR source, unsigned int cpylen)
{return _tcsncpy_s(dest, destlen, source, cpylen);
}//将宽字符串endStr拼接到dest上
//param:
//  dest:目标缓存指针
//  destlen:目标缓存大小
//  endStr:拼接的字符串
inline errno_t TStrCat(PWSTR dest, size_t destlen, PCWSTR endStr)
{return _tcscat_s(dest, destlen, endStr);
}//将宽字符串endStr拼接到dest上
//param:
//  dest:目标缓存指针
//  destlen:目标缓存大小
//  endStr:拼接的字符串
//  catlen:需要从拼接字符串获取的长度
inline errno_t LStrCat(PWSTR dest, size_t destlen, PCWSTR endStr, int catlen)
{return _tcsncat_s(dest, destlen, endStr, catlen);
}//将字符串endStr拼接到dest上
//param:
//  dest:目标缓存指针
//  destlen:目标缓存大小
//  endStr:拼接的字符串
inline errno_t TStrCat(PSTR dest, size_t destlen, PCSTR endStr)
{return strcat_s(dest, destlen, endStr);
}//宽字符串比较函数
//param
//  str1:比较字符串1
//  str2:比较字符串2
inline int TStrCmp(PWSTR str1, PWSTR str2)
{return _tcscmp(str1, str2);
}//宽字符串比较函数
//param
//  str1:比较字符串1
//  str2:比较字符串2
//  cmplen:比较的长度
inline int TStrCmp(PWSTR str1, PWSTR str2, size_t cmplen)
{return _tcsncmp(str1, str2, cmplen);
}//宽字符查找函数(从左往右找)
//param
//  opStr:查找字符串
//  c:目标字符
inline PWSTR TStrChr(PWSTR opStr, WCHAR c)
{return wcschr(opStr, c);
}//字符查找函数(从左往右找)
//param
//  opStr:查找字符串
//  c:目标字符
inline PCSTR TStrChr(PCSTR opStr, CHAR c)
{return strchr(opStr, c);
}//宽字符查找函数(从右往左找)
//param
//  opStr:查找字符串
//  c:目标字符
inline PWSTR TStrRChr(PWSTR opStr, WCHAR c)
{return wcsrchr(opStr, c);
}//字符查找函数(从右往左找)
//param
//  opStr:查找字符串
//  c:目标字符
inline PCSTR TStrRChr(PCSTR opStr, CHAR c)
{return strrchr(opStr, c);
}

字符串类型转换

Windows提供了API用以ASCII和Unicode之间的转化。

/**  将多字节字符串转换为宽字符串*  @param*  uCodePage:标识一个与多字节字符串相关的代码页号。一般设定为CP_ACP*  dwFlags:设定另一个控件,它可以用重音符号之类的区分标记来影响字符*  pMultiByteStr:要转换的多字节字符串*  cchMultiByte:转换字符串的长度。如果传递-1,则该函数用于确定源字符串的长度*  pWideCharStr:指向转换后获得的字符串缓存*  cchWideChar:缓存的最大值*/
int MultiByteToWideChar(UINT uCodePage,DWORD dwFlags,PCSTR pMultiByteStr,int cchMultiByte,PWSTR pWideCharStr,int cchWideChar
);/**  将宽字符串转换为多字节字符串*  @param*  uCodePage:标识一个与多字节字符串相关的代码页号。一般设定为CP_ACP*  dwFlags:设定另一个控件,它可以用重音符号之类的区分标记来影响字符.一般设为0*  pWideCharStr:要转换的款宽字符串*  cchMultiByte:转换字符串的长度。如果传递-1,则该函数用于确定源字符串的长度*  pWideCharStr:指向转换后获得的字符串缓存*  cchWideChar:缓存的最大值*  pDefaultChar:当一个字符转换失败时的默认替代字符*  pfUsedDefaultChar:本次操作是否存在为转换成功而用了默认字符的情况。有为TRUE,无为FALSE*/
int MultiByteToWideChar(UINT uCodePage,DWORD dwFlags,PCWSTR pWideCharStr,int cchMultiByte,PSTR pMultiByteStr,int cchWideChar,PCSTR pDefaultChar,PBOOL pfUsedDefaultChar
)

关于这两个函数如何使用,我在TEString.h实现了ASCII和Unicode字符串。

//ASCII(char)字符串转Unicode
inline void TAsciiToUnicode(PCSTR source, PSTR dest, int destBufferSize)
{size_t size = MultiByteToWideChar(CP_ACP, 0, source, -1, NULL, 0);size = size > destBufferSize ? destBufferSize : size;ULONGLONG len = sizeof(WCHAR) * size;MultiByteToWideChar(CP_ACP, 0, (LPCCH)source, -1, (LPWSTR)dest, len);dest[size + 1] = '\0';
}//Unicode字符串转ASCII(char)
inline void TUnicodeToAscii(PCWSTR source, PSTR dest, int destBufferSize)
{size_t size = WideCharToMultiByte(CP_ACP, NULL, source, -1, NULL, 0, NULL, FALSE);size = size > destBufferSize ? destBufferSize : size;WideCharToMultiByte(CP_ACP, NULL, (LPCWCH)source, -1,     (LPSTR)dest, size, NULL, FALSE);
}

这样,我们得到了一些简单的字符串操作函数合集。这些函数对于文件系统来说足够使用了。

Part1:操作文件名和文件路径

文件名和文件路径构成了文件的一个标识。对于文件路径,我们想要获取的属性主要有:

重要熟悉 描述 用例
文件拓展名 路径字符串从右往左第一个’.'之后的字符串 C:/log.txt 的拓展名是txt
文件名(含拓展名) 路径字符串从右往左第一个’/‘或’\'之后的字符串 C:/log.txt 的文件名是log.txt
文件名(不含拓展名) 路径字符串从右往左第一个’/‘或’\‘之后和第一个’.'之前的字符串 C:/log.txt 的文件名是log
文件所在的根盘符 路径字符串从左往右第一个"

引擎之旅 Chapter.3 文件系统相关推荐

  1. 引擎之旅 Chapter.1 高分辨率时钟

    文章目录 游戏中的时间线 真实时间线 游戏时间线 全局时钟的实现方式 我们如何理解时间.在现实生活中,时间就是一个有方向的直线.从一个无穷远到另一个无穷远.用数学去抽象地思考,它就是一个从无穷小到无穷 ...

  2. 13、不同存储引擎的数据表在文件系统里是如何表示的?

    MySQL 支持 InnoDB.MyISAM.Memory.Merge.Archive.CSV.BLACKHOLE 几种存储引擎,不同存储引擎的数据表在文件系统中的表示也各不相同. MySQL 中的每 ...

  3. 引擎之旅 前传:C++代码规范

    自己以前写代码时,一个项目一个风格.单人开发的工作使得我并没有注意到代码规范性和可读性的问题.每当项目结束后,看到自己杂乱无章的代码,完全没有二次开发和重构的欲望. 写代码就应该像写诗一样优雅. by ...

  4. 开启我的游戏引擎学习之旅

    也不能说是在自己内心中酝酿了许久的一个计划,毕竟以我目前的能力,想要从零到一地写一个哪怕是比较简单的游戏引擎仍旧是一个十分有难度的工作.仅仅是能达到"能够熟练使用Unity进行开发" ...

  5. DCMTK:表示基于文件系统的基本工作列表管理服务类提供程序的控制台引擎的类

    DCMTK:表示基于文件系统的基本工作列表管理服务类提供程序的控制台引擎的类 表示基于文件系统的基本工作列表管理服务类提供程序的控制台引擎的类 表示基于文件系统的基本工作列表管理服务类提供程序的控制台 ...

  6. 数据库存储引擎学习总结

    什么是存储引擎以及不同存储引擎特点 http://www.cnblogs.com/wildfox/p/5815414.html 以前一直玩Oracle数据库,整天围着业务需求和执行计划转,刚刚接触My ...

  7. mysql中存储引擎是啥_mysql中的存储引擎

    mysql存储引擎概述 什么是存储引擎? MySQL中的数据用各种不同的技术存储在文件(或者内存)中.这些技术中的每一种技术都使用不同的存储机制.索引技巧.锁定水平并且最终提供广泛的不同的功能和能力. ...

  8. 场景引擎是什么意思_初识ClickHouse、大数据多场景的热捧者

    点击蓝字获取更多精彩信息 编 者 说 Clickhouse是一个用于联机分析处理(OLAP)的列式数据库管理系统(columnar DBMS).随着业务的增长走到尽头,查询会变得越来越慢.你可能通过增 ...

  9. MySql引擎、索引

    目录 一.MySql架构 1.连接层 2.服务层 3.引擎层 4.物理文件存储 二.Mysql引擎 1.引擎是什么? 2.mysql中MyiSam引擎和Innodb引擎的区别? 3.mysql中常见的 ...

最新文章

  1. Java项目:前台+后台精品水果商城系统设计和实现(java+Springboot+ssm+mysql+jsp+maven)
  2. 全球及中国电池行业需求前景与十四五投资规划分析报告2022-2028年版
  3. 读书笔记九:TCP/IP详解之广播和多播,IGMP协议
  4. android dialog 结构,Android 原生Dialog实现
  5. postgresql主从备份_基于windows平台的postgresql主从数据库流备份配置
  6. mysql连接池源码_一个JAVA数据库连接池实现源码
  7. 高阶函数 map,reduce, filter的用法
  8. [论文翻译]Learning Phrase Representations using RNN Encoder–Decoder for Statistical Machine Translation
  9. 编写led驱动及其实验过程
  10. matlab size11,matlab学习笔记11_3高维数组操作 filp, shiftdim, size, permute, ipermute
  11. ubuntu服务器文件权限设置密码,Ubuntu 开启 root 用户并开启 ssh 远程访问权限
  12. 【Adobe Premiere Pro 2020】pr2020安装和基本操作【PR安装、新建项目流程、导入及管理素材项目文件、添加标记、创建出入点剪辑视频、快速剪接及自动音乐卡点的方法
  13. 2023最新SSM计算机毕业设计选题大全(附源码+LW)之java学生综合考评系统b8vlm
  14. java applepay_【苹果支付】添加ApplePay的支持
  15. FPGA基础之HLS
  16. 磷脂PEG磷脂,DSPE-PEG-DSPE
  17. DAO 中独特的通证经济
  18. 中科院大学计算机科学与技术王伟强,王伟强-中国科学院大学-UCAS
  19. android usb wifi驱动下载,android 平台USB wifi驱动移植及使用
  20. 外设驱动库开发笔记47:ADS111x系列ADC驱动

热门文章

  1. 【echarts】中国地图 china.js 在线引用地址
  2. 定时任务一(quartz):纯java
  3. 【文献学习】异质异构集成
  4. 以太坊相关中文资料整理
  5. 10000+ 代码库、3000+ 研发人员大型保险集团的研发效能提升实践
  6. sql数据库之提取时间函数date()、year()、month()...及示例
  7. 中级软件测试笔试题100精讲_精选软件测试笔试题目及答案笔试题目及答案
  8. Python-提升爬虫速度三种方式
  9. 欧拉操作系统(openEuler)简介
  10. 为什么协程比线程的执行效率更高?