热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Lua教程(五):C/C++操作Lua数组和字符串示例

这篇文章主要介绍了Lua教程(五):CC++操作Lua数组和字符串示例,本文同时还讲解了如何在CC++函数里面存储Lua状态,需要的朋友可以参考下

本文将介绍如何在C/C++里面操作Lua的数组和字符串类型,同时还会介绍如何在C/C++函数里面存储Lua状态(registry和upvalue),而registry在使用C/C++自定义类型时非常有用,可以方便地为userdata指定metatable。

C/C++操作Lua数组

Lua数组Overview

在Lua里面,数组只不过是key为整数的table而已。比如一个table为array = {12,”Hello”, “World”},它是一个数组,可以用下面的代码来访问它:

代码如下:

print(array[1])  --这里会输出array的第一个元素12。
print(array[3]) --这里会输出array的第三个元素World

需要注意的一点就是:Lua的数组的下标是从1开始的。如果你使用下面的语句则会输出nil值:

代码如下:

print(array[0])  --输出nil
print(array["1"])  --输出nil(想想和array[1]的区别:一个是integer作为key,一个是字符串做为key)

通用Table操作方法

之前我们在教程1中介绍了如何传递Table给Lua,以及在教程3中介绍了如何访问Table的数据。因为数组也是Table,所以我们可以用同样的方式来读取数组。

读取数组

假设我们的Lua Table为array = {“Hello”, 1, “World”, 23.2},那么我们可以用下列函数来访问它:

代码如下:

void readLuaArray(lua_State *L)
{
    lua_settop(L,0); //这样确保我们的array是放在当前栈的栈顶。
    lua_getglobal(L, "array");
    //如果前面不调用lua_settop(L,0),那我们必须要使用luaL_len(L,-1)
    int n = luaL_len(L, 1);   //luaL_len可以获得table的元素个数
    for (int i = 1; i <= n; ++i) {
        lua_pushnumber(L, i);  //往栈里面压入i
        lua_gettable(L, -2);  //读取table[i],table位于-2的位置。
        //lua_rawget(L, -2);  //lua_gettable也可以用lua_rawget来替换
        cout<         lua_pop(L, 1);
    }
}

最后输出的结果为:

代码如下:

"Hello", 1, "World", 23.2

修改数组

现在我们如果想要修改这个数组,把每一个数组的元素都变成”hehe[i]”(i = 1-n),我们看看怎么做。

代码如下:

int writeLuaArray(lua_State *L)
{
    lua_settop(L, 0);
    lua_getglobal(L, "array");
    //确保第一个函数一个要是一个table
    luaL_checktype(L, 1, LUA_TTABLE);
    int n = luaL_len(L,1);
    for (int i = 1; i <= n; ++i) {
        lua_pushnumber(L, i);
        char buf[256];
        sprintf(buf, "hehe%d", i);
        lua_pushstring(L, buf);
//        lua_settable(L, -3);
        lua_rawset(L, -3);
    }
    return 0;
}
}

注意这里的lua_rawset和lua_settable是等价的,只不过lua_rawset速度更快。 最后,我们在加载完Lua脚本以后调用这两个函数:

代码如下:

writeLuaArray(L);
readLuaArray(L);

输出结果为:

代码如下:

readLuaArray: hehe1
readLuaArray: hehe2
readLuaArray: hehe3
readLuaArray: hehe4

专门的数组操作方法

因为数组一般在程序语言里面都会被特殊对待,Lua也不例外,它的C API还提供另外一种更方便高效地方法来存取数组的元素。

代码如下:

 void lua_rawgeti (lua_State *L, int index, int key);
 void lua_rawseti (lua_State *L, int index, int key);

这两个函数后面两个参数的意思分别是:index(table在栈中的索引),key(table中数组的索引,下标从1开始) 接下来,我会通过改造上面的示例来演示这两个API的用法。

读取数组

因为lua_rawgeti(L,t,key)等价于:

代码如下:

 lua_pushnumber(L, key);
 lua_rawget(L, t);

因此,我们的读取代码可以改写成下面这样:

代码如下:

void readLuaArray(lua_State *L)
{
    lua_getglobal(L, "array");
    int n = luaL_len(L, -1);
    for (int i = 1; i <= n; ++i) {
        lua_rawgeti(L, 1, i);
        cout<<"readLuaArray: "<         lua_pop(L, 1);
    }
}

修改数组

同理,lua_rawset(L,t,key)等价于

代码如下:

lua_pushnumber(L,key); //此时的栈 table->value->key
lua_insert(L,-2);  //调用完后的栈: table->key->value (table[key]=value)
lua_rawset(L,t);

相应的修改数组的代码可以修改为:

代码如下:

int writeLuaArray(lua_State *L)
{
    lua_settop(L, 0);
    lua_getglobal(L, "array");
    //确保第一个函数一个要是一个table
    luaL_checktype(L, 1, LUA_TTABLE);
    int n = luaL_len(L,1);
    for (int i = 1; i <= n; ++i) {
        char buf[256];
        sprintf(buf, "hehe%d", i);
        lua_pushstring(L, buf);
        lua_rawseti(L, 1, i);
    }
    return 0;
}

C/C++操作Lua字符串

基本字符串操作

Lua C API操作字符串主要包含两个操作:求子串(lua_pushlstring)和字符串拼接(lua_concat). 例如,我们求一个字符串s的子串[i,j],它可以表示为:

代码如下:

lua_pushlstring(L, s + i, j - i + 1);

而lua_concat(L,n)则可以把当前栈顶的n个元素转换成字符串并拼接起来,最后把结果压入栈顶。 比如,我们想定义一个函数mycontact(…,n)可以把n个字符串拼接起来,n表示字符串的个数,那么我们的代码可以写成这样:
代码如下:

static int l_mycontact(lua_State* L){
    luaL_checktype(L, -1, LUA_TNUMBER);
    int n = lua_tonumber(L, -1);
    lua_pop(L, 1);
    lua_concat(L, n);
    return 1;
}

然后,我们需要注册此函数到libs中去,最后在Lua里面调用此函数:

代码如下:

print(mylib.mycontact("zilong","shanren"," meng meng"," da",4))

输出结果为:

代码如下:

zilongshanren meng meng da

格式化输出

当我们想要往Lua里面写入一个格式化字符串时,可以使用函数

代码如下:

const char *lua_pushfstring (lua_State *L, const char *fmt, ...);

另外,我们还可以使用luaL_Buffer,下面是PIL书中的示例,把Lua字符串转换成大写:

代码如下:

 static int str_upper (lua_State *L) {
     size_t l;
     size_t i;
     luaL_Buffer b;
     const char *s = luaL_checklstring(L, 1, &l);  //从Lua栈中取出字符串
     char *p = luaL_buffinitsize(L, &b, l); //分配一块与取出字符串同样大小的缓冲区
     for (i = 0; i        p[i] = toupper(uchar(s[i]));
     luaL_pushresultsize(&b, l);  //把缓冲区结果转换为字符串
     return 1;
}

更多的Lua Buffer操作函数如下:

代码如下:

 void luaL_buffinit   (lua_State *L, luaL_Buffer *B);
 void luaL_addvalue   (luaL_Buffer *B);
 void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l);
 void luaL_addstring  (luaL_Buffer *B, const char *s);
 void luaL_addchar    (luaL_Buffer *B, char c);
 void luaL_pushresult (luaL_Buffer *B);

关于每一个函数的用法和每一个参数的含义,大家可以去Lua的Reference Manual上去查看,本文就不赘述了。

存储Lua状态

在C函数里面,当我们需要保存函数里面的一些状态的时候,我们一般采用全局变量或者静态变量的方式。但是,如果在与Lua交互时,这两种方法都不可取。 原因有二: 1. C变量很难存储各种各样的Lua变量。 2. 当存在多个Lua栈的时候,就不生效了。 在Lua里面有两种方法来存在函数内的non-local数据:registry和upvalue.

Registry方式

Register是一个Lua的全局Table,只有在Lua的C API里面可以访问这个Table。它可以用来存储多个Lua模块之间的数据。 访问Register的方式一般为:

代码如下:

 lua_getfield(L, LUA_REGISTRYINDEX, "Key");

我们需要提供一个LUA_REGISTRYINDEX的“伪索引”来标识它在Lua栈中的位置。我们在操作这个table的时候,最好是使用字符串做为key,而不要使用数字来做为key。关于Registry更为实际的用法,我们会在下一篇文章中讨论。

Upvalue方式

Upvalue主要用来存储模块或者函数内部的一些私有的数据,它与C语言的静态变量有点类似。具体的用法可以参考PIL


推荐阅读
  • 本文介绍了基于c语言的mcs51单片机定时器计数器的应用教程,包括定时器的设置和计数方法,以及中断函数的使用。同时介绍了定时器应用的举例,包括定时器中断函数的编写和频率值的计算方法。主函数中设置了T0模式和T1计数的初值,并开启了T0和T1的中断,最后启动了CPU中断。 ... [详细]
  • C语言注释工具及快捷键,删除C语言注释工具的实现思路
    本文介绍了C语言中注释的两种方式以及注释的作用,提供了删除C语言注释的工具实现思路,并分享了C语言中注释的快捷键操作方法。 ... [详细]
  • c语言\n不换行,c语言printf不换行
    本文目录一览:1、C语言不换行输入2、c语言的 ... [详细]
  • 本文介绍了使用PHP实现断点续传乱序合并文件的方法和源码。由于网络原因,文件需要分割成多个部分发送,因此无法按顺序接收。文章中提供了merge2.php的源码,通过使用shuffle函数打乱文件读取顺序,实现了乱序合并文件的功能。同时,还介绍了filesize、glob、unlink、fopen等相关函数的使用。阅读本文可以了解如何使用PHP实现断点续传乱序合并文件的具体步骤。 ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • 字符常量与变量的定义及使用方法
    本文介绍了字符常量与变量的定义及使用方法,包括字符常量的定义、值和转义字符的表示方法;字符串常量的定义和结束标志;字符型数据与整型数据的区别;字符型变量的定义和内存占用;字符串变量的运算方法。同时提醒注意字符串常量不可赋值给字符型变量,需使用数组或指针进行存取。 ... [详细]
  • 本文介绍了Linux Shell中括号和整数扩展的使用方法,包括命令组、命令替换、初始化数组以及算术表达式和逻辑判断的相关内容。括号中的命令将会在新开的子shell中顺序执行,括号中的变量不能被脚本余下的部分使用。命令替换可以用于将命令的标准输出作为另一个命令的输入。括号中的运算符和表达式符合C语言运算规则,可以用在整数扩展中进行算术计算和逻辑判断。 ... [详细]
  • OO第一单元自白:简单多项式导函数的设计与bug分析
    本文介绍了作者在学习OO的第一次作业中所遇到的问题及其解决方案。作者通过建立Multinomial和Monomial两个类来实现多项式和单项式,并通过append方法将单项式组合为多项式,并在此过程中合并同类项。作者还介绍了单项式和多项式的求导方法,并解释了如何利用正则表达式提取各个单项式并进行求导。同时,作者还对自己在输入合法性判断上的不足进行了bug分析,指出了自己在处理指数情况时出现的问题,并总结了被hack的原因。 ... [详细]
  • 31.项目部署
    目录1一些概念1.1项目部署1.2WSGI1.3uWSGI1.4Nginx2安装环境与迁移项目2.1项目内容2.2项目配置2.2.1DEBUG2.2.2STAT ... [详细]
  • 这篇文章主要介绍了Python拼接字符串的七种方式,包括使用%、format()、join()、f-string等方法。每种方法都有其特点和限制,通过本文的介绍可以帮助读者更好地理解和运用字符串拼接的技巧。 ... [详细]
  • 本文介绍了在Windows系统上使用C语言命令行参数启动程序并传递参数的方法,包括接收参数程序的代码和bat文件的编写方法,同时给出了程序运行的结果。 ... [详细]
  • 树莓派语音控制的配置方法和步骤
    本文介绍了在树莓派上实现语音控制的配置方法和步骤。首先感谢博主Eoman的帮助,文章参考了他的内容。树莓派的配置需要通过sudo raspi-config进行,然后使用Eoman的控制方法,即安装wiringPi库并编写控制引脚的脚本。具体的安装步骤和脚本编写方法在文章中详细介绍。 ... [详细]
  • C语言判断正整数能否被整除的程序
    本文介绍了使用C语言编写的判断正整数能否被整除的程序,包括输入一个三位正整数,判断是否能被3整除且至少包含数字3的方法。同时还介绍了使用qsort函数进行快速排序的算法。 ... [详细]
  • 本文介绍了使用Python解析C语言结构体的方法,包括定义基本类型和结构体类型的字典,并提供了一个示例代码,展示了如何解析C语言结构体。 ... [详细]
  • C语言常量与变量的深入理解及其影响
    本文深入讲解了C语言中常量与变量的概念及其深入实质,强调了对常量和变量的理解对于学习指针等后续内容的重要性。详细介绍了常量的分类和特点,以及变量的定义和分类。同时指出了常量和变量在程序中的作用及其对内存空间的影响,类似于const关键字的只读属性。此外,还提及了常量和变量在实际应用中可能出现的问题,如段错误和野指针。 ... [详细]
author-avatar
宫廷的围脖6uw_1911
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有