当前位置: 首页 > news >正文

C++新手也能懂:手把手教你用xlnt库从Excel读取游戏配置表(含中文乱码解决)

C++游戏开发实战:用xlnt库高效读取Excel配置表

独立游戏开发中,角色属性、道具数据和关卡配置通常需要频繁调整。将这些信息硬编码在C++源码里不仅难以维护,每次修改还要重新编译。Excel表格作为配置表是更优雅的解决方案——但如何让C++程序读取这些数据?这就是xlnt库大显身手的地方。

1. 环境准备与基础配置

1.1 安装xlnt库

xlnt是一个纯头文件的C++库,支持跨平台操作Excel文件。对于Windows平台,最简单的安装方式是通过vcpkg:

vcpkg install xlnt

如果使用CMake管理项目,在CMakeLists.txt中添加:

find_package(xlnt REQUIRED) target_link_libraries(YourProject PRIVATE xlnt::xlnt)

1.2 创建基础项目结构

建议为游戏配置系统建立专门的管理类:

// GameConfig.h #pragma once #include <xlnt/xlnt.hpp> #include <map> #include <string> class GameConfig { public: bool LoadFromExcel(const std::string& filePath); private: std::map<int, Character> characters; // 角色配置 std::map<int, Item> items; // 道具配置 };

2. 读取Excel基础数据

2.1 加载工作簿与工作表

bool GameConfig::LoadFromExcel(const std::string& filePath) { try { xlnt::workbook wb; wb.load(filePath); auto characterSheet = wb.sheet_by_title("Characters"); auto itemSheet = wb.sheet_by_title("Items"); // 后续处理... } catch (const std::exception& e) { std::cerr << "加载Excel失败: " << e.what() << std::endl; return false; } return true; }

2.2 遍历行列数据

读取角色表的典型模式:

for (auto row : characterSheet.rows()) { Character character; character.id = row[0].value<int>(); character.name = row[1].value<std::string>(); character.hp = row[2].value<int>(); characters[character.id] = character; }

3. 解决中文乱码问题

3.1 UTF-8编码处理

Excel文件中的中文需要使用UTF-8编码处理。在C++17及以上版本中:

std::u8string utf8Name = row[1].value<std::u8string>();

对于C++11/14,需要转换处理:

std::string utf8Name = row[1].value<std::string>(); // 确保你的终端/显示系统支持UTF-8

3.2 字体设置技巧

如果导出数据显示乱码,可能需要设置合适的字体:

xlnt::font chineseFont; chineseFont.name("Microsoft YaHei"); // 或SimSun等中文字体 ws.cell("A1").font(chineseFont);

4. 高级数据处理技巧

4.1 数据验证与转换

auto ValidateCell = [](const xlnt::cell& cell, auto& output) -> bool { if (cell.data_type() != xlnt::cell::type::number) { return false; } try { output = cell.value<decltype(output)>(); return true; } catch (...) { return false; } }; int hpValue; if (!ValidateCell(row[2], hpValue)) { std::cerr << "无效的HP值" << std::endl; continue; }

4.2 性能优化建议

对于大型配置表:

  1. 禁用公式计算:

    wb.calculation_properties().force_full_calculation(false);
  2. 批量读取数据:

    auto range = ws.range("A2:D100"); for (auto row : range) { // 处理每行数据 }

5. 实战:游戏配置系统实现

5.1 数据结构设计

struct Character { int id; std::string name; int hp; int attack; // 其他属性... }; struct Item { int id; std::string name; ItemType type; int value; // 其他属性... };

5.2 完整加载流程

bool GameConfig::LoadFromExcel(const std::string& filePath) { characters.clear(); items.clear(); xlnt::workbook wb; try { wb.load(filePath); // 加载角色数据 auto charSheet = wb.sheet_by_title("Characters"); for (auto row : charSheet.rows(false)) { // false表示跳过空行 if (row[0].row() == 1) continue; // 跳过标题行 Character c; c.id = row[0].value<int>(); c.name = row[1].value<std::string>(); c.hp = row[2].value<int>(); c.attack = row[3].value<int>(); characters[c.id] = c; } // 加载道具数据 auto itemSheet = wb.sheet_by_title("Items"); for (auto row : itemSheet.rows(false)) { if (row[0].row() == 1) continue; Item item; item.id = row[0].value<int>(); item.name = row[1].value<std::string>(); item.type = static_cast<ItemType>(row[2].value<int>()); item.value = row[3].value<int>(); items[item.id] = item; } } catch (...) { return false; } return true; }

6. 常见问题排查

6.1 文件路径问题

确保使用正确的文件路径格式:

  • Windows:"C:\\Path\\To\\File.xlsx"R"(C:\Path\To\File.xlsx)"
  • Unix-like:"/path/to/file.xlsx"

6.2 数据类型不匹配

使用安全类型转换:

template <typename T> std::optional<T> SafeGetValue(const xlnt::cell& cell) { try { return cell.value<T>(); } catch (...) { return std::nullopt; } } if (auto val = SafeGetValue<int>(row[0])) { // 使用*val } else { // 处理错误 }

6.3 内存管理建议

对于大型Excel文件:

  • 按需读取,不要一次性加载所有数据
  • 使用wb.clear()及时释放内存
  • 考虑使用数据库替代超大Excel文件
http://www.jsqmd.com/news/746304/

相关文章:

  • 硬核干货】万字长文吃透PID算法:从通俗原理解析到C语言实战落地(附保姆级调参口诀)
  • 联邦迁移学习(FTL)深度解析:原理、实战与未来
  • 如何永久禁用Windows Defender:开源管理工具的终极指南
  • MakerAi:AI如何革新硬件开发,从代码生成到全流程辅助
  • Qt6实战:用QProcess、共享内存和TCP/IP三种方式搞定进程间通信(附完整代码)
  • Ollama桌面客户端:图形化界面提升本地大模型管理效率
  • 联想ThinkEdge SE60n Gen 2边缘AI计算机解析
  • 5分钟解锁Cursor Pro无限使用:告别AI编程助手限制的终极方案
  • TiKV内存管理终极指南:10个实用技巧避免内存溢出
  • macbook开发环境的配置记录
  • 10个Amazon Redshift Utils安全最佳实践:身份管理和权限控制完整指南
  • Rust 微服务性能优化:从 500ms 到 50ms 的实战记录
  • 从图像处理到推荐系统:盘点np.linalg.norm()在Python项目里的5个高频用法
  • Gerev AI API使用教程:构建自定义搜索应用的最佳实践
  • Node Editor Framework安装配置详解:从UPM到开发版本的全流程教程
  • 【Java 25密封类模式实战指南】:20年架构师亲授5大高危误用场景与3步安全迁移法
  • Depth-Anything-V2:重新定义单目深度估计的技术范式与产业应用边界
  • 终极Streamlink Twitch GUI高级配置指南:自定义播放器、热键和主题设置全攻略
  • Krypton:革命性.NET WinForms控件套件完全指南
  • 终极指南:如何快速实现blog_os的多平台交叉编译与工具链配置
  • Pearcleaner:macOS系统清理的终极解决方案,彻底告别应用残留文件
  • 夜间视觉与深度估计:UniK3D与EgoNight技术解析
  • PEzor源码深度解析:Shellcode加载与注入机制揭秘
  • 终极指南:ForkHub项目架构全解析——基于官方废弃应用的Android GitHub客户端重生之路
  • 终极指南:使用Rust编写云原生操作系统的完整教程
  • tmux-sensible代码架构分析:从bash脚本看优雅的配置管理
  • macOS开发环境终极安全指南:Laptop脚本权限设置最佳实践
  • StyleGAN3跨模型迁移学习终极指南:基于预训练权重的快速微调方法
  • 从智能家居到工业网关:一文讲透I2C、SPI、Modbus、CAN在真实项目里的选型逻辑
  • 终极指南:Mini Tokyo 3D如何利用公共交通开放数据构建实时3D地图