北京大学网站建设,公众号推广方案,wordpress博客必备插件,wordpress网页和软件视频参考:https://www.bilibili.com/video/BV1mbUBY7E24 关于游戏中文件输入输出#xff08;IO#xff09;操作的讨论。主要分为两类#xff1a; 只读资产的加载 这部分主要涉及游戏中用于展示和运行的只读资源#xff0c;例如音乐、音效、美术资源#xff08;如 3D 模型和…视频参考:https://www.bilibili.com/video/BV1mbUBY7E24 关于游戏中文件输入输出IO操作的讨论。主要分为两类 只读资产的加载 这部分主要涉及游戏中用于展示和运行的只读资源例如音乐、音效、美术资源如 3D 模型和纹理等。这些文件从磁盘加载到内存中供游戏使用但不会被修改。在现代游戏中这些数据可能非常庞大数千兆字节通常通过后台流式加载的方式避免加载屏幕过长从而提升用户体验。 游戏状态的保存和加载 这部分与游戏配置和进度相关例如窗口模式设置、声音音量设置、解锁状态、存档文件等。这些文件既需要写入磁盘也需要在后续运行时从磁盘中读取。通常这些数据量相对较小因此可以通过简单的平面调用加载不需要复杂的流式处理。
文件的读写过程因数据类型和用途的不同有不同的处理方式
只读资源需要注重性能优化如流式加载以避免影响游戏运行。状态数据则关注正确性和持久性确保配置和进度能在多次运行中保持一致。
在过去的开发中文件操作通常是通过以下步骤完成的
打开文件 使用文件名调用 openFile 函数获得文件句柄file handle。 读取文件内容 提供一个缓冲区如 128 字节通过 read 函数从文件中读取指定字节的数据。 根据返回值判断读取是否成功如果失败需要处理错误。 关闭文件 在操作完成后清理文件句柄。
// 定义文件名为 test.bmp指向字符串的指针
char *Filename test.bmp;// 打开文件并获取文件句柄
file_handle *File OpenFile(Filename);// 定义一个大小为 128 字节的缓冲区
uint8 Buffer[128];// 尝试从文件中读取缓冲区大小的数据
if(Read(File, sizeof(Buffer), Buffer)) {// 如果读取成功执行相应操作
} else {// 如果读取失败执行失败处理逻辑
}// 关闭文件以释放资源
closeFile(File);文件 I/O 操作的策略特别是针对流式文件处理streaming-based file I/O是否适合某些目的进行了详细分析。以下是内容的要点总结和理解 GetFileSize 是 Windows API 中用于获取文件大小的函数。 功能
返回指定文件的大小以字节为单位。 参数说明 hFile 输入参数文件的句柄HANDLE 类型。句柄必须是由支持文件读取的函数如 CreateFile返回的且文件不能是管道。 lpFileSizeHigh 可选参数指向一个 DWORD 类型的变量用于存储文件大小的高 32 位适用于大于 4GB 的文件。如果为 NULL则忽略高 32 位。 返回值 成功 返回文件大小的低 32 位。如果 lpFileSizeHigh 非空则其指向的值存储文件大小的高 32 位。 失败 返回 INVALID_FILE_SIZE0xFFFFFFFF。此时需要调用 GetLastError() 检查是否确实发生错误如文件大小正好等于 INVALID_FILE_SIZE不会返回错误。 注意事项
对于超过 4GB 的文件需要结合 lpFileSizeHigh 计算完整文件大小uint64_t fullFileSize ((uint64_t)lpFileSizeHigh 32) | fileSizeLow;如果文件句柄不可读函数会失败。 示例代码
#include windows.h
#include stdio.hint main() {// 打开文件HANDLE hFile CreateFileA(example.txt, // 文件路径GENERIC_READ, // 读取权限0, // 不共享NULL, // 默认安全属性OPEN_EXISTING, // 打开现有文件FILE_ATTRIBUTE_NORMAL, // 普通文件属性NULL // 无模板文件);if (hFile INVALID_HANDLE_VALUE) {printf(Failed to open file. Error: %lu\n, GetLastError());return 1;}// 获取文件大小DWORD fileSizeLow;DWORD fileSizeHigh;fileSizeLow GetFileSize(hFile, fileSizeHigh);if (fileSizeLow INVALID_FILE_SIZE GetLastError() ! NO_ERROR) {printf(Failed to get file size. Error: %lu\n, GetLastError());CloseHandle(hFile);return 1;}// 计算完整文件大小uint64_t fullFileSize ((uint64_t)fileSizeHigh 32) | fileSizeLow;printf(File size: %llu bytes\n, fullFileSize);// 关闭句柄CloseHandle(hFile);return 0;
}CloseHandle 是 Windows API 中用于关闭对象句柄的函数。 功能
关闭一个打开的句柄释放与之关联的系统资源。 参数说明
hObject 输入参数要关闭的句柄HANDLE 类型。句柄可以是文件、线程、进程、同步对象如互斥量、信号量等。 返回值 成功 返回 TRUE。 失败 返回 FALSE。可以调用 GetLastError() 获取错误码以确定失败原因例如句柄无效。 注意事项 句柄无效时调用 如果传入的句柄已经被关闭或未初始化会导致函数失败。 重复调用 不应多次关闭同一个句柄否则可能导致程序异常。 文件句柄 文件操作完成后必须调用 CloseHandle 关闭文件句柄以避免资源泄漏。 句柄类型 确保关闭的是正确类型的句柄错误的操作可能会影响其他系统资源。 示例代码
#include windows.h
#include stdio.hint main() {// 打开文件HANDLE hFile CreateFileA(example.txt, // 文件路径GENERIC_READ, // 读取权限0, // 不共享NULL, // 默认安全属性OPEN_EXISTING, // 打开现有文件FILE_ATTRIBUTE_NORMAL, // 普通文件属性NULL // 无模板文件);if (hFile INVALID_HANDLE_VALUE) {printf(Failed to open file. Error: %lu\n, GetLastError());return 1;}printf(File opened successfully.\n);// 关闭文件句柄if (CloseHandle(hFile)) {printf(Handle closed successfully.\n);} else {printf(Failed to close handle. Error: %lu\n, GetLastError());}return 0;
}用途
关闭文件、进程、线程、同步对象等句柄。释放资源防止资源泄漏或句柄耗尽。适用于清理已完成的操作保持程序高效运行。 常见场景
文件操作结束后调用。线程或进程执行完毕后释放句柄。锁或事件对象不再使用时销毁句柄。 用途
获取文件大小用于内存分配或文件操作。确保文件未超出特定大小限制。用于处理大文件时计算高低 32 位文件大小。
核心内容 流式文件 I/O 不适用当前目标 对于作者的特定目标流式文件 I/O 并不合适因为他们的需求是针对文件块的明确读取而不是从一个大型流中逐块拉取数据。他们需要读取的文件数据通常是固定大小的、可以预知的不需要使用流的逐步处理。 当前需求是高效读取 他们的重点是加载完整的文件或资产例如加载一个完整的位图文件。读取操作是“全或无”即一次性加载整个数据块而非逐步处理。 未来可能的优化 在目前开发阶段他们选择简单直接的 I/O 操作策略而不会为复杂流式操作进行过多优化。将来当需要加载一个打包的资源文件时例如包含多个资产的文件可以使用类似流式读取的系统但需要支持多线程。 日志文件写入的例外 唯一可能需要流式处理的场景是调试日志的写入但目前并没有计划实现这一功能。 对比分析 流式文件 I/O 的优势 适用于处理超大文件或实时数据流的场景比如从网络中逐步拉取视频数据。资源占用较少按需读取避免一次性加载整个文件而导致内存压力。 当前策略的选择 出于效率和简单性作者选择直接一次性读取所需的文件块而不是处理流式数据。这种方法适合加载固定大小、结构明确的文件内容。 未来策略的演变 随着需求升级例如加载复杂的打包资源可能需要采用更高级的策略比如使用多线程支持的分块流式读取。 关键总结
当前任务简单高效地加载文件无需使用流式文件处理。未来可能在需要复杂的打包文件读取时会考虑实现多线程和流式文件处理。优化方向现阶段优先选择易于实现的直接文件读取而不是为复杂功能增加开发成本。 CreateFileA 是 Windows API 中用于创建、打开文件、设备、管道或通信资源的函数。其作用和用途如下
功能
打开一个现有文件以进行读取、写入或两者操作。创建一个新文件。打开设备如磁盘驱动器、控制台等。创建/打开管道或通信端口。 参数说明 lpFileName 指向一个以空字符结尾的字符串表示文件或设备的路径名。 dwDesiredAccess 指定所需的访问模式如读取、写入或两者。常见值 GENERIC_READ: 读取访问权限GENERIC_WRITE: 写入访问权限 dwShareMode 指定文件或设备共享模式。常见值 FILE_SHARE_READ: 允许其他进程读取FILE_SHARE_WRITE: 允许其他进程写入0: 不共享 lpSecurityAttributes 可选参数指定安全属性。如果为 NULL使用默认设置。 dwCreationDisposition 指定文件的行为如是否创建新文件或覆盖现有文件。常见值 CREATE_NEW: 如果文件不存在则创建存在时失败CREATE_ALWAYS: 始终创建新文件覆盖现有文件OPEN_EXISTING: 打开现有文件文件不存在时失败OPEN_ALWAYS: 打开文件文件不存在时创建TRUNCATE_EXISTING: 打开文件并清空其内容 dwFlagsAndAttributes 指定文件或设备的标志和属性如文件是否为隐藏或系统文件。 hTemplateFile 可选参数仅用于创建文件时指定模板文件句柄。 返回值
成功返回文件或设备的句柄HANDLE 类型。失败返回 INVALID_HANDLE_VALUE可通过 GetLastError() 获取具体错误码。 常见用途
打开文件进行读取或写入。创建新的日志文件。打开串口通信设备如 COM1。操作管道或共享资源。 示例
HANDLE fileHandle CreateFileA(example.txt, // 文件路径GENERIC_READ | GENERIC_WRITE, // 读取和写入权限0, // 不共享NULL, // 默认安全属性CREATE_ALWAYS, // 始终创建新文件FILE_ATTRIBUTE_NORMAL, // 普通文件属性NULL // 不使用模板文件
);if (fileHandle INVALID_HANDLE_VALUE) {printf(Failed to create or open file. Error: %lu\n, GetLastError());
} else {printf(File created/opened successfully.\n);CloseHandle(fileHandle); // 关闭文件句柄
}ReadFile 是 Windows API 中用于从文件或 I/O 设备读取数据的函数。 功能
从文件或输入/输出设备如文件、管道、串口等中读取指定数量的字节数据到缓冲区。 参数说明 hFile 输入参数文件或设备的句柄HANDLE 类型。该句柄必须是由支持读取的函数如 CreateFile打开的并具有读取权限。 lpBuffer 输出参数指向一个缓冲区用于存储读取到的数据。如果此参数为 NULL表示无效的缓冲区函数将失败。 nNumberOfBytesToRead 输入参数要读取的字节数。指定读取操作期望完成的最大字节数。 lpNumberOfBytesRead 输出参数指向一个变量用于接收实际读取的字节数。如果设置为 NULL则调用方必须使用重叠OVERLAPPED结构处理字节计数。 lpOverlapped 输入/输出参数指向一个 OVERLAPPED 结构用于异步操作。如果未使用异步操作此参数必须为 NULL。 返回值 成功 返回 TRUE。*lpNumberOfBytesRead 包含实际读取的字节数。 失败 返回 FALSE。可以通过调用 GetLastError() 获取具体错误码。如果读取操作被挂起如使用异步操作则错误码可能为 ERROR_IO_PENDING。 注意事项 同步与异步操作 如果句柄未设置为异步模式如未指定 FILE_FLAG_OVERLAPPED操作将以同步方式执行函数在读取完成前不会返回。如果句柄为异步模式则必须提供 lpOverlapped以便跟踪操作状态。 管道或设备的特殊性 对于管道或设备ReadFile 的行为可能会根据数据可用性发生变化如阻塞或非阻塞模式。 缓冲区大小 确保 lpBuffer 有足够的大小存储 nNumberOfBytesToRead 字节数据。 示例代码
同步读取文件
#include windows.h
#include stdio.hint main() {// 打开文件HANDLE hFile CreateFileA(example.txt, // 文件路径GENERIC_READ, // 读取权限0, // 不共享NULL, // 默认安全属性OPEN_EXISTING, // 打开现有文件FILE_ATTRIBUTE_NORMAL, // 普通文件属性NULL // 无模板文件);if (hFile INVALID_HANDLE_VALUE) {printf(Failed to open file. Error: %lu\n, GetLastError());return 1;}// 读取文件内容char buffer[128] {0}; // 缓冲区DWORD bytesRead 0; // 实际读取字节数if (ReadFile(hFile, buffer, sizeof(buffer) - 1, bytesRead, NULL)) {printf(Read %lu bytes: %s\n, bytesRead, buffer);} else {printf(Failed to read file. Error: %lu\n, GetLastError());}// 关闭文件句柄CloseHandle(hFile);return 0;
}用途
从文件中读取数据用于处理或存储。从管道或串口读取输入数据。结合异步 I/O 提高性能处理多任务数据读取。 常见场景
文件数据解析。读取串口通信内容。处理日志或流数据。
VirtualAlloc 是 Windows API 中用于分配虚拟内存的函数。 功能
分配、保留或提交一个虚拟内存区域并可设置该内存区域的访问权限。 参数说明 lpAddress 输入参数指定内存区域的首地址可选。如果为 NULL系统将自动选择合适的地址。如果非 NULL则表示请求分配特定的地址但需要符合内存对齐要求。 dwSize 输入参数要分配的内存大小以字节为单位。必须为系统页面大小通常为 4KB的倍数。 flAllocationType 输入参数指定内存分配的类型。常见值 MEM_COMMIT: 提交内存分配实际的物理内存或交换文件空间。MEM_RESERVE: 保留内存地址空间但不分配物理内存。MEM_RESET: 将指定内存标记为已重置但保留其保留状态。MEM_RESET_UNDO: 撤销 MEM_RESET 操作。 flProtect 输入参数指定内存区域的访问保护类型。常见值 PAGE_READONLY: 只读访问权限。PAGE_READWRITE: 可读可写访问权限。PAGE_EXECUTE: 可执行但不可读写权限。PAGE_EXECUTE_READWRITE: 可执行、可读、可写权限。 返回值 成功 返回分配的内存区域的起始地址LPVOID 类型。 失败 返回 NULL。可以调用 GetLastError() 获取具体错误码。 注意事项 内存释放 使用 VirtualFree 释放通过 VirtualAlloc 分配的内存避免内存泄漏。 内存类型 如果同时指定 MEM_COMMIT | MEM_RESERVE表示既保留地址空间又提交内存。 页面大小对齐 分配的内存大小和地址必须符合页面大小对齐要求通常为 4KB。 分配失败 可能由内存不足、无效参数或地址冲突导致分配失败。 示例代码
分配和释放内存
#include windows.h
#include stdio.hint main() {// 分配 16KB 内存SIZE_T size 16 * 1024; // 16KBLPVOID lpMemory VirtualAlloc(NULL, // 系统选择地址size, // 分配大小MEM_COMMIT | MEM_RESERVE, // 提交和保留内存PAGE_READWRITE // 可读可写权限);if (lpMemory NULL) {printf(Memory allocation failed. Error: %lu\n, GetLastError());return 1;}printf(Memory allocated at address: %p\n, lpMemory);// 使用分配的内存char *data (char *)lpMemory;for (int i 0; i size; i) {data[i] A;}printf(Memory written successfully.\n);// 释放内存if (!VirtualFree(lpMemory, 0, MEM_RELEASE)) {printf(Memory release failed. Error: %lu\n, GetLastError());return 1;}printf(Memory released successfully.\n);return 0;
}用途
创建大内存块以供动态使用。管理内存保护以实现安全或性能优化如只读、只执行内存。实现内存映射文件、动态加载器或自定义内存分配器。 常见场景
高性能计算时分配大内存块。动态管理虚拟内存。实现内存保护和访问权限控制。