1.lua 语言如何 读取文本的码行每一行数字值,把每行得到的码行数字赋予给x1,x2,x3,x4,x5等?
2.Lua5.4 源码剖析——杂谈 之 如何调试Lua源码
3.求Lua 实现单词计数的代码
4.Lua 5.4 String
5.Lua字节码文件结构及加载过程
lua 语言如何 读取文本的每一行数字值,把每行得到的码行数字赋予给x1,x2,x3,x4,x5等?
local file=io.open("sth.txt")
local line_no=1
for line in file:lines() do
_G['x'..line_no]=tonumber(line)
end
Lua5.4 源码剖析——杂谈 之 如何调试Lua源码
我们有时候写了一段Lua代码,希望能通过断点调试的码行方式看一下我们的代码在执行过程中Lua虚拟机的状态与运行流程。本篇教程我将教大家Windows与Mac环境下如何配置Lua源码调试环境。码行
Lua调试环境需要有Lua源码,码行燕窝假溯源码图片我们从官网下载源码:
Windows下Lua源码调试环境搭建
1)下载Visual Studio,码行可自行在官网下载最新版本即可:
2)打开VIsual Studio,码行创建一个新的码行C++控制台工程,我这里以Visual Studio 版本进行举例:
项目可任意命名,码行本例中我们命名为TestLua:
3)工程中添加Lua源码文件:
3.1)拷贝源代码文件到项目的码行文件夹,Makefile文件可以不拷贝:
3.2)把上面这些文件导入工程:
"
.h
头文件导入:导入所有".h"后缀文件到头文件文件夹中(右键头文件->添加->现有项):
"
.c
源文件导入:导入所有".c"后缀文件到源文件的码行文件夹(右键源文件->添加->现有项):
4)生成exe可执行文件:
文件都导入完成了,这时候如果按"生成"或者"F5",码行会有如下的码行报错:
这是因为除了我们创建项目工程的时候自带源文件中的一个main函数以外,Lua源码中也定义了两个Main函数。码行他们分别对应的是luac编译工具的启动函数和lua运行工具的启动函数。要想编译通过,我们只需要根据自己要调试目的,从3个main里面把用不到的2个main删掉或者重命名即可。
本例中,我打算在自己的main里面实现通过dofile函数执行一个Lua文件的功能,所以我不需要启动lua和luac指令控制台,所以我把他们的ectouch砍价源码下载main函数改名:
luac.c:把main函数改名为luac_main函数:
lua.c:把main函数改为lua_main:
上述源码中多余的2个main函数都改名了,这时候已经能编译通过并生成出exe可执行文件了。
接下来我们可以开始编写自己的main函数逻辑了,打开TestLua.cpp,输入以下内容,作用是运行一个在项目目录下名字为"testlua.lua"的lua文件:
5)testlua.lua文件创建与编写:
上述代码在运行时会执行testlua.lua文件,接下来我们就需要在工程目录下创建这个将要被执行的testlua.lua文件:
打开testlua.lua文件,添加任意lua代码,这里我们简单调用print打印一句信息:
6)在Visual Studio中按“F5”开启调试,可以看到控制台被成功运行,我们的lua文件也被成功执行,打印出了信息:
7)断点调试指令OpCode:
学习过我的《Lua源码剖析 之 虚拟机》系列教程的同学应该知道Lua的指令就是各种OpCode的执行,我们可以在《lvm.c》的下面这个地方加断点再按F5重新启动程序,程序在每执行一条OpCode指令就会在这处代码断点下来,这时候我们就能看到下一条要执行的OpCode是哪一条了:
在本例中的print函数最终会执行到OP_CALL这个调用分支:
Windows环境下搭建Lua源码调试环境的教程到此结束。
Mac下Lua源码调试环境搭建
因为大部分流程与上面Windows一样,所以我下面会省略一些重复步骤。
1)下载XCode,可自行在AppStore进行下载。
2)打开XCode,创建一个新的C++控制台工程,本例中命名为TestLua:
3)工程中添加Lua源码文件:
3.1)拷贝源代码文件到项目的文件夹,Makefile文件可以不拷贝:
3.2)把拷贝后的搜索小红鸭源码文件导入工程:
不需要区分".h"和".cpp",全选导进来就好了:
4)与Windows流程同样,把源码自带的2个main函数改名:
luac.c:把main函数改名为luac_main函数:
lua.c:把main函数改为lua_main:
把源码中多余的2个main函数都改名了,接下来同样,开始编写我们的main.cpp,打开该文件并添加代码如下代码。为了在mac下文件读取代码更简洁,在下面的Lua文件我暂时先使用文件的绝对路径,暂时把testlua.lua文件放在我的mac的桌面上进行读取:
5)在mac的桌面上创建testlua.lua文件,添加任意lua代码:
6)同理可正常运行或者加断点进行调试,这里不再赘述:
总结
本文我们学习了如何在Windows与Mac下搭建Lua源码调试环境。另外,我们上述使用的例子是通过dofile运行一个lua文件,同学们也可以试试保留lua.c里面的main函数,删掉另外两个,此时按开始调试可启动lua的即时解析控制台,在控制台里面可自行输入任意Lua代码,并可断点查看即时运行状态或最终结果,感兴趣的同学可以自行试试。
不过,尽管能调试Lua源码,但如果之前没有学习过我的那些Lua源码剖析教程,可能很多地方会看不懂,源码正常 framework崩溃所以这里建议有空的同学还是可以先去学习一下的。
谢谢阅读。
求Lua 实现单词计数的代码
给你个全功能的wc (linux命令wc 即word count)
local BUFSIZE = 2^ -- 8K
local f = io.input(arg[1]) -- open input file
local cc, lc, wc = 0, 0, 0 -- char, line, and word counts
for lines, rest in io.lines(arg[1], BUFSIZE, "*L") do
if rest then lines = lines .. rest end
cc = cc + #lines
-- count words in the chunk
local _, t = string.gsub(lines, "%S+", "")
wc = wc + t
-- count newlines in the chunk
_,t = string.gsub(lines, "\n", "\n")
lc = lc + t
end
print(lc, wc, cc) --打印行数,单词数,字符数
示例来自 《Programming in Lua third edition》
Lua 5.4 String
在Lua 5.4中,字符串的基本实现是将字符串存储在contents变量中。由于Lua使用C语言编写,因此字符串末尾会添加一个“\0”,导致字符串的总大小增加。
正如之前Lua基础讲解中提到的,Lua将字符串分为short string和long string。其宏定义如下:
这意味着小于等于字节的是短字符串,而大于字节的是长字符串。在创建方式和存储上,它们存在差异。
下面通过代码来讲解字符串的创建过程:
在创建字符串时,会调用luaS_new()函数,该函数主要完成以下两项任务:
接下来,我们将介绍全局的stringcache。在global_State结构中,倒数第四个就是strcache,它是sbattle战斗牛源码一个二维数组,其大小由STRCACHE_N和STRCACHE_M决定。STRCACHE_N是数组的行数,STRCACHE_M是列数。
接下来,我们将看看luaS_newlstr()函数的功能:
然后,我们再来探讨internshrstr()函数的作用:
在创建过程中,我们再次使用global_State结构中定义的另一个stringtable,在第7行stringtable strt;。这个哈希表用于保存当前所有创建的短字符串,并以开散列的方式进行管理。
创建过程如下:
为什么第一步只是将已有元素和桶数量进行比较?因为开散列有一个负载因子的概念,即已存放元素数量与桶数量之比。如果大于1,说明比较拥挤,在没有hash冲突的情况下,一个桶一个元素,但如果有hash冲突,一个桶可能对应多个元素,这会导致链表查询速度变慢,因此需要避免。控制负载因子的大小应小于1。
第二步,扩容操作实际上调用的是luaS_resize函数。luaS_resize是实际改变stringtable桶数量的函数。它目前只会被两个地方调用到:
当newsize > oldsize时,顺序是先进行扩容,然后进行rehash。扩容会调用realloc函数,而rehash的代码非常简洁,只需遍历每个桶,将每个桶中的元素重新哈希到正确的桶中。
当newsize < oldsize时,顺序是相反的,需要先根据newsize进行rehash,然后在确保所有元素已收缩到newsize个桶中后,才能进行shrink操作,这里也会调用realloc函数。
在了解了短string的生成后,我们来探讨长string的生成——luaS_createlngstrobj()。
最后,我们来看看长短字符串的hash计算,这两个在实现上存在差异:
这意味着:短字符串的所有字符都会用于计算hash,step = 1。然而,长字符串的hash计算并非如此。以长度为的字符串为例,其step为step = 2+1 = 3。
Lua字节码文件结构及加载过程
在探索Lua的世界中,字节码文件结构与加载过程是程序运行效率的关键。本文将为你揭示Lua 5.4.3的内部奥秘,从文件头到函数块,逐一剖析其构造与加载流程。让我们一起深入理解Lua的加载逻辑,通过luaU_undump函数,将源代码编织成二进制的指令织锦。
首先,Lua字节码文件由文件头和函数块两大部分组成,如同编织的经纬线,共同构建起程序的基石。文件头包含了Lua的签名("\x1bLua")、版本号(例如Lua 5.4.3的)、官方格式号(0)以及LUAC_DATA的校验信息。紧接着是指令、整型和浮点型大小的指示,每个部分都承载着特定的含义,确保文件的正确性。
深入解析luaU_undump函数,它是Lua加载阶段的灵魂,处理着二进制文件的字节码。这个函数首先会进行头检查,包括Lua签名、版本号、格式号等,随后是lua_Integer和lua_Number的长度指示。例如,Lua 5.4.3的头字节包含整型、浮点型校验和文件头信息,通过反编译,我们可以看到函数原型的细节,如函数名、参数、行数和指令数量等。
在Lua 5.4.3的loadFunction函数中,我们看到函数块被精细划分,包括源代码、行号、参数、可变参数、栈大小、字节码、常量、上值和闭包等元素。这些元素通过loadStringN、loadUnsigned等函数逐一加载,确保每个部分都按照特定规则组织。
文件结构的关键部分包括loadConstants(如main函数中的常量"a")、loadUpvalues(如全局表"_ENV"的加载)、loadProtos(函数内部原型的加载),以及loadDebug信息,如行号和变量名称。比如,main函数的4个指令和a函数的5个,以及upvalues数据" 5f 4e",都在这个过程中得到解析。
在Lua的实现中,lauxlib.c、lapi.c、ldo.c和lundump.c等核心文件,以及lua_load、f_parser、luaU_undump、checkHeader和loadFunction等核心函数,共同构建起字节码的编译和加载流程。从源代码到二进制,再到虚拟机的执行,每一个环节都经过精心设计,以达到高效的运行效率。
总的来说,Lua字节码文件结构的复杂性映射出其内在的执行效率。理解这些细节,不仅有助于我们编写更优化的脚本,也让我们对Lua的底层机制有了更深的认识。让我们继续探索luaU_undump的更多奥秘,揭开Lua字节码加载过程的神秘面纱。