Lua教程-1:C/C++操作Lua数组和字符串


原文链接:

https://blog.csdn.net/fyfywg/article/details/77102162

http://blog.csdn.net/cbbbc/article/details/45116203

读后感受:基本介绍了C/C++如何传值给lua,例子也不错,但是最后没有详细讲解lua api提供的三种保存非局部变量的方法,注册表、环境、upvalue,这些都很有用,现在我也不是很了解,等之后弄明白之后,在整理一下例子代码。
原文链接: http://4gamers.cn/blog/2014/08/10/cpp-operate-lua-string/ 原文内容如下:
本文将介绍如何在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);  
    }  
}  

最后输出的结果为:

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

相关参考

《Programming in Lua》第27章

C++怎么传递一个数组到LUA

https://blog.csdn.net/fyfywg/article/details/77102176

数组的操作,参考实例如下:
数组其实是一种特殊的表格。

int LuaArray(lua_State* L)
{
    lua_newtable(L);                    //新建一个表,并压入栈顶
    lua_pushnumber(L,-1);            //这行和下面一行,没搞懂为什么需要这样,但如果不执行,lua中获取的数据就会出错
    lua_rawseti(L,-2,0);
    int array[]={11,22,33,44,55};
    for(int n=0;n<sizeof(array)/sizeof(int);n++)
    {
        lua_pushinteger(L,array[n]);        //将数组的数据入栈
        lua_rawseti(L,-2,n+1);                //将刚刚入栈的数据设置为数组的第n+1个数据,同时这个数据会自动从栈里pop
    }
    return 1;        //push进栈的数据都在设置数组时自动pop了,所以现在栈里只有1个数据,就是最开始push的表
}
--lua获取刚刚传的表格
array=luaArray()
for i,v in ipairs(array)
    do
        print(v)
end

lua堆栈

首先了解下c++与lua之间的通信:
假设在一个lua文件中有如下定义
– hello.lua 文件
myName = “beauty girl”
在这里插入图片描述
请注意红色数字,代表通信顺序:

1) C++想获取Lua的myName字符串的值,所以它把myName放到Lua堆栈(栈顶),以便Lua能看到

2) Lua从堆栈(栈顶)中获取myName,此时栈顶再次变为空

3) Lua拿着这个myName去Lua全局表查找myName对应的字符串

4) 全局表返回一个字符串”beauty girl”

5) Lua把取得的“beauty girl”字符串放到堆栈(栈顶)

6) C++可以从Lua堆栈中取得“beauty girl”

若有9个元素分别入栈,则:

  1. 正数索引,栈底是1,然后一直到栈顶是逐渐+1,最后变成9(9大于1)

  2. 负数索引,栈底是-9,然后一直到栈顶是逐渐+1,最后变成-1(-1大于-9)

索引相关:
3. 正数索引,不需要知道栈的大小,我们就能知道栈底在哪,栈底的索引永远是1

  1. 负数索引,不需要知道栈的大小,我们就能知道栈顶在哪,栈顶的索引永远是-1

Lua 字符串

https://www.runoob.com/lua/lua-strings.html

字符串或串(String)是由数字、字母、下划线组成的一串字符。

Lua 语言中字符串可以使用以下三种方式来表示:

  • 单引号间的一串字符。
  • 双引号间的一串字符。
  • [[ 与 ]] 间的一串字符。

以上三种方式的字符串实例如下:

string1 = "Lua"
print("\"字符串 1 是\"",string1)
string2 = 'runoob.com'
print("字符串 2 是",string2)

string3 = [["Lua 教程"]]
print("字符串 3 是",string3)

以上代码执行输出结果为:

"字符串 1 是"    Lua
字符串 2 是    runoob.com
字符串 3 是    "Lua 教程"

转义字符用于表示不能直接显示的字符,比如后退键,回车键,等。如在字符串转换双引号可以使用 “”"。

所有的转义字符和所对应的意义:
在这里插入图片描述

字符串操作

Lua 提供了很多的方法来支持字符串的操作:

字符串截取

字符串截取使用 sub() 方法。

string.sub() 用于截取字符串,原型为:
string.sub(s, i [, j])

参数说明:

s:要截取的字符串。
i:截取开始位置。
j:截取结束位置,默认为 -1,最后一个字符。

-- 字符串
local sourcestr = "prefix--runoobgoogletaobao--suffix"
print("\n原始字符串", string.format("%q", sourcestr))

-- 截取部分,第4个到第15个
local first_sub = string.sub(sourcestr, 4, 15)
print("\n第一次截取", string.format("%q", first_sub))

-- 取字符串前缀,第1个到第8个
local second_sub = string.sub(sourcestr, 1, 8)
print("\n第二次截取", string.format("%q", second_sub))

-- 截取最后10个
local third_sub = string.sub(sourcestr, -10)
print("\n第三次截取", string.format("%q", third_sub))

-- 索引越界,输出原始字符串
local fourth_sub = string.sub(sourcestr, -100)
print("\n第四次截取", string.format("%q", fourth_sub))

字符串大小写转换

以下实例演示了如何对字符串大小写进行转换:

字符串查找与反转

字符串格式化

Lua 提供了 string.format() 函数来生成具有特定格式的字符串, 函数的第一个参数是格式 , 之后是对应格式中每个代号的各种数据。

由于格式字符串的存在, 使得产生的长字符串可读性大大提高了。这个函数的格式很像 C 语言中的 printf()。

以下实例演示了如何对字符串进行格式化操作:

格式字符串可能包含以下的转义码:

%c - 接受一个数字, 并将其转化为ASCII码表中对应的字符
%d, %i - 接受一个数字并将其转化为有符号的整数格式
%o - 接受一个数字并将其转化为八进制数格式
%u - 接受一个数字并将其转化为无符号整数格式
%x - 接受一个数字并将其转化为十六进制数格式, 使用小写字母
%X - 接受一个数字并将其转化为十六进制数格式, 使用大写字母
%e - 接受一个数字并将其转化为科学记数法格式, 使用小写字母e
%E - 接受一个数字并将其转化为科学记数法格式, 使用大写字母E
%f - 接受一个数字并将其转化为浮点数格式
%g(%G) - 接受一个数字并将其转化为%e(%E, 对应%G)及%f中较短的一种格式
%q - 接受一个字符串并将其转化为可安全被Lua编译器读入的格式
%s - 接受一个字符串并按照给定的参数格式化该字符串
为进一步细化格式, 可以在%号后添加参数. 参数将以如下的顺序读入:

(1) 符号: 一个+号表示其后的数字转义符将让正数显示正号. 默认情况下只有负数显示符号.
(2) 占位符: 一个0, 在后面指定了字串宽度时占位用. 不填时的默认占位符是空格.
(3) 对齐标识: 在指定了字串宽度时, 默认为右对齐, 增加-号可以改为左对齐.
(4) 宽度数值
(5) 小数位数/字串裁切: 在宽度数值后增加的小数部分n, 若后接f(浮点数转义符, 如%6.3f)则设定该浮点数的小数只保留n位, 若后接s(字符串转义符, 如%5.3s)则设定该字符串只显示前n位。

字符与整数相互转换

匹配模式

Lua 中的匹配模式直接用常规的字符串来描述。 它用于模式匹配函数 string.find, string.gmatch, string.gsub, string.match。

你还可以在模式串中使用字符类。

字符类指可以匹配一个特定字符集合内任何字符的模式项。比如,字符类 %d 匹配任意数字。所以你可以使用模式串 %d%d/%d%d/%d%d%d%d 搜索 dd/mm/yyyy 格式的日期:

--实例
s = "Deadline is 30/05/1999, firm"
date = "%d%d/%d%d/%d%d%d%d"
print(string.sub(s, string.find(s, date)))    --> 30/05/1999
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值