Lua教程(五):C/C++操作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_tostring(L, -1)<<endl;
        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_tostring(L, -1)<<endl;
        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 < l; 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

(0)

相关推荐

  • C/C++中获取数组长度的方法示例

    学过C/C++的人都知道,在C/C++中并没有提供直接获取数组长度的函数,对于存放字符串的字符数组提供了一个strlen函数获取其长度,那么对于其他类型的数组如何获取他们的长度呢? 其中一种方法是使用sizeof(array) / sizeof(array[0]), 在C语言中习惯上在使用时都把它定义成一个宏,比如: #define GET_ARRAY_LEN(array,len) {len = (sizeof(array) / sizeof(array[0]));} 而在C++中则可以使用模板

  • 图文详解c/c++中的多级指针与多维数组

    前言 首先先声明一些常识,如果你对这些常识还不理解,请先去弥补一下基础知识: 1.实际上并不存在多维数组,所谓的多维数组本质上是用一维数组模拟的. 2.数组名是一个常量(意味着不允许对其进行赋值操作),其代表数组首元素的首地址. 3.数组与指针的关系是因为数组下标操作符[],比如,int a[3][2]相当于*(*(a+3)+2) . 4.指针是一种变量,也具有类型,其占用内存空间大小和系统有关,一般32位系统下,sizeof(指针变量)=4. 5.指针可以进行加减算术运算,加减的基本单位是si

  • C/C++ 数组和指针及引用的区别

    C/C++ 数组和指针及引用的区别 1.数组和指针的区别 (1)定义 数组是一个符号,不是变量,因而没有自己对应的存储空间.但是,指针是一个变量,里面存储的内容是另外一个变量的地址,因为是变量所以指针有自己的内存空间,只不过里面存储的内容比较特殊. (2)区别 a.对于声明和定义,指针和数组是不相同的,定义为数组,则声明也应该是数组,不可混淆 b.当作下标操作符时,指针和数组是等价的.a[i]会被编译器翻译成*(a+i). c.当数组声明被用作函数形参的时候,数组实际会被当作指针来使用. (3)

  • C/C++ 动态数组的创建的实例详解

    C/C++ 动态数组的创建的实例详解 在C++语言中,二维动态数组主要使用指针的方法建立,以建立一个整数二维数组为例: #include<iostream> #include<string> #include<malloc.h> using namespace std; int main(int argc,char **argv) { ///*int a[2][3]={{1,2,3},{4,5,6}}; //cout<<sizeof(a+1)<<

  • 浅析C/C++,Java,PHP,JavaScript,Json数组、对象赋值时最后一个元素后面是否可以带逗号

    1 C,C++,Java,PHP都能容忍末尾的逗号 C,C++,Java中对数组赋值时,最后一个元素末尾的逗号可有可无.下面两行代码对这些语言来说是等效的. int a[] = {1,2,3}; /* 正确 */ int a[] = {1,2,3,}; /* 正确 */ PHP这一点也继承了C的特点,下面的两行代码等效. $a = array(1,2,3); /* 正确 */ $a = array(1,2,3,); /* 正确 */ 2 JavaScript视末尾逗号为语法错误! 然而到了Jav

  • js数组常见操作及数组与字符串相互转化实例详解

    本文实例讲述了js数组常见操作及数组与字符串相互转化方法.分享给大家供大家参考,具体如下: 数组与字符串的相互转化 <script type="text/javascript"> var obj="new1abcdefg".replace(/(.)(?=[^$])/g,"$1,").split(","); //字符串转化为数组 var obj2 = "new2abcdefg".split(&qu

  • Lua教程(五):C/C++操作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"},它是一个数组,可以用下

  • Jquery操作js数组及对象示例代码

    贴一段jQuery对js对象及数组的操作:增删改查的代码. 复制代码 代码如下: var WorkList = new Array();//数组对象 //下面是自己定义的实体 function WorkEx(depart, title, begintime, endtime) {     this.SId = 0;     this.Id = -(WorkList.length+1);     this.DepartmentName = depart;     this.Title = titl

  • Lua教程(三):C语言、C++中调用Lua的Table示例

    从写上一篇Lua的文章到现在,已经过去半月有余了,是时候让自己的Lua状态refresh一下了.本教程将介绍Lua的栈及基本栈操作,同时还有如何在C/C++代码里面读取Lua的Table. 理解Lua栈 Lua通过一个"虚拟栈"与C/C++程序进行数据交互,所有的Lua C API都是通过操作这个栈来完成相应的数据通信. Lua的这个"虚拟栈"解决了C/C++程序与Lua程序通信的两大问题: 1.Lua使用垃圾回收,而C/C++需要手动管理内存. 2.Lua使用动态

  • Lua教程(四):函数详解

    一.函数: 在Lua中函数的调用方式和C语言基本相同,如:print("Hello World")和a = add(x, y).唯一的差别是,如果函数只有一个参数,并且该参数的类型为字符串常量或table的构造器,那么圆括号可以省略,如print "Hello World"和f {x = 20, y = 20}.     Lua为面对对象式的调用也提供了一种特殊的语法--冒号操作符.表达式o.foo(o,x)的另一种写法是o:foo(x).冒号操作符使调用o.foo

  • Lua教程(四):在Lua中调用C语言、C++的函数

    本教程将介绍如何在Lua里面调用c/c++函数. 在Lua里面调用c/c++函数其实是比较简单,本文将通过两个示例演示具体的做法:一个是求平均数,另一个是打印lua函数的一些参数信息. 最后,本文会介绍如何把这两个函数定义成一个模块,这样lua代码里面就可以不再使用全局的名字空间了. 前言 当我们需要在Lua里面调用c/c++函数时,所有的函数都必须满足以下函数签名: 复制代码 代码如下: typedef int (*lua_CFunction) (lua_State *L); 换句话说,所有的

  • Lua教程(二):基础知识、类型与值介绍

    一.基础知识: 1. 第一个程序和函数:     在目前这个学习阶段,运行Lua程序最好的方式就是通过Lua自带的解释器程序,如:   复制代码 代码如下: /> lua     > print("Hello World")     Hello World 这样我们就可以以交互性的方式输入lua代码,并立即得到执行结果了.对于代码块较少的测试程序来说,这种方式确实是非常方便的,然而对于相对复杂的程序而言,这种方式就不是很合适了.如果是这样,我们可以将Lua代码保存到一个独立

  • Lua教程(二十一):编写C函数的技巧

    1. 数组操作: 在Lua中,"数组"只是table的一个别名,是指以一种特殊的方法来使用table.出于性能原因,Lua的C API为数组操作提供了专门的函数,如:   复制代码 代码如下: void lua_rawgeti(lua_State* L, int index, int key);     void lua_rawseti(lua_State* L, int index, int key); 以上两个函数分别用于读取和设置数组中的元素值.其中index参数表示待操作的ta

  • Lua教程(二):C++和Lua相互传递数据示例

    这是我的Lua系列教程的第二篇,本篇文章主要介绍C++和Lua相互传递数据.如果你还不知道怎么在c/c++里面调用Lua脚本的话,请参考这篇文章. 本文主要介绍基本数据类型的传递,比如整形(int),字符串(string).数字(number)及bool值. 加载并运行Lua脚本 由于在上一个教程里面已经介绍过如何在C/C++里面嵌入Lua,所以这一节就简单的介绍一下程序怎么用,配置就略过啦. 创建Lua虚拟机 复制代码 代码如下: lua_State *lua_state = luaL_new

  • Lua教程(九):元表与元方法详解

    Lua中提供的元表是用于帮助Lua数据变量完成某些非预定义功能的个性化行为,如两个table的相加.假设a和b都是table,通过元表可以定义如何计算表达式a+b.当Lua试图将两个table相加时,它会先检查两者之一是否有元表,然后检查该元表中是否存在__add字段,如果有,就调用该字段对应的值.这个值就是所谓的"元方法",这个函数用于计算table的和. Lua中每个值都有一个元表.table和userdata可以有各自独立的元表,而其它数据类型的值则共享其类型所属的单一元表.缺省

随机推荐