lua语言学习总结

lua基础数据类型

nil

一个变量在第一次赋值前的默认值是 nil, 将nil 赋予给一个全局变量就等同于删除它。

boolean

布尔类型, 可选值 true/false; Lua 中 nil 和 false 为“假”, 其它所有值均为“真”。比如 0 和空 字符串就是“真”;

local a = true

if a then
    print("a") -->output:a
else
    print("not a") --这个没有执行
end

number

Number 类型用于表示实数。可以使用数学函数 math.floor( 向下取整) 和 math.ceil( 向上取整) 进行取整操作。

local order = 3.99
local score = 98.01

print(math.floor(order)) -->output:3
print(math.ceil(score)) -->output:99

string

Lua 中有三种方式表示字符串:

  1. 使用一对单引号 'hello'
  2. 使用一对双引号"hello"
  3. 字符串还可以用一种长括号( 即[[ ]]) 括起来的方式定义。

Lua 的字符串是不可改变的值,也不能通过下标来访问字符串的某个字符

local str1 = 'hello world'
local str2 = "hello lua"
local str3 = [["add\name",'hello']]
local str4 = [=[string have a [[]].]=]

print(str1) -->output:hello world
print(str2) -->output:hello lua
print(str3) -->output:"add\name",'hello'
print(str4) -->output:string have a [[]].

table

Table 类型实现了一种抽象的“关联数组”。 “关联数组”是一种具有特殊索引方式的数组, 索引通常是字符串( string) 或者 number 类型, 但也可以是除 nil 以外的任意类型的值。

local corp = {
	web = "www.google.com", --索引为字符串, key = "web",
	                        -- value = "www.google.com"
	telephone = "12345678", --索引为字符串
	staff = {"Jack", "Scott", "Gary"}, --索引为字符串, 值也是一个表
	100876,                            --相当于 [1] = 100876, 此时索引为数字
	                                   -- key = 1, value = 100876
	100191,                            --相当于 [2] = 100191, 此时索引为数字
	[10] = 360,                        --直接把数字索引给出
	["city"] = "Beijing"               --索引为字符串
} 

print(corp.web)                -->output:www.google.com
print(corp["telephone"])       -->output:12345678
print(corp[2])                 -->output:100191
print(corp["city"])            -->output:"Beijing"
print(corp.staff[1])           -->output:Jack
print(corp[10])                -->output:360

function

在 Lua 中, 函数 也是一种数据类型, 函数可以存储在变量中, 可以通过参数传递给其他函数, 还可以作为其他函数的返回值。

local function foo()
	print("in the function")
	--dosomething()
	local x = 10
	local y = 20
	return x + y
end

local a = foo --把函数赋给变量
print(a())

--output:
in the function
30

表达式

逻辑运算符

逻辑运算符说明
and逻辑与
or逻辑或
not逻辑非

字符串连接

使用操作符“..”( 两个点)连接两个字符串,也可以使用 string 库函数 string.format 连接字符串。

print("Hello " .. "World")    -->打印 Hello World
print(0 .. 1)                 -->打印 01

str1 = string.format("%s-%s","hello","world")
print(str1)                   -->打印 hello-world

str2 = string.format("%d-%s-%.2f",123,"world",1.21)
print(str2)                   -->打印 123-world-1.21

使用table 和 table.concat() 来进行很多字符串的拼接:

local pieces = {}
for i, elem in ipairs(my_list) do
	pieces[i] = my_process(elem)
end
local res = table.concat(pieces)

控制结构

if-else

单个if分支

x = 10
if x > 0 then
	print("x is a positive number")
end

两个分支if-else

x = 10
if x > 0 then
	print("x is a positive number")
else
	print("x is a non-positive number")
end

多个分支if-elseif-else

score = 90
if score == 100 then
	print("Very good!Your score is 100")
elseif score >= 60 then
	print("Congratulations, you have passed it,your score greater or equal to 60")
--此处可以添加多个elseif
else
	print("Sorry, you do not pass the exam! ")
end

while

while 表达式 do
--body
end

Lua 并没有像许多其他语言那样提供类似 continue 这样的控制语句用来立即进入下一个循环迭代( 如果有的话) 。 因此, 我们需要仔细地安排循环体里的分支, 以避免这样的需求。 lua提供了break语句,可以跳出当前循环。

repeat

repeat 控制结构类似于其他语言中的 do-while,执行 repeat 循环体后, 直到 until 的条件为真时才结束

x = 10
repeat
    print(x)
until false

for

for数字型

for var = begin, finish, step do
--body
end

step参数是可选的,默认为1

for i = 1, 5 do
    print(i)
end

for泛型

泛型 for 循环通过一个迭代器( iterator) 函数来遍历所有值:

-- 打印数组a的所有值
local a = {"a", "b", "c", "d"}
for i, v in ipairs(a) do
	print("index:", i, " value:", v)
end

-- output:
index: 1 value: a
index: 2 value: b
index: 3 value: c
index: 4 value: d

ipairs()是用于遍历数组的迭代器函数。在每次循环中, i 会被赋予一个索引值, 同时 v 被赋予一个对应于该索引的数组元素值。 pairs()用于遍历

break,return关键字

break

语句 break 用来终止 while 、 repeat 和 for 三种循环的执行, 并跳出当前循环体, 继续执行当前循环之后的语句

return

return 主要用于从函数中返回结果, 或者用于简单的结束一个函数的执行 有时候, 为了调试方便, 我们可以想在某个函数的中间提前 return , 以进行控制流的短路。此时我们可以将 return 放在一个 do ... end 代码块中

local function foo()
	print("before")
	do return end
	print("after") -- 这一行语句永远不会执行到
end

函数的参数

按值传递

Lua 函数的参数大部分是按值传递的。在调用函数的时候,若形参个数和实参个数不同时, Lua会自动调整实参个数。 调整规则:

  • 若实参个数大于形参个数, 从左向右, 多余的实参被忽略;
  • 若实参个数小于形参个数, 从左向右, 没有被实参初始化的形参会被初始化为 nil。

变长参数

若形参为 ... , 表示该函数可以接收不同长度的参数。 访问参数的时候也要使用 ...

local function func( ... )                  -- 形参为 ... ,表示函数采用变长参数
	local temp = {...}                      -- 访问的时候也要使用 ...
	local ans = table.concat(temp, " ")     -- 使用 table.concat 库函数对数
											-- 组内容使用 " " 拼接成字符串。
	print(ans)
end

func(1, 2)            -- 传递了两个参数
func(1, 2, 3, 4)      -- 传递了四个参数

-->output
1 2
1 2 3 4

具名参数

Lua 还支持通过名称来指定实参,这时候要把所有的实参组织到一个 table 中, 并将这个table 作为唯一的实参传给函数

local function change(arg) -- change 函数, 改变长方形的长和宽, 使其各增长一倍
	arg.width = arg.width * 2
	arg.height = arg.height * 2
	return arg
end

local rectangle = { width = 20, height = 15 }
print("before change:", "width =", rectangle.width,
						"height =", rectangle.height)
rectangle = change(rectangle)
print("after change:", "width =", rectangle.width,
					   "height =", rectangle.height)
					   
-->output
before change: width = 20 height = 15
after change: width = 40 height = 30

按引用传递

当函数参数是 table 类型时, 传递进来的是 实际参数的引用, 此时在函数内部对该 table 所做的修改, 会直接对调用者所传递的实际参数生效

function change(arg)            --change函数, 改变长方形的长和宽, 使其各增长一倍
	arg.width = arg.width * 2   --表arg不是表rectangle的拷贝, 他们是同一个表
	arg.height = arg.height * 2
end -- 没有return语句了

local rectangle = { width = 20, height = 15 }
print("before change:", "width = ", rectangle.width,
                        " height = ", rectangle.height)
change(rectangle)
print("after change:", "width = ", rectangle.width,
                       " height =", rectangle.height)

--> output
before change: width = 20 height = 15
after change: width = 40 height = 30

函数返回值

函数必须放在调用的代码之前

lua允许函数返回多个值。返回多个值时, 值之间用“,”隔开。

local function swap(a, b) -- 定义函数 swap, 实现两个变量交换值
	return b, a           -- 按相反顺序返回变量的值
end

模块

一个 Lua 模块的数据结构是用一个 Lua 值( 通常是一个 Lua 表或者 Lua 函数) 。 一个 Lua 模块代码就是一个会返回这个 Lua 值的代码块。可以使用内建函数 require() 来加载和缓存模块。 模块加载后的结果通过是一个 Lua table,这个表就像是一个命名空间,其内容就是模块中导出的所有东西, 比如函数和变量。 require 函数会返回Lua 模块加载后的结果, 即用于表示该 Lua 模块的 Lua 值

require函数

要加载一个模块, 只需要简单地调用 require "file" 就可以了, file 指模块所在的文件名。这个调用会返回一个由模块函数组成的table, 并且还会定义一个包含该 table 的全局变量。 在 Lua 中创建一个模块最简单的方法是: 创建一个 table, 并将所有需要导出的函数放入其中, 最后返回这个 table 就可以了。 相当于将导出的函数作为 table 的一个字段

#my.lua, 创建了my模块
local foo={}

local function getname()
	return "Lucy"
end

function foo.greeting()
	print("hello " .. getname())
end

return foo
#在main.lua中加载my模块
local fp = require("my")
fp.greeting() -->output: hello Lucy

注: 对于需要导出给外部使用的公共模块, 处于安全考虑, 是要避免全局变量的出现。

String库

string.byte(s[,i[,j]]) 返回字符 s[i]、 s[i + 1]、 s[i + 2]、 ······、 s[j] 所对应的 ASCII 码

string.char (...) 接收 0 个或更多的整数( 整数范围: 0~255) , 返回这些整数所对应的 ASCII 码字符组成的字符串

string.upper(s) 接收一个字符串 s, 返回一个把所有小写字母变成大写字母的字符串

string.lower(s) 接收一个字符串 s, 返回一个把所有大写字母变成小写字母的字符串。

string.len(s) 接收一个字符串, 返回它的长度。

string.find(s, p [, init [, plain]]) 在 s 字符串中第一次匹配 p 字符串。 若匹配成功, 则返回 p 字符串在 s 字符串中出现的开始位置和结束位置; 若匹配失败, 则返回 nil。

string.format(formatstring, ...) 按照格式化参数 formatstring, 返回后面 ... 内容的格式化版本

string.match(s, p [, init]) 在字符串 s 中匹配( 模式) 字符串 p, 若匹配成功, 则返回目标字符串中与模式匹配的子串;否则返回 nil。

string.gmatch(s, p) 返回一个迭代器函数, 通过这个迭代器函数可以遍历到在字符串 s 中出现模式串 p 的所有地方。

string.rep(s, n) 返回字符串 s 的 n 次拷贝。

string.sub(s, i [, j]) 返回字符串 s 中, 索引 i 到索引 j 之间的子字符串

string.gsub(s, p, r [, n]) 将目标字符串 s 中所有的子串 p 替换成字符串 r

string.reverse (s) 接收一个字符串 s, 返回这个字符串的反转

table库

下标从 1 开始。在 Lua 中, 数组下标从 1 开始计数。

table.getn 获取长度

对于常规的数组, 里面从 1 到 n 放着一些非空的值的时候, 它的长度就精确的为 n, 即最后一个值的下标。 如果数组有一个“空洞”( 就是说, nil 值被夹在非空值之间) , 那么 #t 可能是指向任何一个是 nil 值的前一个位置的下标。 不要在 Lua 的 table 中使用 nil 值, 如果一个元素要删除, 直接 remove, 不要用 nil 去代替。

table.concat (table [, sep [, i [, j ] ] ]) 对于元素是 string 或者 number 类型的表 table, 返回 table[i]..sep..table[i+1] ··· sep..table[j] 连接成的字符串

table.insert (table, [pos ,] value) 在( 数组型) 表 table 的 pos 索引位置插入 value, 其它元素向后移动到空的地方

table.maxn (table) 返回( 数组型) 表 table 的最大索引编号; 如果此表没有正的索引编号, 返回 0

table.remove (table [, pos]) 在表 table 中删除索引为 pos( pos 只能是 number 型) 的元素, 并返回这个被删除的元素,它后面所有元素的索引值都会减一

table.sort (table [, comp]) 按照给定的比较函数 comp 给表 table 排序, 也就是从 table[1] 到 table[n], 这里 n 表示 table的长度

##日期时间函数

os.time ([table]) 如果不使用参数 table 调用 time 函数, 它会返回当前的时间和日期( 它表示从某一时刻到现在的秒数)

os.difftime (t2, t1) 返回 t1 到 t2 的时间差, 单位为秒

os.date ([format [, time]]) 把一个表示日期和时间的数值, 转换成更高级的表现形式

格式字符含义
%a一星期中天数的简写( 例如: Wed)
%A一星期中天数的全称( 例如: Wednesday)
%b月份的简写( 例如: Sep)
%B月份的全称( 例如: September)
%c日期和时间( 例如: 07/30/15 16:57:24)
%d一个月中的第几天[01 ~ 31]
%H24小时制中的小时数[00 ~ 23]
%I12小时制中的小时数[01 ~ 12]
%j一年中的第几天[001 ~ 366]
%M分钟数[00 ~ 59]
%m月份数[01 ~ 12]
%p“上午( am) ”或“下午( pm) ”
%S秒数[00 ~ 59]
%w一星期中的第几天[1 ~ 7 = 星期天 ~ 星期六]
%x日期( 例如: 07/30/15)
%X时间( 例如: 16:57:24)
%y两位数的年份[00 ~ 99]
%Y完整的年份( 例如: 2015)
%%字符'%'

数学库

函数名函数功能
math.rad(x)角度x转换成弧度
math.deg(x)弧度x转换成角度
math.max(x, ...)返回参数中值最大的那个数, 参数必须是number型
math.min(x, ...)返回参数中值最小的那个数, 参数必须是number型
math.random ([m[, n]])不传入参数时, 返回 一个在区间[0,1)内均匀分布的伪随机实数; 只使用一个整数参数m时, 返回一个在区间[1, m]内均匀分布的伪随机整数; 使用两个整数参数时, 返回一个在区间[m, n]内均匀分布的伪随机整数
math.randomseed(x)为伪随机数生成器设置一个种子x, 相同的种子将会生成相同的数字序列
math.abs(x)返回x的绝对值
math.fmod(x, y)返回 x对y取余数
math.pow(x, y)返回x的y次方
math.sqrt(x)返回x的算术平方根
math.exp(x)返回自然数e的x次方
math.log(x)返回x的自然对数
math.log10(x)返回以10为底, x的对数
math.floor(x)返回最大且不大于x的整数
math.ceil(x)返回最小且不小于x的整数
math.pi圆周率
math.sin(x)求弧度x的正弦值
math.cos(x)求弧度x的余弦值
math.tan(x)求弧度x的正切值
math.asin(x)求x的反正弦值
math.acos(x)求x的反余弦值
math.atan(x)求x的反正切值

文件操作

io.open (filename [, mode]) 按指定的模式 mode, 打开一个文件名为 filename 的文件, 成功则返回文件句柄, 失败则返回 nil 加错误信息

模式含义文件不存在时
"r"读模式 (默认)返回nil加错误信息
"w"写模式创建文件
"a"添加模式创建文件
"r+"更新模式, 保存之前的数据返回nil加错误信息
"w+"更新模式, 清除之前的数据创建文件
"a+"添加更新模式, 保存之前的数据,在文件尾进行添加创建文件

file:close () 关闭文件。 注意: 当文件句柄被垃圾收集后, 文件将自动关闭。 句柄将变为一个不可预知的值。

io.close ([file]) 关闭文件, 和 file:close() 的作用相同。 没有参数 file 时, 关闭默认输出文件。

file:flush () 把写入缓冲区的所有数据写入到文件 file 中。

io.flush () 相当于 file:flush(), 把写入缓冲区的所有数据写入到默认输出文件

io.input ([file]) 当使用一个文件名调用时, 打开这个文件( 以文本模式) , 并设置文件句柄为默认输入文件; 当使用一个文件句柄调用时, 设置此文件句柄为默认输入文件; 当不使用参数调用时,返回默认输入文件句柄。

file:lines () 返回一个迭代函数, 每次调用将获得文件中的一行内容, 当到文件尾时, 将返回 nil, 但不关闭文件。

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

io.output ([file]) 类似于 io.input, 但操作在默认输出文件上。

file:read (...) 按指定的格式读取一个文件。 按每个格式将返回一个字符串或数字, 如果不能正确读取将返回nil, 若没有指定格式将指默认按行方式进行读取。

格式含义
"*n"读取一个数字
"*a"从当前位置读取整个文件。 若当前位置为文件尾, 则返回空字符串
"*l"读取下一行的内容。 若为文件尾, 则返回nil。 (默认)
number读取指定字节数的字符。 若为文件尾, 则返回nil。 如果number为0,则返回空字符串, 若为文件尾,则返回nil

io.read (...) 相当于 io.input():read

io.type (obj) 检测 obj 是否一个可用的文件句柄。 如果 obj 是一个打开的文件句柄, 则返回 "file" 如果 obj是一个已关闭的文件句柄, 则返回 "closed file" 如果 obj 不是一个文件句柄, 则返回 nil。

file:write (...) 把每一个参数的值写入文件。 参数必须为字符串或数字, 若要输出其它值, 则需通过 tostring 或 string.format 进行转换。

io.write (...) 相当于 io.output():write

file:seek ([whence] [, offset]) 设置和获取当前文件位置, 成功则返回最终的文件位置(按字节, 相对于文件开头),失败则返回 nil 加错误信息。

whence含义
"set"文件开始
"cur"文件当前位置(默认)
"end"文件结束

file:setvbuf (mode [, size]) 设置输出文件的缓冲模式

模式含义
"no"没有缓冲, 即直接输出
"full"全缓冲, 即当缓冲满后才进行输出操作(也可调用flush马上输出)
"line"以行为单位, 进行输出

元表

元表 (metatable) 的表现行为类似于 C++ 语言中的操作符重载 Lua 提供了两个十分重要的用来处理元表的方法, 如下:

  • setmetatable(table, metatable): 此方法用于为一个表设置元表。
  • getmetatable(table): 此方法用于获取表的元表对象。
# 设置元表
local mytable = {}
local mymetatable = {}
setmetatable(mytable, mymetatable)
元方法含义
"__add"+ 操作
"__sub"- 操作 其行为类似于 "add" 操作
"__mul"* 操作 其行为类似于 "add" 操作
"__div"/ 操作 其行为类似于 "add" 操作
"__mod"% 操作 其行为类似于 "add" 操作
"__pow"^ ( 幂) 操作 其行为类似于 "add" 操作
"__unm"一元 - 操作
"__concat".. ( 字符串连接) 操作
"__len"# 操作
"__eq"== 操作 函数 getcomphandler 定义了 Lua 怎样选择一个处理器来作比较操作 仅在两个对象类型相同且有对应操作相同的元方法时才起效
"__lt"< 操作
"__le"<= 操作
"__index"取下标操作用于访问 table[key]
"__newindex"赋值给指定下标 table[key] = value
"__tostring"转换成字符串
"__call"当 Lua 调用一个值时调用
"__mode"用于弱表(week table)
"__metatable"用于保护metatable不被访问

面向对象

可以使用表和函数实现面向对象。 将函数和相关的数据放置于同一个表中就形成了一个对象。

下面的代码,创建了account类: account.lua

local _M = {}

local mt = { __index = _M }

function _M.deposit (self, v)
	self.balance = self.balance + v
end

function _M.withdraw (self, v)
	if self.balance > v then
		self.balance = self.balance - v
	else
		error("insufficient funds")
	end
end

function _M.new (self, balance)
	balance = balance or 0
	return setmetatable({balance = balance}, mt)
end

return _M

下面代码是使用account类示例:

local account = require("account")

local a = account:new()
a:deposit(100)

local b = account:new()
b:deposit(50)

print(a.balance) --> output: 100
print(b.balance) --> output: 50

继承

继承可以用元表实现, 它提供了在父类中查找存在的方法和变量的机制。 在 Lua 中是不推荐使用继承方式完成构造的, 这样做引入的问题可能比解决的问题要多

---------- s_base.lua
local _M = {}

local mt = { __index = _M }

function _M.upper (s)
	return string.upper(s)
end

return _M

---------- s_more.lua
local s_base = require("s_base")

local _M = {}
_M = setmetatable(_M, { __index = s_base })

function _M.lower (s)
	return string.lower(s)
end

return _M

---------- test.lua
local s_more = require("s_more")

print(s_more.upper("Hello")) -- output: HELLO
print(s_more.lower("Hello")) -- output: hello

局部变量

在一个 block 中的变量, 如果之前没有定义过, 那么认为它是一个全局变量, 而不是这个 block 的局部变量 Lua 中的局部变量要用 local 关键字来显式定义, 不使用 local 显式定义的变量就是全局变量 局部变量的生命周期是有限的, 它的作用域仅限于声明它的块( block) 。 一个块是一个控制结构的执行体、 或者是一个函数的执行体再或者是一个程序块( chunk) “尽量使用局部变量”是一种良好的编程风格

判断数组大小

table.getn(t) 等价于 #t 但计算的是数组元素, 不包括 hash 键值。 而且数组是以第一个 nil 元素来判断数组结束。 # 只计算 array 的元素个数, 它实际上调用了对象的 metatable 的__len 函数。 对于有 __len 方法的函数返回函数返回值, 不然就返回数组成员数目。

注意: 一定不要使用 # 操作符或 table.getn 来计算包含 nil 的数组长度, 这是一个未定义的操作, 不一定报错, 但不能保证结果如你所想。 如果你要删除一个数组中的元素, 请使用remove 函数, 而不是用 nil 赋值。

非空判断

对于table型变量,可以如下判断非空:

local person = {name = "Bob", sex = "M"}
if person ~= nil and person.name ~= nil then
	print(person.name)
end

对于简单类型的变量, 我们可以用 if (var == nil) then 这样的简单句子来判断。 但是对于 table型的 Lua 对象, 就不能这么简单判断它是否为空了。 一个 table 型变量的值可能是 {} , 这时它不等于 nil

正则表达式(openresty中的正则表达式规范 -- ngx.re.*)

下面是使用openresty中正则的例子:

location /test {
	content_by_lua_block {
	local regex = [[\d+]]
	
	-- 参数 "j" 启用 JIT 编译, 参数 "o" 是开启缓存必须的
	local m = ngx.re.match("hello, 1234", regex, "jo")
	if m then
		ngx.say(m[0])
	else
		ngx.say("not matched!")
	end
	}
}

正则中使用了jo 两个参数:

  • ngx.re.* 中的 o 选项, 指明该参数, 被编译的 Pattern 将会在工作进程中缓存, 并且被当前工作进程的每次请求所共享
  • ngx.re.* 中的 j 选项, 指明该参数, 如果使用的 PCRE 库支持 JIT, OpenResty 会在编译 Pattern 时启用 JIT。即使运行在不支持 JIT 的 OpenResty 上, 加上 j 选项也不会带来坏的影响

lua正则汇总:

符号匹配次数匹配模式
+匹配前一字符 1 次或多次非贪婪
*匹配前一字符 0 次或多次贪婪
-匹配前一字符 0 次或多次非贪婪
?匹配前一字符 0 次或1次仅用于此, 不用于标识是否贪婪
符号匹配模式
.任意字符
%a字母
%c控制字符
%d数字
%l小写字母
%p标点字符
%s空白符
%u大写字母
%w字母和数字
%x十六进制数字
%z代表 0 的字符

虚变量

当一个方法返回多个值时, 有些返回值有时候用不到, 要是声明很多变量来一一接收, 显然不太合适( 不是不能) 。 Lua 提供了一个虚变量(dummy variable), 以单个下划线( “_”) 来命名, 用它来丢弃不需要的数值, 仅仅起到占位的作用。

-- string.find (s,p) 从string 变量s的开头向后匹配 string
-- p, 若匹配不成功, 返回nil, 若匹配成功, 返回第一次匹配成功的起止下标。
local start, finish = string.find("hello", "he") --start 值为起始下标, finish
                                                 --值为结束下标
print ( start, finish )                          --输出 1 2
local start = string.find("hello", "he")         -- start值为起始下标
print ( start )                                  -- 输出 1
local _,finish = string.find("hello", "he")      --采用虚变量( 即下划线) , 接收起
											     --始下标值, 然后丢弃, finish接收
												 --结束下标值
print ( finish )                                 --输出 2

点号与冒号操作符的区别

冒号操作会带入一个 self 参数, 用来代表 自己 。 而点号操作, 只是 内容 的展开。

local str = "abcde"
print("case 1:", str:sub(1, 2))
print("case 2:", str.sub(str, 1, 2))

转载于:https://my.oschina.net/ez8life/blog/1922154

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值