JiaHe's Blog

读万卷书,行万里路

数据类型

数据类型描述
nil这个最简单,只有值nil属于该类,表示一个无效值(在条件表达式中相当于false)。
boolean包含两个值:false和true。
number表示双精度类型的实浮点数
string字符串由一对双引号或单引号来表示
function由 C 或 Lua 编写的函数
userdata表示任意存储在变量中的C数据结构
thread表示执行的独立线路,用于执行协同程序
tableLua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字、字符串或表类型。在 Lua 里,table 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表。

变量

Lua 变量有三种类型:全局变量(默认)、局部变量、表中的域。

局部变量的作用域为从声明位置开始到所在语句块结束。

变量的默认值均为 nil。

-- 赋值是改变一个变量的值和改变表域的最基本的方法。
a = "hello" .. "world"
t.n = t.n + 1

-- Lua 可以对多个变量同时赋值,变量列表和值列表的各个元素用逗号分开,赋值语句右边的值会依次赋给左边的变量。
a, b = 10, 2*x <--> a=10; b=2*x

-- 遇到赋值语句Lua会先计算右边所有的值然后再执行赋值操作,所以我们可以这样进行交换变量的值:
x, y = y, x -- swap 'x' for 'y'
a[i], a[j] = a[j], a[i] -- swap 'a[i]' for 'a[j]'

--[[
当变量个数和值的个数不一致时,Lua会一直以变量个数为基础采取以下策略:
a. 变量个数 > 值的个数 按变量个数补足nil
b. 变量个数 < 值的个数 多余的值会被忽略
]]

a, b, c = 0, 1
print(a,b,c) --> 0 1 nil

a, b = a+1, b+1, b+2 -- value of b+2 is ignored
print(a,b) --> 1 2

a, b, c = 0
print(a,b,c) --> 0 nil nil

-- 多值赋值经常用来交换变量,或将函数调用返回给变量:
a, b = f()

--[[
应该尽可能的使用局部变量,有两个好处:

1. 避免命名冲突。
2. 访问局部变量的速度比全局变量更快。
]]

-- 对 table 的索引使用方括号 []。Lua 也提供了 . 操作。
t[i]
t.i -- 当索引为字符串类型时的一种简化写法
gettable_event(t,i) -- 采用索引访问本质上是一个类似这样的函数调用

循环

while

--[[
while循环

while(condition)
do
statements
end
]]

a=10
while( a < 20 )
do
print("a 的值为:", a)
a = a+1
end

for

-- [[
数值for循环

var 从 exp1 变化到 exp2,每次变化以 exp3 为步长递增 var,并执行一次 "执行体"。exp3 是可选的,如果不指定,默认为1。

for var=exp1,exp2,exp3 do
<执行体>
end
]]
--

for i=1,f(x) do
print(i)
end

for i=10,1,-1 do
print(i)
end

-- [[
泛型for循环

泛型 for 循环通过一个迭代器函数来遍历所有值,类似 java 中的 foreach 语句。
]]

-- 打印数组a的所有值
-- i是数组索引值,v是对应索引的数组元素值。ipairs是Lua提供的一个迭代器函数,用来迭代数组。
a = {"one", "two", "three"}
for i, v in ipairs(a) do
print(i, v)
end

repeat...until

--[[
repeat...until循环

当condition为true时,结束statements
repeat
statements
until( condition )
]]

a = 10
repeat
print("a的值为:", a)
a = a + 1
until( a > 15 )

流程控制

--[[
if( 布尔表达式 1) then
--[ 在布尔表达式 1 为 true 时执行该语句块 --]
if(布尔表达式 2) then
--[ 布尔表达式 2 为 true 时执行该语句块 --]
end
elseif( 布尔表达式 2) then
--[ 在布尔表达式 2 为 true 时执行该语句块 --]

elseif( 布尔表达式 3) then
--[ 在布尔表达式 3 为 true 时执行该语句块 --]
else
--[ 如果以上布尔表达式都不为 true 则执行该语句块 --]
end
]]

函数

在Lua中,函数是对语句和表达式进行抽象的主要方法。既可以用来处理一些特殊的工作,也可以用来计算一些值。

Lua 提供了许多的内建函数,你可以很方便的在程序中调用它们,如print()函数可以将传入的参数打印在控制台上。

Lua 函数主要有两种用途:

1.完成指定的任务,这种情况下函数作为调用语句使用;
2.计算并返回值,这种情况下函数作为赋值语句的表达式使用。

--[[
optional_function_scope function function_name( argument1, argument2, argument3..., argumentn)
function_body
return result_params_comma_separated
end

* optional_function_scope: 该参数是可选的制定函数是全局函数还是局部函数,未设置该参数默认为全局函数,如果你需要设置函数为局部函数需要使用关键字 local。
* function_name: 指定函数名称。
* argument1, argument2, argument3..., argumentn: 函数参数,多个参数以逗号隔开,函数也可以不带参数。
* function_body: 函数体,函数中需要执行的代码语句块。
* result_params_comma_separated: 函数返回值,Lua语言函数可以返回多个值,每个值以逗号隔开。
]]

--[[
将函数作为参数传递给函数
]]
myprint = function(param)
print("这是打印函数 - ##",param,"##")
end

function add(num1,num2,functionPrint)
result = num1 + num2
-- 调用传递的函数参数
functionPrint(result)
end
myprint(10)
-- myprint 函数作为参数传递
add(2,5,myprint)

--[[
多返回值

Lua函数可以返回多个结果值,比如string.find,其返回匹配串"开始和结束的下标"(如果不存在匹配串返回nil)。
]]
function maximum (a)
local mi = 1 -- 最大值索引
local m = a[mi] -- 最大值
for i,val in ipairs(a) do
if val > m then
mi = i
m = val
end
end
return m, mi
end

print(maximum({8,10,23,12,5}))

--[[
可变参数

Lua 函数可以接受可变数目的参数,和 C 语言类似,在函数参数列表中使用三点 ... 表示函数有可变的参数。

通常在遍历变长参数的时候只需要使用 {…},然而变长参数可能会包含一些 nil,那么就可以用 select 函数来访问变长参数了:select('#', …) 或者 select(n, …)

* select('#', …) 返回可变参数的长度。
* select(n, …) 用于返回从起点 n 开始到结束位置的所有参数列表。
]]
function add(...)
local s = 0
for i, v in ipairs{...} do --> {...} 表示一个由所有变长参数构成的数组
s = s + v
end
return s
end
print(add(3,4,5,6,7)) --->25

运算符

Lua提供了以下几种运算符类型:

  • 算术运算符

    操作符描述实例
    +加法A + B 输出结果 30
    -减法A - B 输出结果 -10
    *乘法A * B 输出结果 200
    /除法B / A 输出结果 2
    %取余B % A 输出结果 0
    ^乘幂A^2 输出结果 100
    -负号-A 输出结果 -10
    //整除运算符(>=lua5.3)5//2 输出结果 2
  • 关系运算符

    操作符描述实例
    ==等于,检测两个值是否相等,相等返回 true,否则返回 false(A == B) 为 false。
    ~=不等于,检测两个值是否相等,不相等返回 true,否则返回 false(A ~= B) 为 true。
    >大于,如果左边的值大于右边的值,返回 true,否则返回 false(A > B) 为 false。
    <小于,如果左边的值大于右边的值,返回 false,否则返回 true(A < B) 为 true。
    >=大于等于,如果左边的值大于等于右边的值,返回 true,否则返回 false(A >= B) 返回 false。
    <=小于等于, 如果左边的值小于等于右边的值,返回 true,否则返回 false(A <= B) 返回 true。
  • 逻辑运算符

    操作符描述实例
    and逻辑与操作符。 若 A 为 false,则返回 A,否则返回 B。(A and B) 为 false。
    or逻辑或操作符。 若 A 为 true,则返回 A,否则返回 B。(A or B) 为 true。
    not逻辑非操作符。与逻辑运算结果相反,如果条件为 true,逻辑非为 false。not(A and B) 为 true。
  • 其他运算符

    操作符描述实例
    ..连接两个字符串a..b ,其中 a 为 "Hello " , b 为 "World", 输出结果为 "Hello World"。
    #一元运算符,返回字符串或表的长度。#"Hello" 返回 5
  • 运算符优先级

    -- 除了 ^ 和 .. 外所有的二元运算符都是左连接的。
    ^
    not - (unary)
    * / %
    + -
    ..
    < > <= >= ~= ==
    and
    or

字符串

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

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

字符串操作

-- 大小写转换
string.upper(string
string.lower(string)

-- 替换
string.gsub(mainString,findString,replaceString,num)

-- 查找
string.find(string)

-- 反转
string.reverse(string)

-- 格式化,类似java中的String.format
string.format(templateString, params)

-- 整型与字符之间的转换
string.byte("A") --> 65
string.byte("ABCD", 4) --> 68
string.char(65, 66, 67, 68) --> "ABCD"

-- 字符串长度
string.len("123") -- 3

-- 字符串拷贝
string.rep("a", 2) --> "aa"

-- 连接多个字符串
print("123".."456") --> "123456"

--[[
返回一个迭代器函数,每一次调用这个函数,返回一个在字符串 str 找到的下一个符合 pattern 描述的子串。
如果参数 pattern 描述的字符串没有找到,迭代函数返回nil。
]]
for word in string.gmatch("Hello Lua user", "%a+") do print(word) end
--[[
Hello
Lua
user
]]

--[[
string.match()只寻找源字串str中的第一个配对. 参数init可选, 指定搜寻过程的起点, 默认为1。
在成功配对时, 函数将返回配对表达式中的所有捕获结果;
如果没有设置捕获标记, 则返回整个配对字符串. 当没有成功的配对时, 返回nil。
]]
string.match("I have 2 questions for you.", "%d+ %a+") --> 2 questions
string.format("%d, %q", string.match("I have 2 questions for you.", "(%d+) (%a+)")) --> 2, "questions"

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

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

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

string.sub("1234", 2, 3) --> 23
string.sub("1234", 2, 2) --> 2
string.sub("1234", -2) --> 34
string.sub("1234", -10) --> 1234

匹配模式

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

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

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

正则描述
.(点)与任何字符配对
%a与任何字母配对
%c与任何控制符配对(例如 \n)
%d与任何数字配对
%l与任何小写字母配对
%p与任何标点(punctuation)配对
%s与空白字符配对
%u与任何大写字母配对
%w与任何字母/数字配对
%x与任何十六进制数配对
%z与任何代表 0 的字符配对
%x(此处 x 是非字母非数字字符) 与字符 x 配对。主要用来处理表达式中有功能的字符 (^$()%.[]*+-?)的配对问题,例如 %% 与 % 配对
[数个字符类]与任何 [] 中包含的字符类配对,例如 [%w_] 与任何字母 / 数字, 或下划线符号 (_) 配对
[^数个字符类]与任何不包含在[]中的字符类配对. 例如[^%s]与任何非空白字符配对

当上述的字符类用大写书写时, 表示与非此字符类的任何字符配对. 例如, %S表示与任何非空白字符配对.例如,'%A'非字母的字符:

> print(string.gsub("hello, up-down!", "%A", "."))
hello..up.down. 4

数字4不是字符串结果的一部分,他是gsub返回的第二个结果,代表发生替换的次数。

在模式匹配中有一些特殊字符,他们有特殊的意义,Lua中的特殊字符如下:

( ) . % + - * ? [ ^ $

'%' 用作特殊字符的转义字符,因此 '%.' 匹配点;'%%' 匹配字符 '%'。转义字符 '%'不仅可以用来转义特殊字符,还可以用于所有的非字母的字符。

模式条目可以是

  • 单个字符类匹配该类别中任意单个字符;
  • 单个字符类跟一个 ‘*’, 将匹配零或多个该类的字符。 这个条目总是匹配尽可能长的串;
  • 单个字符类跟一个 ‘+’, 将匹配一或更多个该类的字符。 这个条目总是匹配尽可能长的串;
  • 单个字符类跟一个 ‘-’, 将匹配零或更多个该类的字符。 和 ‘*’ 不同, 这个条目总是匹配尽可能短的串;
  • 单个字符类跟一个 ‘?’, 将匹配零或一个该类的字符。 只要有可能,它会匹配一个;
  • %*n*, 这里的 n 可以从 1 到 9; 这个条目匹配一个等于 n 号捕获物(后面有描述)的子串。
  • %b*xy*, 这里的 xy 是两个明确的字符; 这个条目匹配以 x 开始 y 结束, 且其中 xy 保持 平衡 的字符串。 意思是,如果从左到右读这个字符串,对每次读到一个 x+1 ,读到一个 y-1, 最终结束处的那个 y 是第一个记数到 0 的 y。 举个例子,条目 %b() 可以匹配到括号平衡的表达式。
  • %f[*set*], 指边境模式; 这个条目会匹配到一个位于 set 内某个字符之前的一个空串, 且这个位置的前一个字符不属于 set 。 集合 set 的含义如前面所述。 匹配出的那个空串之开始和结束点的计算就看成该处有个字符 ‘\0’ 一样。

模式:

模式 指一个模式条目的序列。 在模式最前面加上符号 '^' 将锚定从字符串的开始处做匹配。 在模式最后面加上符号 '$' 将使匹配过程锚定到字符串的结尾。 如果 '^' 和 '$' 出现在其它位置,它们均没有特殊含义,只表示自身。

捕获:

模式可以在内部用小括号括起一个子模式; 这些子模式被称为 捕获物。 当匹配成功时,由 捕获物 匹配到的字符串中的子串被保存起来用于未来的用途。 捕获物以它们左括号的次序来编号。 例如,对于模式 "(a*(.)%w(%s*))" , 字符串中匹配到 "a*(.)%w(%s*)" 的部分保存在第一个捕获物中 (因此是编号 1 ); 由 "." 匹配到的字符是 2 号捕获物, 匹配到 "%s*" 的那部分是 3 号。

作为一个特例,空的捕获 () 将捕获到当前字符串的位置(它是一个数字)。 例如,如果将模式 "()aa()" 作用到字符串 "flaaap" 上,将产生两个捕获物: 3 和 5 。

数组

Lua 数组的索引键值可以使用整数表示,数组的大小不是固定的。

-- 一维数组: 默认索引以1开始,可以为0和负数指定value
array = {"Lua", "Tutorial"}

-- 多维数组: 即数组中包含数组或一维数组的索引键对应一个数组。
array = {}
for i=1,3 do
array[i] = {}
for j=1,3 do
array[i][j] = i*j
end
end
格式化输出array
{
1 = {
1 = 1
2 = 2
3 = 3
}
2 = {
1 = 2
2 = 4
3 = 6
}
3 = {
1 = 3
2 = 6
3 = 9
}
}

array = {}
maxRows = 3
maxColumns = 3
for row=1,maxRows do
for col=1,maxColumns do
array[row*maxColumns +col] = row*col
end
end
格式化输出array
{
4 = 1
5 = 2
6 = 3
7 = 2
8 = 4
12 = 9
9 = 6
10 = 3
11 = 6
}

table

table作为function的参数时,传递的是对象地址,而不是值。

表的构造

-- 初始化表
mytable = {}

-- 指定值
mytable[1]= "Lua"

-- 移除引用
mytable = nil
-- lua 垃圾回收会释放内存

表的操作

连接

fruits = {"banana","orange","apple"}
-- 返回 table 连接后的字符串
print("连接后的字符串 ",table.concat(fruits))

-- 指定连接字符
print("连接后的字符串 ",table.concat(fruits,", "))

-- 指定索引来连接 table
print("连接后的字符串 ",table.concat(fruits,", ", 2,3))

插入和移除

fruits = {"banana","orange","apple"}

-- 在末尾插入
table.insert(fruits,"mango")
print("索引为 4 的元素为 ",fruits[4])

-- 在索引为 2 的键处插入
table.insert(fruits,2,"grapes")
print("索引为 2 的元素为 ",fruits[2])

print("最后一个元素为 ",fruits[5])
table.remove(fruits)
print("移除后最后一个元素为 ",fruits[5])

排序

fruits = {"banana","orange","apple","grapes"}
print("排序前")
for k,v in ipairs(fruits) do print(k,v) end

table.sort(fruits)
print("排序后")
for k,v in ipairs(fruits) do print(k,v) end

函数小技巧

分割字符串

function split(s, delimiter)
result = {};
for match in (s..delimiter):gmatch("(.-)"..delimiter) do
table.insert(result, match);
end
return result;
end

模块与包

模块类似于一个封装库,从 Lua 5.1 开始,Lua 加入了标准的模块管理机制,可以把一些公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。

Lua 的模块是由变量、函数等已知元素组成的 table,因此创建一个模块很简单,就是创建一个 table,然后把需要导出的常量、函数放入其中,最后返回这个 table 就行。以下为创建自定义模块 module.lua,文件代码格式如下:

-- 文件名为 module.lua
-- 定义一个名为 module 的模块
module = {}

-- 定义一个常量
module.constant = "这是一个常量"

-- 定义一个函数
function module.func1()
io.write("这是一个公有函数!\n")
end

local function func2()
print("这是一个私有函数!")
end

function module.func3()
func2()
end

return module

由上可知,模块的结构就是一个 table 的结构,因此可以像操作调用 table 里的元素那样来操作调用模块里的常量或函数。

上面的 func2 声明为程序块的局部变量,即表示一个私有函数,因此是不能从外部访问模块里的这个私有函数,必须通过模块里的公有函数来调用.

require 函数

Lua提供了一个名为require的函数用来加载模块。要加载一个模块,只需要简单地调用就可以了。例如:

require("<模块名>")
-- OR
require "<模块名>"

执行 require 后会返回一个由模块常量或函数组成的 table,并且还会定义一个包含该 table 的全局变量。

test_module.lua 文件
-- test_module.lua 文件
-- module 模块为上文提到到 module.lua
require("module")

print(module.constant) --> 这是一个常量
module.func3() --> 这是一个私有函数!

或者给加载的模块定义一个别名变量,方便调用:

test_module2.lua 文件
-- test_module2.lua 文件
-- module 模块为上文提到到 module.lua
-- 别名变量 m
local m = require("module")

print(m.constant) --> 这是一个常量
m.func3() --> 这是一个私有函数!

加载机制

对于自定义的模块,模块文件不是放在哪个文件目录都行,函数 require 有它自己的文件路径加载策略,它会尝试从 Lua 文件或 C 程序库中加载模块。

require 用于搜索 Lua 文件的路径是存放在全局变量 package.path 中,当 Lua 启动后,会以环境变量 LUA_PATH 的值来初始这个环境变量。如果没有找到该环境变量,则使用一个编译时定义的默认路径来初始化。

当然,如果没有 LUA_PATH 这个环境变量,也可以自定义设置,在当前用户根目录下打开 .profile 文件(没有则创建,打开 .bashrc 文件也可以),例如把 "~/lua/" 路径加入 LUA_PATH 环境变量里:

#LUA_PATH
export LUA_PATH="~/lua/?.lua;;"

文件路径以 ";" 号分隔,最后的 2 个 ";;" 表示新加的路径后面加上原来的默认路径。

接着,更新环境变量参数,使之立即生效。

source ~/.profile

这时假设 package.path 的值是:

/Users/dengjoe/lua/?.lua;./?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;/usr/local/lib/lua/5.1/?.lua;/usr/local/lib/lua/5.1/?/init.lua

那么调用 require("module") 时就会尝试打开以下文件目录去搜索目标。

/Users/dengjoe/lua/module.lua;
./module.lua
/usr/local/share/lua/5.1/module.lua
/usr/local/share/lua/5.1/module/init.lua
/usr/local/lib/lua/5.1/module.lua
/usr/local/lib/lua/5.1/module/init.lua

如果找过目标文件,则会调用 package.loadfile 来加载模块。否则,就会去找 C 程序库。

搜索的文件路径是从全局变量 package.cpath 获取,而这个变量则是通过环境变量 LUA_CPATH 来初始。

搜索的策略跟上面的一样,只不过现在换成搜索的是 so 或 dll 类型的文件。如果找得到,那么 require 就会通过 package.loadlib 来加载它。

C 包

Lua和C是很容易结合的,使用 C 为 Lua 写包。

与Lua中写包不同,C包在使用以前必须首先加载并连接,在大多数系统中最容易的实现方式是通过动态连接库机制。

Lua在一个叫loadlib的函数内提供了所有的动态连接的功能。这个函数有两个参数:库的绝对路径和初始化函数。所以典型的调用的例子如下:

local path = "/usr/local/lua/lib/libluasocket.so"
local f = loadlib(path, "luaopen_socket")

loadlib 函数加载指定的库并且连接到 Lua,然而它并不打开库(也就是说没有调用初始化函数),反之他返回初始化函数作为 Lua 的一个函数,这样我们就可以直接在Lua中调用他。

如果加载动态库或者查找初始化函数时出错,loadlib 将返回 nil 和错误信息。我们可以修改前面一段代码,使其检测错误然后调用初始化函数:

local path = "/usr/local/lua/lib/libluasocket.so"
-- 或者 path = "C:\\windows\\luasocket.dll",这是 Window 平台下
local f = assert(loadlib(path, "luaopen_socket"))
f() -- 真正打开库

一般情况下我们期望二进制的发布库包含一个与前面代码段相似的 stub 文件,安装二进制库的时候可以随便放在某个目录,只需要修改 stub 文件对应二进制库的实际路径即可。

将 stub 文件所在的目录加入到 LUA_PATH,这样设定后就可以使用 require 函数加载 C 库了。

I/O

Lua I/O 库用于读取和处理文件。分为简单模式(和C一样)、完全模式。

  • 简单模式(simple model)拥有一个当前输入文件和一个当前输出文件,并且提供针对这些文件相关的操作。
  • 完全模式(complete model) 使用外部的文件句柄来实现。它以一种面对对象的形式,将所有的文件操作定义为文件句柄的方法

简单模式在做一些简单的文件操作时较为合适。但是在进行一些高级的文件操作的时候,简单模式就显得力不从心。例如同时读取多个文件这样的操作,使用完全模式则较为合适。

打开文件操作语句如下:

file = io.open (filename [, mode])

mode 的值有:

模式描述
r以只读方式打开文件,该文件必须存在。
w打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
a以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留)
r+以可读写方式打开文件,该文件必须存在。
w+打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
a+与a类似,但此文件可读可写
b二进制模式,如果文件是二进制文件,可以加上b
+号表示对文件既可以读也可以写

简单模式

简单模式使用标准的 I/O 或使用一个当前输入文件和一个当前输出文件。

以下为 file.lua 文件代码,操作的文件为test.lua(如果没有你需要创建该文件),代码如下:

-- 以只读方式打开文件*
file = io.open("test.lua", "r")

-- 设置默认输入文件为 test.lua*
io.input(file)

-- 输出文件第一行*
print(io.read())

-- 关闭打开的文件*
io.close(file)

-- 以附加的方式打开只写文件*
file = io.open("test.lua", "a")

-- 设置默认输出文件为 test.lua*
io.output(file)

-- 在文件最后一行添加 Lua 注释*
io.write("-- test.lua 文件末尾注释")

-- 关闭打开的文件*
io.close(file)

执行以上代码,你会发现,输出了 test.lua 文件的第一行信息,并在该文件最后一行添加了 lua 的注释。如我这边输出的是:

-- test.lua 文件

在以上实例中我们使用了 io."x" 方法,其中 io.read() 中我们没有带参数,参数可以是下表中的一个:

模式描述
"*n"读取一个数字并返回它。例:file.read("*n")
"*a"从当前位置读取整个文件。例:file.read("*a")
"*l"(默认)读取下一行,在文件尾 (EOF) 处返回 nil。例:file.read("*l")
number返回一个指定字符个数的字符串,或在 EOF 时返回 nil。例:file.read(5)

其他的 io 方法有:

  • **io.tmpfile():**返回一个临时文件句柄,该文件以更新模式打开,程序结束时自动删除
  • io.type(file): 检测obj是否一个可用的文件句柄
  • io.flush(): 向文件写入缓冲中的所有数据
  • io.lines(optional file name): 返回一个迭代函数,每次调用将获得文件中的一行内容,当到文件尾时,将返回nil,但不关闭文件

完全模式

通常我们需要在同一时间处理多个文件。我们需要使用 file:function_name 来代替 io.function_name 方法。以下实例演示了如何同时处理同一个文件:

-- 以只读方式打开文件*
file = io.open("test.lua", "r")

-- 输出文件第一行*
print(file:read())

-- 关闭打开的文件*
file:close()

-- 以附加的方式打开只写文件*
file = io.open("test.lua", "a")

-- 在文件最后一行添加 Lua 注释*
file:write("--test")

-- 关闭打开的文件*
file:close()

执行以上代码,你会发现,输出了 test.lua 文件的第一行信息,并在该文件最后一行添加了 lua 的注释。如我这边输出的是:

-- test.lua 文件

read 的参数与简单模式一致。

其他方法:

  • file:seek(optional whence, optional offset): 设置和获取当前文件位置,成功则返回最终的文件位置(按字节),失败则返回nil加错误信息。参数 whence 值可以是:

    • "set": 从文件头开始
    • "cur": 从当前位置开始[默认]
    • "end": 从文件尾开始
    • offset:默认为0

    不带参数file:seek()则返回当前位置,file:seek("set")则定位到文件头,file:seek("end")则定位到文件尾并返回文件大小

  • file:flush(): 向文件写入缓冲中的所有数据

  • io.lines(optional file name): 打开指定的文件filename为读模式并返回一个迭代函数,每次调用将获得文件中的一行内容,当到文件尾时,将返回nil,并自动关闭文件。
    若不带参数时io.lines() <=> io.input():lines(); 读取默认输入设备的内容,但结束时不关闭文件,如:

    for line in io.lines("main.lua") do
      print(line)
    end

以下实例使用了 seek 方法,定位到文件倒数第 25 个位置并使用 read 方法的 *a 参数,即从当前位置(倒数第 25 个位置)读取整个文件。

-- 以只读方式打开文件*
file = io.open("test.lua", "r")

file:seek("end",-25)
print(file:read("*a"))

-- 关闭打开的文件*
file:close()

我这边输出的结果是:

st.lua 文件末尾--test