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

lua和C的交互

1.C调用lua例子

#include <iostream>
#include <lua.hpp>int main() 
{//用于创建一个新的lua虚拟机lua_State* L = luaL_newstate();luaL_openlibs(L);//打开标准库/*if (luaL_dofile(L, "test.lua") != LUA_OK) {std::cerr << "Lua error: " << lua_tostring(L, -1) << std::endl;}luaL_dofile = luaL_loadfile + lua_pcall(L, 0, 0, 0);*//*编译和解析lua代码 但是并不会运行lua代码*/if (luaL_loadfile(L, "test.lua") != LUA_OK){std::cerr << "Lua error: " << lua_tostring(L, -1) << std::endl;return -1;}/*全局变量和函数此时才会注册到lua虚拟机里面 如果不调用lua_pcall 将获取不到函数*/lua_pcall(L, 0, 0, 0);lua_getglobal(L, "str");lua_pushstring(L, "ZZH ");lua_pushnumber(L, 25);// 调用函数 (2个参数,1个返回值, 错误处理函数的index是0 表示没有)if (lua_pcall(L, 2, 1, 0) != 0) {  std::cerr << "Error: " << lua_tostring(L, -1) << std::endl;return 1;}// 获取返回值 在栈顶const char *str = lua_tostring(L, -1); lua_pop(L, 1);  // 弹出返回值std::cout << "Result: " << str << std::endl;//关闭lua虚拟机lua_close(L);return 0;
}

2.lua虚拟栈

lua是一个脚本,经常用来和宿主程序交互,他们之间通过lua虚拟机沟通
在这里插入图片描述
lua的堆栈是用数字索引的,1代表栈底, -1代表栈顶

我们可以看到在上面C调用lua的时候,先获取到lua中的全局函数str
lua_getglobal这个函数将获取到的参数压入栈顶,然后依次调用lua_pushxxxx函数将两个参数也压入栈顶。
lua_pcall这个函数是用来调用lua函数的,lua_pcall的第一个参数是lua虚拟机,第二个参数是函数参数个数,第三个参数是期望的返回值个数,第四个参数是错误处理函数在栈中的位置。
执行完lua_pcall之后,lua的函数栈自己清理,然后将结果放到栈顶,我们获取到结果之后,必须调用lua_pop将返回值弹出,否则会内存泄漏

3.往栈中压入元素

lua的栈中可以压入任意的lua类型
在这里插入图片描述

4.从栈中获取一个值,index是栈中的位置

在这里插入图片描述

5.判断栈中元素类型

在这里插入图片描述

6.通用栈操作

在这里插入图片描述

1.lua_gettop返回栈中元素的个数,也就是栈顶索引
2.lua_settop将栈顶设置为一个值,即修改栈中元素数量,多了补nil
3.lua_pushvalue 将栈中指定索引位置处的元素拷贝一份到栈顶
4.lua_rotate 将栈中指定index位置处的元素,向上/向下移动
5.lua_replace弹出一个值,并且栈顶设置为索引上的值
6.lua_copy将一个索引处的值复制到另外一个索引上

7.对table的操作函数

在这里插入图片描述


8.lua调用C函数

C函数要想被lua调用,那么函数原型必须是

int (*Cfun)(lua_State*L);返回值代表了参数个数
{return 1;	//代表往栈中压入了1个返回值
}

举个例子

int Cadd(lua_State* L)
{//lua压入的第一个参数int a =  lua_tonumber(L, 1);//lua压入的第二个参数int b = lua_tonumber(L, 2);int c = a + b;std::cout << "a = " << a << " b = " << b << std::endl;lua_pushnumber(L, c);//返回一个参数return 1;
}int main() 
{//用于创建一个新的lua虚拟机lua_State* L = luaL_newstate();luaL_openlibs(L);//打开标准库//将C函数放入到lua的虚拟栈上lua_pushcfunction(L, Cadd);//将Cadd这个C函数注册到globallua_setglobal(L, "Cadd");if (luaL_dofile(L, "test.lua") != LUA_OK) {std::cerr << "Lua error: " << lua_tostring(L, -1) << std::endl;}//关闭lua虚拟机lua_close(L);return 0;
}--lua代码function CallAdd()print("lua call Cfun ret = " .. Cadd(10, 20))
endCallAdd()

结果
在这里插入图片描述

9.lua_Reg批量导入C函数

int Cadd(lua_State* L)
{int a = luaL_checkinteger(L, 1);int b = luaL_checkinteger(L, 2);std::cout << "Cadd a=" << a << " b = " << b << std::endl;lua_pushinteger(L, a + b);return 1;
}int Csub(lua_State* L)
{int a = luaL_checkinteger(L, 1);int b = luaL_checkinteger(L, 2);std::cout << "Csub a=" << a << " b = " << b << std::endl;lua_pushinteger(L, a - b);return 1;
}luaL_Reg g_Cfuns[] = {{"Cadd", Cadd},{"Csub", Csub},{"NULL", nullptr}
};int main() 
{//用于创建一个新的lua虚拟机lua_State* L = luaL_newstate();luaL_openlibs(L);//打开标准库luaL_newlib(L, g_Cfuns);lua_setglobal(L, "g_Cfuns"); // 将 table 绑定到全局变量 "g_Cfuns"if (luaL_dofile(L, "test.lua") != LUA_OK) {std::cerr << "Lua error: " << lua_tostring(L, -1) << std::endl;}//关闭lua虚拟机lua_close(L);return 0;
}
function CallAdd()print("lua call Cfun ret = " .. g_Cfuns.Cadd(10, 20))
print("lua call Cfun ret = " .. g_Cfuns.Csub(20, 10))
endCallAdd()

拿一段skynet中的代码来演示另外一种用法

luaL_Reg l[] = {{ "start", lstart },        // 绑定 C 函数 lstart{ "stop", lstop },          // 绑定 C 函数 lstop{ "resume", luaB_coresume },// 绑定 C 函数 luaB_coresume{ "wrap", luaB_cowrap },    // 绑定 C 函数 luaB_cowrap{ NULL, NULL },             // 结束标记
};luaL_newlibtable(L, l); // 1. 创建一个与 l 数组大小相同的 table(但此时 table 为空)lua_newtable(L); // 2. 创建一个新的 table(用于存储线程 start 时间)
lua_newtable(L); // 3. 创建一个新的 table(用于存储线程 total 运行时间)lua_newtable(L); // 4. 创建一个弱表(用于存储线程数据)lua_pushliteral(L, "kv"); // 5. 压入字符串 "kv",表示 key-value 方式的弱表模式
lua_setfield(L, -2, "__mode"); // 6. 设置 `__mode` 字段,使该 table 变成弱引用表lua_pushvalue(L, -1); // 7. 复制弱表,使两个线程时间表共享相同的弱表
lua_setmetatable(L, -3); // 8. 设置 `thread->start time` 表的 metatable(弱引用)
lua_setmetatable(L, -3); // 9. 设置 `thread->total time` 表的 metatable(弱引用)luaL_setfuncs(L, l, 2); // 10. 绑定 l 中的 C 函数,并将栈顶的 2 个 table 作为 upvalue 传入

10.数组操作 lua_geti

第一个参数是lua虚拟机,第二个参数是数组在栈中的索引,第三个参数是在表中的下标

int visitArr(lua_State* L)
{luaL_checktype(L, 1, LUA_TTABLE);   //检验是否是tablelua_len(L, 1);int len = lua_tointeger(L, -1);lua_pop(L, 1);for (int i = 1; i <= len; i++){lua_pushvalue(L, 1);lua_geti(L, 1, i);std::cout << " i = " << i << "  num=" << lua_tointeger(L, -1) << std::endl;lua_pop(L, 1);}return 0;
}luaL_Reg g_Cfuns[] = {{"visitArr", visitArr},{"NULL", nullptr}
};int main() 
{//用于创建一个新的lua虚拟机lua_State* L = luaL_newstate();luaL_openlibs(L);//打开标准库luaL_newlib(L, g_Cfuns);lua_setglobal(L, "g_Cfuns"); // 将 table 绑定到全局变量 "g_Cfuns"if (luaL_dofile(L, "test.lua") != LUA_OK) {std::cerr << "Lua error: " << lua_tostring(L, -1) << std::endl;}//关闭lua虚拟机lua_close(L);return 0;
}function testArr()g_Cfuns.visitArr({7,5,3,4})
endtestArr()

11.字符串的操作

当C函数接收到一个lua字符串为参数时,必须遵守两个原则:1.使用字符串期间不能将其从栈中弹出 2.不应该修改字符串

    const char* str = "Hello World";lua_getglobal(L, "testStr");lua_pushlstring(L, str + 0, strlen(str));function testStr(str)print("lua str = " .. str)end

一旦调用了lua_pushlstring,那么在lua栈上就会产生一个字符串的副本,lua会管理这部分内存,C上面的字符串释放与否,都没关系


lua_pushfstring
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

12.在C函数中保存状态

注册表registry,也就等于是提供了一个全局变量吧,但是lua全局变量会被lua代码访问修改 不安全

注册表是一个只能被C代码访问的全局表, 总是位于LUA_REGISTRYINDEX中,伪索引就像是栈中的索引,但是关联的值不在栈中。
获取注册表中键为key的值,可以使用
lua_getfield(L, LUA_REGISTRYINDEX, "key");设置注册表中键为key的值
lua_pushlightuserdata(L, 一个指针);
lua_setfield(L, LUA_REGISTRYINDEX, "key");
注册表是一个普通的lua表,但是不能用数字作为键

upvalue上值实现了一种类似于C语言静态变量的机制,每一次在lua中创建新的C函数时候,可以将任意数量的上值和这个函数关联起来,每个上值保存一个lua值,后面调用该函数的时候,通过伪索引自由访问这些值

#include <lua.h>
#include <lauxlib.h>// 计数器增加
int lua_counter_add(lua_State *L) {int count = lua_tointeger(L, lua_upvalueindex(1));count += luaL_checkinteger(L, 1);  // 加上 Lua 传入的值lua_pushinteger(L, count);lua_replace(L, lua_upvalueindex(1));  // 更新 upvaluereturn 1;
}// 计数器读取
int lua_counter_get(lua_State *L) {lua_pushinteger(L, lua_tointeger(L, lua_upvalueindex(1)));  // 返回 upvaluereturn 1;
}// 初始化模块
int luaopen_counter(lua_State *L) {lua_pushinteger(L, 0);  // 计数器初始值 0lua_pushcclosure(L, lua_counter_add, 1);  // 绑定 upvaluelua_setglobal(L, "add_count");  // 绑定到全局变量 add_countlua_pushinteger(L, 0);  // 计数器初始值 0lua_pushcclosure(L, lua_counter_get, 1);  // 绑定 upvaluelua_setglobal(L, "get_count");  // 绑定到全局变量 get_countreturn 0;
}add_count(5)
add_count(3)
print(get_count())  -- 8
add_count(2)
print(get_count())  -- 10

luaL_setfuncs 这个也可以为函数集合设置他们的upvalue

13.require

在这里插入图片描述
在这里插入图片描述

14.C中的 用户自定义类型

一个简单的例子

struct Pos
{int x;int y;
};int setX(lua_State* L)
{Pos * p = (Pos*)lua_touserdata(L, 1);int v = lua_tointeger(L, 2);p->x = v;return 0;
}int setY(lua_State* L)
{Pos* p = (Pos*)lua_touserdata(L, 1);int v = lua_tointeger(L, 2);p->y = v;return 0;
}int getX(lua_State* L)
{Pos* p = (Pos*)lua_touserdata(L, 1);lua_pushinteger(L, p->x);return 1;
}int getY(lua_State* L)
{Pos* p = (Pos*)lua_touserdata(L, 1);lua_pushinteger(L, p->y);return 1;
}int createNewPos(lua_State* L)
{Pos * p =  (Pos*)lua_newuserdata(L, sizeof(Pos));int x = lua_tointeger(L, 1);int y = lua_tointeger(L, 2);p->y = y;p->x = x;return 1;
}luaL_Reg g_Cfuns[] = {{"createNewPos", createNewPos},{"setX", setX},{"setY", setY},{"getX", getX},{"getY", getY},{"NULL", nullptr}
};int main() 
{//用于创建一个新的lua虚拟机lua_State* L = luaL_newstate();luaL_openlibs(L);//打开标准库luaL_newlib(L, g_Cfuns);lua_setglobal(L, "g_Cfuns"); // 将 table 绑定到全局变量 "g_Cfuns"if (luaL_dofile(L, "test.lua") != LUA_OK) {std::cerr << "Lua error: " << lua_tostring(L, -1) << std::endl;}//关闭lua虚拟机lua_close(L);return 0;
}local pos = g_Cfuns.createNewPos(10, 20)
print(g_Cfuns.getX(pos))

但是这个用法不规范,lua不能直接调用他的方法 也不能安全访问字段,一般需要绑定元表

#include <lua.hpp>
#include <iostream>struct Pos {int x;int y;
};int setX(lua_State* L) {Pos* p = (Pos*)luaL_checkudata(L, 1, "PosMeta"); // 安全获取 userdatap->x = luaL_checkinteger(L, 2);return 0;
}int setY(lua_State* L) {Pos* p = (Pos*)luaL_checkudata(L, 1, "PosMeta");p->y = luaL_checkinteger(L, 2);return 0;
}int getX(lua_State* L) {Pos* p = (Pos*)luaL_checkudata(L, 1, "PosMeta");lua_pushinteger(L, p->x);return 1;
}int getY(lua_State* L) {Pos* p = (Pos*)luaL_checkudata(L, 1, "PosMeta");lua_pushinteger(L, p->y);return 1;
}int createNewPos(lua_State* L) {int x = luaL_checkinteger(L, 1);int y = luaL_checkinteger(L, 2);Pos* p = (Pos*)lua_newuserdata(L, sizeof(Pos));p->x = x;p->y = y;luaL_getmetatable(L, "PosMeta");lua_setmetatable(L, -2);  // 绑定元表return 1;
}luaL_Reg pos_methods[] = {{"setX", setX},{"setY", setY},{"getX", getX},{"getY", getY},{nullptr, nullptr}
};extern "C" int luaopen_mylib(lua_State* L) {luaL_newmetatable(L, "PosMeta");lua_pushvalue(L, -1);lua_setfield(L, -2, "__index");  // 让对象可以用 `pos:getX()` 访问方法luaL_setfuncs(L, pos_methods, 0);  // PosMeta 绑定方法 lua_newtable(L);lua_pushcfunction(L, createNewPos);lua_setfield(L, -2, "createNewPos");return 1;
}int main() {lua_State* L = luaL_newstate();luaL_openlibs(L);luaopen_mylib(L);  // 注册模块lua_setglobal(L, "PosLib");if (luaL_dofile(L, "test.lua") != LUA_OK) {std::cerr << "Lua error: " << lua_tostring(L, -1) << std::endl;}lua_close(L);return 0;
}------lua
local pos = PosLib.createNewPos(10, 20)print(pos:getX())  -- 10
print(pos:getY())  -- 20pos:setX(30)
pos:setY(40)print(pos:getX())  -- 30
print(pos:getY())  -- 40

相关文章:

lua和C的交互

1.C调用lua例子 #include <iostream> #include <lua.hpp>int main() {//用于创建一个新的lua虚拟机lua_State* L luaL_newstate();luaL_openlibs(L);//打开标准库/*if (luaL_dofile(L, "test.lua") ! LUA_OK) {std::cerr << "Lua error: &…...

Css:如何解决绝对定位子元素内容被父级元素overflow:hidden属性剪裁

一、问题描述 今天小伙伴提了一个bug&#xff0c;在点击列表项的“…”按钮应该出现的悬浮菜单显示不完整&#xff1a; 二、问题排查 一般这种问题&#xff0c;是由于悬浮菜单采用的是绝对定位&#xff0c;而父级采用了overflow:hidden属性。但需要注意的是&#xff0c;这里的…...

RoMo: Robust Motion Segmentation Improves Structure from Motion

前言 看起来像是一篇投稿CVPR的文章&#xff0c;不知道被哪个瞎眼审稿人拒了。同期还有一篇CVPR被接收的工作Segment Any Motion in Videos&#xff0c;看起来不如这篇直白&#xff08;也可能是因为我先看过spotlesssplats的缘故&#xff09;&#xff0c;后面也应该一并介绍了…...

MCP 极简入门 - 三分钟 Cline + Smithery 运行 time 服务

文章目录 一、&#x1f680; 初识Smithery&#xff1a;AI服务的新大陆找到心仪的服务 二、Cline 编辑配置文件&#x1f527;1、打开配置文件2. 添加Time Server配置3. 验证配置效果 三、&#x1f4ac; 实战对话&#xff1a;让AI告诉你时间四、服务管理小技巧&#x1f504;&…...

基本机动飞行性能

机动飞行时描述飞机在给定构型和发动机工作状态下改变飞行速度、飞行高度和飞行方向的能力 1. 水平加&#xff08;减&#xff09;速 水平加&#xff08;减&#xff09;速性能反映飞机在水平面内改变直线飞行速度的能力。描述水平加&#xff08;减&#xff09;速性能的参数包括…...

ES6 新特性全面总结

ES6 新特性全面总结 ES6(ECMAScript 2015)是JavaScript语言的重大更新&#xff0c;引入了许多强大的新特性&#xff0c;极大地提升了JavaScript的开发体验和能力。以下是ES6主要新增知识点的详细总结&#xff1a; (一)、ES6变量声明&#xff1a;let 和 const 详解 一、let 和…...

【Linux】进程间通信、匿名管道、进程池

一.什么是通信 进程间通信(Inter-Process Communication&#xff0c;IPC),是指在操作系统中&#xff0c;不同进程之间进行数据交换和同步的机制。由于每个进程通常拥有独立的内存空间&#xff0c;进程间无法直接访问对方的内存&#xff0c;因此需要通过特定的机制来实现通信和…...

【HTML】纯前端网页小游戏-戳破彩泡

分享一个简单有趣的网页小游戏 - 彩色泡泡爆破。玩家需要点击屏幕上随机出现的彩色泡泡来得分。 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-wi…...

【MATLAB定位例程】TDOA(到达时间差)的chan-tylor,三维环境,附完整代码

该代码实现了基于三维空间的动态目标TDOA定位,结合了Chan算法(解析解)与Taylor级数展开法(迭代优化)的双重优势。 文章目录 运行结果MATLAB代码代码讲解代码功能概述核心算法原理代码结构解析可视化与结果分析运行结果 定位示意图: 三轴状态曲线: 三轴误差曲线: MA…...

数字化转型中的开源AI智能客服与S2B2C商城小程序的融合创新

摘要 数字经济时代&#xff0c;企业需通过技术重构用户交互与供应链体系。本文以“开源AI智能客服”“AI智能名片”及“S2B2C商城小程序”为核心&#xff0c;研究三者如何通过技术协同与场景化应用实现企业营销、客户服务与供应链管理的智能化升级。通过案例分析、技术架构设…...

重生之我是去噪高手——diffusion model

diffusion model是如何运作的&#xff1f; 想象一下&#xff0c;你有一张清晰的图片。扩散模型的核心思想分为两个过程&#xff1a; 前向过程&#xff08;Forward Process / Diffusion Process&#xff09;&#xff1a;逐步加噪反向过程&#xff08;Reverse Process / Denois…...

【C#】.net core 6.0 依赖注入常见问题之一,在构造函数使用的类,都需要注入到容器里,否则会提示如下报错,让DeepSeek找找原因,看看效果

&#x1f339;欢迎来到《小5讲堂》&#x1f339; &#x1f339;这是《C#》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。&#x1f339; &#x1f339;温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01;&#…...

论文阅读笔记——RDT-1B: A DIFFUSION FOUNDATION MODEL FOR BIMANUAL MANIPULATION

RDT-1B 论文 模型表达与泛化能力&#xff1a;由于双臂操作中动作空间维度是单臂空间的两倍&#xff0c;传统方法难以建模其多模态分布。 数据&#xff1a;双臂数据少且不同机器人的物理结构和动作空间差异&#xff08;如关节数、运动范围&#xff09;导致数据分布不一致&#x…...

Vue中将pdf文件转为图片

平时开发中,我们经常遇到的场景应该是调用后端接口返回给前端pdf格式的文件流,然后我们可以通过URL.createObjectURL的方式转为object url临时路径然后可以通过window.open的方式来打开一个新的浏览器页签来进行预览,效果如下图: 但有时候这样满足不了的需求,它不想这样预…...

day39——输入操作:多值输入

数组输入&#xff1a; int main() {//***** 1、多值输入&#xff08;C&#xff09;/*输入&#xff1a;3 --> 3个值5 4 9*/int n;cin >> n; //输入个数const int MAX_SIZE 0xFFFF;//限定最大个数int a[MAX_SIZE];for (int i 0; i < n; i) {//用 n 作控制输入…...

微软的 Copilot 现在可以浏览网页并为您执行操作

在庆祝其 50 岁生日之际&#xff0c;微软正在向其人工智能驱动的 Copilot 聊天机器人传授一些新技巧。 从 BASIC 到 AI&#xff0c;改变世界的公司&#xff1a;微软 微软表示&#xff0c;Copilot 现在可以在“大多数网站”上采取行动&#xff0c;使其能够预订门票、预订餐厅等…...

elasticsearch 7.17 索引模板

文章目录 概要 概要 模板 import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.introns.framework.es.builder.OperationsBuilder; import java.util.HashMap; import java.util.Map;abstract class AbstractBuilder<T extends Abstrac…...

深入理解Python元组:从基础到高级应用

1. 元组基础认知 1.1 什么是元组 不可变序列&#xff1a;元组(tuple)是Python内置的不可变序列类型异构容器&#xff1a;可以存储不同类型的数据&#xff08;与列表类似&#xff09;语法特征&#xff1a;使用圆括号()定义&#xff0c;元素间用逗号分隔 # 基本示例 t1 (1, 2…...

【零基础入门unity游戏开发——动画篇】unity旧动画系统Animation组件的使用

考虑到每个人基础可能不一样&#xff0c;且并不是所有人都有同时做2D、3D开发的需求&#xff0c;所以我把 【零基础入门unity游戏开发】 分为成了C#篇、unity通用篇、unity3D篇、unity2D篇。 【C#篇】&#xff1a;主要讲解C#的基础语法&#xff0c;包括变量、数据类型、运算符、…...

Python+AI提示词用贝叶斯样条回归拟合BSF方法分析樱花花期数据模型构建迹图、森林图可视化

原文链接&#xff1a;https://tecdat.cn/?p41308 在数据科学的领域中&#xff0c;我们常常会遇到需要处理复杂关系的数据。在众多的数据分析方法中&#xff0c;样条拟合是一种非常有效的处理数据非线性关系的手段。本专题合集围绕如何使用PyMC软件&#xff0c;对樱花花期数据进…...

记一个.NET AOT交叉编译时的坑

记一个.NET AOT交叉编译时的坑 背景&#xff1a; 使用.NET9开发的Avalonia项目需要部署到Linux-arm64 踩坑&#xff1a; 根据官方AOT交叉编译文档配置后执行打包 dotnet publish -r linux-arm64提示error : The PrivateSdkAssemblies ItemGroup is required for _ComputeA…...

消息中间件对比与选型指南:Kafka、ActiveMQ、RabbitMQ与RocketMQ

目录 引言 消息中间件的定义与作用 消息中间件在分布式系统中的重要性 对比分析的四种主流消息中间件概述 消息中间件核心特性对比 消息传递模型 Kafka&#xff1a;专注于发布-订阅模型 ActiveMQ&#xff1a;支持点对点和发布-订阅两种模型 RabbitMQ&#xff1a;支持点…...

实战打靶集锦-38-inclusiveness

文章目录 1. 主机发现2. 端口扫描&服务枚举3. 服务探查4.系统提权 靶机地址&#xff1a;https://download.vulnhub.com/inclusiveness/Inclusiveness.ova 1. 主机发现 目前只知道目标靶机在192.168.56.xx网段&#xff0c;通过如下的命令&#xff0c;看看这个网段上在线的主…...

JVM 学习计划表(2025 版)

JVM 学习计划表&#xff08;2025 版&#xff09; &#x1f4da; 基础阶段&#xff08;2 周&#xff09; 1. JVM 核心概念 ​JVM 作用与体系结构 理解 JVM 在 Java 跨平台运行中的核心作用&#xff0c;掌握类加载子系统、运行时数据区、执行引擎的交互流程​内存结构与数据存…...

Unhandled exception: org.apache.poi.openxml4j.exceptions.InvalidFormatException

代码在main方法里面没有报错&#xff0c;在Controller里面就报错了。 原来Controller类里面少了行代码 import org.apache.poi.openxml4j.exceptions.InvalidFormatException; 加上去就解决了。...

Java的Selenium元素定位-xpath

xpath其实就是一个path(路径)&#xff0c;一个描述页面元素位置信息的路径&#xff0c;相当于元素的坐标xpath基于XML文档树状结构&#xff0c;是XML路径语言&#xff0c;用来查询xml文档中的节点。 绝对定位 从根开始找--/(根目录)/html/body/div[2]/div/form/div[5]/button缺…...

【QT】Qt5 QtWebEngine使用教程

目录 1、QtWebEngine相比于QtWebKit的优势2、项目配置2.1 确认 Qt 版本2.2 在.pro 文件中添加依赖3、显示网页4、实现Qt和网页JavaScript之间的交互4.1 Qt执行网页的JavaScript代码4.2 JavaScript调用Qt对象的函数QtWebEngine 是 Qt 框架中用于在应用程序中嵌入 Web 内容的模块…...

python基础-13-处理excel电子表格

文章目录 【README】【13】处理Excel电子表格【13.1】Excel文档【13.2】安装openpyxl模块【13.3】读取Excel文档【13.3.1】使用openpyxl模块打开excel文档【13.3.2】从工作簿取得工作表【13.3.3】从工作表sheet获取单元格cell【13.3.5】从表中获取行和列【13.3.6】工作簿、工作…...

03.unity开发资源 获取

03.unity开发资源 获取 提示&#xff1a;帮帮志会陆续更新非常多的IT技术知识&#xff0c;希望分享的内容对您有用。本章分享的是Python基础语法。前后每一小节的内容是存在的有&#xff1a;学习and理解的关联性&#xff0c;希望对您有用~ unity简介-unity基础 第三节 &#xf…...

设计模式简述(四)模板方法模式

模板方法模式 描述基本定义使用 描述 当一系列业务的基本流程是相同的&#xff0c;对于不同的业务可以在各自子类实现 所谓模板方法指的就是父类中固定的那部分代码 其实这里的思想和前面设计原则中开闭原则的描述是一致的&#xff0c;父类中的模板代码就是稳定的部分&#x…...