12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598 |
- --[[
- Copyright (c) 2011-2014 chukong-inc.com
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- ]]
- --[[--
- 提供一组常用函数,以及对 Lua 标准库的扩展
- ]]
- --[[--
- 输出格式化字符串
- ~~~ lua
- printf("The value = %d", 100)
- ~~~
- @param string fmt 输出格式
- @param [mixed ...] 更多参数
- ]]
- function printf(fmt, ...)
- print(string.format(tostring(fmt), ...))
- end
- local skynet = require "skynet"
- local cjson = require "cjson"
- function table.empty(tlb)
- local t = tlb or {}
- for k, v in pairs(tlb) do
- return false
- end
- return true
- end
- function table.array(array)
- return setmetatable(array, cjson.empty_array_mt)
- end
- function table.dumpdebug(value, desciption, nesting)
- if type(nesting) ~= "number" then nesting = 3 end
- local lookupTable = {}
- local result = {}
- local function _v(v)
- if type(v) == "string" then
- v = "\"" .. v .. "\""
- end
- return tostring(v)
- end
- local traceback = string.split(debug.traceback("", 2), "\n")
- if not ngx then
- skynet.error("dump from: " .. string.trim(traceback[3]))
- else
- ngx.log(ngx.DEBUG,"dump from: " .. string.trim(traceback[3]))
- end
- local function _dump(value, desciption, indent, nest, keylen)
- desciption = desciption or "<var>"
- spc = ""
- if type(keylen) == "number" then
- spc = string.rep(" ", keylen - string.len(_v(desciption)))
- end
- if type(value) ~= "table" then
- result[#result +1 ] = string.format("%s%s%s = %s", indent, _v(desciption), spc, _v(value))
- elseif lookupTable[value] then
- result[#result +1 ] = string.format("%s%s%s = *REF*", indent, desciption, spc)
- else
- lookupTable[value] = true
- if nest > nesting then
- result[#result +1 ] = string.format("%s%s = *MAX NESTING*", indent, desciption)
- else
- result[#result +1 ] = string.format("%s%s = {", indent, _v(desciption))
- local indent2 = indent.." "
- local keys = {}
- local keylen = 0
- local values = {}
- for k, v in pairs(value) do
- keys[#keys + 1] = k
- local vk = _v(k)
- local vkl = string.len(vk)
- if vkl > keylen then keylen = vkl end
- values[k] = v
- end
- table.sort(keys, function(a, b)
- if type(a) == "number" and type(b) == "number" then
- return a < b
- else
- return tostring(a) < tostring(b)
- end
- end)
- for i, k in ipairs(keys) do
- _dump(values[k], k, indent2, nest + 1, keylen)
- end
- result[#result +1] = string.format("%s}", indent)
- end
- end
- end
- _dump(value, desciption, "- ", 1)
- for i, line in ipairs(result) do
- if not ngx then
- skynet.error(line)
- else
- ngx.log(ngx.DEBUG,line)
- end
- end
- end
- string.split = function(s, p)
- local rt= {}
- string.gsub(s, '[^'..p..']+', function(w) table.insert(rt, w) end )
- return rt
- end
- --[[--
- 检查并尝试转换为数值,如果无法转换则返回 0
- @param mixed value 要检查的值
- @param [integer base] 进制,默认为十进制
- @return number
- ]]
- function checknumber(value, base)
- return tonumber(value, base) or 0
- end
- --[[--
- 检查并尝试转换为整数,如果无法转换则返回 0
- @param mixed value 要检查的值
- @return integer
- ]]
- function checkint(value)
- return math.round(checknumber(value))
- end
- --[[--
- 检查并尝试转换为布尔值,除了 nil 和 false,其他任何值都会返回 true
- @param mixed value 要检查的值
- @return boolean
- ]]
- function checkbool(value)
- return (value ~= nil and value ~= false)
- end
- --[[--
- 检查值是否是一个表格,如果不是则返回一个空表格
- @param mixed value 要检查的值
- @return table
- ]]
- function checktable(value)
- if type(value) ~= "table" then value = {} end
- return value
- end
- --[[--
- 如果表格中指定 key 的值为 nil,或者输入值不是表格,返回 false,否则返回 true
- @param table hashtable 要检查的表格
- @param mixed key 要检查的键名
- @return boolean
- ]]
- function isset(hashtable, key)
- local t = type(hashtable)
- return (t == "table" or t == "userdata") and hashtable[key] ~= nil
- end
- --[[--
- 深度克隆一个值
- ~~~ lua
- -- 下面的代码,t2 是 t1 的引用,修改 t2 的属性时,t1 的内容也会发生变化
- local t1 = {a = 1, b = 2}
- local t2 = t1
- t2.b = 3 -- t1 = {a = 1, b = 3} <-- t1.b 发生变化
- -- clone() 返回 t1 的副本,修改 t2 不会影响 t1
- local t1 = {a = 1, b = 2}
- local t2 = clone(t1)
- t2.b = 3 -- t1 = {a = 1, b = 2} <-- t1.b 不受影响
- ~~~
- @param mixed object 要克隆的值
- @return mixed
- ]]
- function clone(object)
- local lookup_table = {}
- local function _copy(object)
- if type(object) ~= "table" then
- return object
- elseif lookup_table[object] then
- return lookup_table[object]
- end
- local new_table = {}
- lookup_table[object] = new_table
- for key, value in pairs(object) do
- new_table[_copy(key)] = _copy(value)
- end
- return setmetatable(new_table, getmetatable(object))
- end
- return _copy(object)
- end
- --[[--
- 创建一个类
- ~~~ lua
- -- 定义名为 Shape 的基础类
- local Shape = class("Shape")
- -- ctor() 是类的构造函数,在调用 Shape.new() 创建 Shape 对象实例时会自动执行
- function Shape:ctor(shapeName)
- self.shapeName = shapeName
- printf("Shape:ctor(%s)", self.shapeName)
- end
- -- 为 Shape 定义个名为 draw() 的方法
- function Shape:draw()
- printf("draw %s", self.shapeName)
- end
- --
- -- Circle 是 Shape 的继承类
- local Circle = class("Circle", Shape)
- function Circle:ctor()
- -- 如果继承类覆盖了 ctor() 构造函数,那么必须手动调用父类构造函数
- -- 类名.super 可以访问指定类的父类
- Circle.super.ctor(self, "circle")
- self.radius = 100
- end
- function Circle:setRadius(radius)
- self.radius = radius
- end
- -- 覆盖父类的同名方法
- function Circle:draw()
- printf("draw %s, raidus = %0.2f", self.shapeName, self.raidus)
- end
- --
- local Rectangle = class("Rectangle", Shape)
- function Rectangle:ctor()
- Rectangle.super.ctor(self, "rectangle")
- end
- --
- local circle = Circle.new() -- 输出: Shape:ctor(circle)
- circle:setRaidus(200)
- circle:draw() -- 输出: draw circle, radius = 200.00
- local rectangle = Rectangle.new() -- 输出: Shape:ctor(rectangle)
- rectangle:draw() -- 输出: draw rectangle
- ~~~
- ### 高级用法
- class() 除了定义纯 Lua 类之外,还可以从 C++ 对象继承类。
- 比如需要创建一个工具栏,并在添加按钮时自动排列已有的按钮,那么我们可以使用如下的代码:
- ~~~ lua
- -- 从 CCNode 对象派生 Toolbar 类,该类具有 CCNode 的所有属性和行为
- local Toolbar = class("Toolbar", function()
- return display.newNode() -- 返回一个 CCNode 对象
- end)
- -- 构造函数
- function Toolbar:ctor()
- self.buttons = {} -- 用一个 table 来记录所有的按钮
- end
- -- 添加一个按钮,并且自动设置按钮位置
- function Toolbar:addButton(button)
- -- 将按钮对象加入 table
- self.buttons[#self.buttons + 1] = button
- -- 添加按钮对象到 CCNode 中,以便显示该按钮
- -- 因为 Toolbar 是从 CCNode 继承的,所以可以使用 addChild() 方法
- self:addChild(button)
- -- 按照按钮数量,调整所有按钮的位置
- local x = 0
- for _, button in ipairs(self.buttons) do
- button:setPosition(x, 0)
- -- 依次排列按钮,每个按钮之间间隔 10 点
- x = x + button:getContentSize().width + 10
- end
- end
- ~~~
- class() 的这种用法让我们可以在 C++ 对象基础上任意扩展行为。
- 既然是继承,自然就可以覆盖 C++ 对象的方法:
- ~~~ lua
- function Toolbar:setPosition(x, y)
- -- 由于在 Toolbar 继承类中覆盖了 CCNode 对象的 setPosition() 方法
- -- 所以我们要用以下形式才能调用到 CCNode 原本的 setPosition() 方法
- getmetatable(self).setPosition(self, x, y)
- printf("x = %0.2f, y = %0.2f", x, y)
- end
- ~~~
- **注意:** Lua 继承类覆盖的方法并不能从 C++ 调用到。也就是说通过 C++ 代码调用这个 CCNode 对象的 setPosition() 方法时,并不会执行我们在 Lua 中定义的 Toolbar:setPosition() 方法。
- @param string classname 类名
- @param [mixed super] 父类或者创建对象实例的函数
- @return table
- ]]
- function class(classname, super)
- local superType = type(super)
- local cls
- if superType ~= "function" and superType ~= "table" then
- superType = nil
- super = nil
- end
- if superType == "function" or (super and super.__ctype == 1) then
- -- inherited from native C++ Object
- cls = {}
- if superType == "table" then
- -- copy fields from super
- for k,v in pairs(super) do cls[k] = v end
- cls.__create = super.__create
- cls.super = super
- else
- cls.__create = super
- cls.ctor = function() end
- end
- cls.__cname = classname
- cls.__ctype = 1
- function cls.new(...)
- local instance = cls.__create(...)
- -- copy fields from class to native object
- for k,v in pairs(cls) do instance[k] = v end
- instance.class = cls
- instance:ctor(...)
- return instance
- end
- else
- -- inherited from Lua Object
- if super then
- cls = {}
- setmetatable(cls, {__index = super})
- cls.super = super
- else
- cls = {ctor = function() end}
- end
- cls.__cname = classname
- cls.__ctype = 2 -- lua
- cls.__index = cls
- function cls.new(...)
- local instance = setmetatable({}, cls)
- instance.class = cls
- instance:ctor(...)
- return instance
- end
- end
- return cls
- end
- -- 提供假名以避免和 moonscript 发生冲突
- function quick_class(classname, super)
- return class(classname, super)
- end
- --[[--
- 如果对象是指定类或其子类的实例,返回 true,否则返回 false
- ~~~ lua
- local Animal = class("Animal")
- local Duck = class("Duck", Animal)
- print(iskindof(Duck.new(), "Animal")) -- 输出 true
- ~~~
- @param mixed obj 要检查的对象
- @param string classname 类名
- @return boolean
- ]]
- function iskindof(obj, classname)
- local t = type(obj)
- local mt
- if t == "table" then
- mt = getmetatable(obj)
- elseif t == "userdata" then
- mt = tolua.getpeer(obj)
- end
- while mt do
- if mt.__cname == classname then
- return true
- end
- mt = mt.super
- end
- return false
- end
- --[[--
- 载入一个模块
- import() 与 require() 功能相同,但具有一定程度的自动化特性。
- 假设我们有如下的目录结构:
- ~~~
- app/
- app/classes/
- app/classes/MyClass.lua
- app/classes/MyClassBase.lua
- app/classes/data/Data1.lua
- app/classes/data/Data2.lua
- ~~~
- MyClass 中需要载入 MyClassBase 和 MyClassData。如果用 require(),MyClass 内的代码如下:
- ~~~ lua
- local MyClassBase = require("app.classes.MyClassBase")
- local MyClass = class("MyClass", MyClassBase)
- local Data1 = require("app.classes.data.Data1")
- local Data2 = require("app.classes.data.Data2")
- ~~~
- 假如我们将 MyClass 及其相关文件换一个目录存放,那么就必须修改 MyClass 中的 require() 命令,否则将找不到模块文件。
- 而使用 import(),我们只需要如下写:
- ~~~ lua
- local MyClassBase = import(".MyClassBase")
- local MyClass = class("MyClass", MyClassBase)
- local Data1 = import(".data.Data1")
- local Data2 = import(".data.Data2")
- ~~~
- 当在模块名前面有一个"." 时,import() 会从当前模块所在目录中查找其他模块。因此 MyClass 及其相关文件不管存放到什么目录里,我们都不再需要修改 MyClass 中的 import() 命令。这在开发一些重复使用的功能组件时,会非常方便。
- 我们可以在模块名前添加多个"." ,这样 import() 会从更上层的目录开始查找模块。
- ~
- 不过 import() 只有在模块级别调用(也就是没有将 import() 写在任何函数中)时,才能够自动得到当前模块名。如果需要在函数中调用 import(),那么就需要指定当前模块名:
- ~~~ lua
- # MyClass.lua
- # 这里的 ... 是隐藏参数,包含了当前模块的名字,所以最好将这行代码写在模块的第一行
- local CURRENT_MODULE_NAME = ...
- local function testLoad()
- local MyClassBase = import(".MyClassBase", CURRENT_MODULE_NAME)
- # 更多代码
- end
- ~~~
- @param string moduleName 要载入的模块的名字
- @param [string currentModuleName] 当前模块名
- @return module
- ]]
- function import(moduleName, currentModuleName)
- local currentModuleNameParts
- local moduleFullName = moduleName
- local offset = 1
- while true do
- if string.byte(moduleName, offset) ~= 46 then -- .
- moduleFullName = string.sub(moduleName, offset)
- if currentModuleNameParts and #currentModuleNameParts > 0 then
- moduleFullName = table.concat(currentModuleNameParts, ".") .. "." .. moduleFullName
- end
- break
- end
- offset = offset + 1
- if not currentModuleNameParts then
- if not currentModuleName then
- local n,v = debug.getlocal(3, 1)
- currentModuleName = v
- end
- currentModuleNameParts = string.split(currentModuleName, ".")
- end
- table.remove(currentModuleNameParts, #currentModuleNameParts)
- end
- return require(moduleFullName)
- end
- --[[--
- 将 Lua 对象及其方法包装为一个匿名函数
- 在 quick-cocos2d-x 中,许多功能需要传入一个 Lua 函数做参数,然后在特定事件发生时就会调用传入的函数。例如触摸事件、帧事件等等。
- ~~~ lua
- local MyScene = class("MyScene", function()
- return display.newScene("MyScene")
- end)
- function MyScene:ctor()
- self.frameTimeCount = 0
- -- 注册帧事件
- self:addEventListener(cc.ENTER_FRAME_EVENT, self.onEnterFrame)
- end
- function MyScene:onEnterFrame(dt)
- self.frameTimeCount = self.frameTimeCount + dt
- end
- ~~~
- 上述代码执行时将出错,报告"Invalid self" ,这就是因为 C++ 无法识别 Lua 对象方法。因此在调用我们传入的 self.onEnterFrame 方法时没有提供正确的参数。
- 要让上述的代码正常工作,就需要使用 handler() 进行一下包装:
- ~~~ lua
- function MyScene:ctor()
- self.frameTimeCount = 0
- -- 注册帧事件
- self:addEventListener(cc.ENTER_FRAME_EVENT, handler(self, self.onEnterFrame))
- end
- ~~~
- 实际上,除了 C++ 回调 Lua 函数之外,在其他所有需要回调的地方都可以使用 handler()。
- @param mixed obj Lua 对象
- @param function method 对象方法
- @return function
- ]]
- function handler(obj, method)
- return function(...)
- return method(obj, ...)
- end
- end
- --[[--
- 根据系统时间初始化随机数种子,让后续的 math.random() 返回更随机的值
- ]]
- function math.newrandomseed()
- local ok, socket = pcall(function()
- return require("socket")
- end)
- if ok then
- -- 如果集成了 socket 模块,则使用 socket.gettime() 获取随机数种子
- math.randomseed(socket.gettime() * 1000)
- else
- math.randomseed(os.time())
- end
- math.random()
- math.random()
- math.random()
- math.random()
- end
- --[[--
- 对数值进行四舍五入,如果不是数值则返回 0
- @param number value 输入值
- @return number
- ]]
- function math.round(value)
- return math.floor(value + 0.5)
- end
- function math.angle2radian(angle)
- return angle*math.pi/180
- end
- function math.radian2angle(radian)
- return radian/math.pi*180
- end
- --[[--
- 检查指定的文件或目录是否存在,如果存在返回 true,否则返回 false
- 可以使用 CCFileUtils:fullPathForFilename() 函数查找特定文件的完整路径,例如:
- ~~~ lua
- local path = CCFileUtils:sharedFileUtils():fullPathForFilename("gamedata.txt")
- if io.exists(path) then
- ....
- end
- ~~~
- @param string path 要检查的文件或目录的完全路径
- @return boolean
- ]]
- function io.exists(path)
- local file = io.open(path, "r")
- if file then
- io.close(file)
- return true
- end
- return false
- end
- --[[--
- 读取文件内容,返回包含文件内容的字符串,如果失败返回 nil
- io.readfile() 会一次性读取整个文件的内容,并返回一个字符串,因此该函数不适宜读取太大的文件。
- @param string path 文件完全路径
- @return string
- ]]
- function io.readfile(path)
- local file = io.open(path, "r")
- if file then
- local content = file:read("*a")
- io.close(file)
- return content
- end
- return nil
- end
- --[[--
- 以字符串内容写入文件,成功返回 true,失败返回 false
- "mode 写入模式" 参数决定 io.writefile() 如何写入内容,可用的值如下:
- - "w+" : 覆盖文件已有内容,如果文件不存在则创建新文件
- - "a+" : 追加内容到文件尾部,如果文件不存在则创建文件
- 此外,还可以在 "写入模式" 参数最后追加字符 "b" ,表示以二进制方式写入数据,这样可以避免内容写入不完整。
- **Android 特别提示:** 在 Android 平台上,文件只能写入存储卡所在路径,assets 和 data 等目录都是无法写入的。
- @param string path 文件完全路径
- @param string content 要写入的内容
- @param [string mode] 写入模式,默认值为 "w+b"
- @return boolean
- ]]
- function io.writefile(path, content, mode)
- mode = mode or "w+b"
- local file = io.open(path, mode)
- if file then
- if file:write(content) == nil then return false end
- io.close(file)
- return true
- else
- return false
- end
- end
- --[[--
- 拆分一个路径字符串,返回组成路径的各个部分
- ~~~ lua
- local pathinfo = io.pathinfo("/var/app/test/abc.png")
- -- 结果:
- -- pathinfo.dirname = "/var/app/test/"
- -- pathinfo.filename = "abc.png"
- -- pathinfo.basename = "abc"
- -- pathinfo.extname = ".png"
- ~~~
- @param string path 要分拆的路径字符串
- @return table
- ]]
- function io.pathinfo(path)
- local pos = string.len(path)
- local extpos = pos + 1
- while pos > 0 do
- local b = string.byte(path, pos)
- if b == 46 then -- 46 = char "."
- extpos = pos
- elseif b == 47 then -- 47 = char "/"
- break
- end
- pos = pos - 1
- end
- local dirname = string.sub(path, 1, pos)
- local filename = string.sub(path, pos + 1)
- extpos = extpos - pos
- local basename = string.sub(filename, 1, extpos - 1)
- local extname = string.sub(filename, extpos)
- return {
- dirname = dirname,
- filename = filename,
- basename = basename,
- extname = extname
- }
- end
- --[[--
- 返回指定文件的大小,如果失败返回 false
- @param string path 文件完全路径
- @return integer
- ]]
- function io.filesize(path)
- local size = false
- local file = io.open(path, "r")
- if file then
- local current = file:seek()
- size = file:seek("end")
- file:seek("set", current)
- io.close(file)
- end
- return size
- end
- --[[--
- 计算表格包含的字段数量
- Lua table 的 "#" 操作只对依次排序的数值下标数组有效,table.nums() 则计算 table 中所有不为 nil 的值的个数。
- @param table t 要检查的表格
- @return integer
- ]]
- function table.nums(t)
- local count = 0
- for k, v in pairs(t) do
- count = count + 1
- end
- return count
- end
- --[[--
- 返回指定表格中的所有键
- ~~~ lua
- local hashtable = {a = 1, b = 2, c = 3}
- local keys = table.keys(hashtable)
- -- keys = {"a", "b", "c"}
- ~~~
- @param table hashtable 要检查的表格
- @return table
- ]]
- function table.keys(hashtable)
- local keys = {}
- for k, v in pairs(hashtable) do
- keys[#keys + 1] = k
- end
- return keys
- end
- --[[--
- 返回指定表格中的所有值
- ~~~ lua
- local hashtable = {a = 1, b = 2, c = 3}
- local values = table.values(hashtable)
- -- values = {1, 2, 3}
- ~~~
- @param table hashtable 要检查的表格
- @return table
- ]]
- function table.values(hashtable)
- local values = {}
- for k, v in pairs(hashtable) do
- values[#values + 1] = v
- end
- return values
- end
- --[[--
- 将来源表格中所有键及其值复制到目标表格对象中,如果存在同名键,则覆盖其值
- ~~~ lua
- local dest = {a = 1, b = 2}
- local src = {c = 3, d = 4}
- table.merge(dest, src)
- -- dest = {a = 1, b = 2, c = 3, d = 4}
- ~~~
- @param table dest 目标表格
- @param table src 来源表格
- ]]
- function table.merge(dest, src)
- for k, v in pairs(src) do
- dest[k] = v
- end
- end
- --[[--
- 在目标表格的指定位置插入来源表格,如果没有指定位置则连接两个表格
- ~~~ lua
- local dest = {1, 2, 3}
- local src = {4, 5, 6}
- table.insertto(dest, src)
- -- dest = {1, 2, 3, 4, 5, 6}
- dest = {1, 2, 3}
- table.insertto(dest, src, 5)
- -- dest = {1, 2, 3, nil, 4, 5, 6}
- ~~~
- @param table dest 目标表格
- @param table src 来源表格
- @param [integer begin] 插入位置
- ]]
- function table.insertto(dest, src, begin)
- begin = checkint(begin)
- if begin <= 0 then
- begin = #dest + 1
- end
- local len = #src
- for i = 0, len - 1 do
- dest[i + begin] = src[i + 1]
- end
- end
- --[[
- 从表格中查找指定值,返回其索引,如果没找到返回 false
- ~~~ lua
- local array = {"a", "b", "c"}
- print(table.indexof(array, "b")) -- 输出 2
- ~~~
- @param table array 表格
- @param mixed value 要查找的值
- @param [integer begin] 起始索引值
- @return integer
- ]]
- function table.indexof(array, value, begin)
- for i = begin or 1, #array do
- if array[i] == value then return i end
- end
- return false
- end
- --[[--
- 从表格中查找指定值,返回其 key,如果没找到返回 nil
- ~~~ lua
- local hashtable = {name = "dualface", comp = "chukong"}
- print(table.keyof(hashtable, "chukong")) -- 输出 comp
- ~~~
- @param table hashtable 表格
- @param mixed value 要查找的值
- @return string 该值对应的 key
- ]]
- function table.keyof(hashtable, value)
- for k, v in pairs(hashtable) do
- if v == value then return k end
- end
- return nil
- end
- --[[--
- 从表格中删除指定值,返回删除的值的个数
- ~~~ lua
- local array = {"a", "b", "c", "c"}
- print(table.removebyvalue(array, "c", true)) -- 输出 2
- ~~~
- @param table array 表格
- @param mixed value 要删除的值
- @param [boolean removeall] 是否删除所有相同的值
- @return integer
- ]]
- function table.removebyvalue(array, value, removeall)
- local c, i, max = 0, 1, #array
- while i <= max do
- if array[i] == value then
- table.remove(array, i)
- c = c + 1
- i = i - 1
- max = max - 1
- if not removeall then break end
- end
- i = i + 1
- end
- return c
- end
- --[[--
- 对表格中每一个值执行一次指定的函数,并用函数返回值更新表格内容
- ~~~ lua
- local t = {name = "dualface", comp = "chukong"}
- table.map(t, function(v, k)
- -- 在每一个值前后添加括号
- return "[" .. v .. "]"
- end)
- -- 输出修改后的表格内容
- for k, v in pairs(t) do
- print(k, v)
- end
- -- 输出
- -- name [dualface]
- -- comp [chukong]
- ~~~
- fn 参数指定的函数具有两个参数,并且返回一个值。原型如下:
- ~~~ lua
- function map_function(value, key)
- return value
- end
- ~~~
- @param table t 表格
- @param function fn 函数
- ]]
- function table.map(t, fn)
- for k, v in pairs(t) do
- t[k] = fn(v, k)
- end
- end
- --[[--
- 对表格中每一个值执行一次指定的函数,但不改变表格内容
- ~~~ lua
- local t = {name = "dualface", comp = "chukong"}
- table.walk(t, function(v, k)
- -- 输出每一个值
- print(v)
- end)
- ~~~
- fn 参数指定的函数具有两个参数,没有返回值。原型如下:
- ~~~ lua
- function map_function(value, key)
- end
- ~~~
- @param table t 表格
- @param function fn 函数
- ]]
- function table.walk(t, fn)
- for k,v in pairs(t) do
- fn(v, k)
- end
- end
- --[[--
- 对表格中每一个值执行一次指定的函数,如果该函数返回 false,则对应的值会从表格中删除
- ~~~ lua
- local t = {name = "dualface", comp = "chukong"}
- table.filter(t, function(v, k)
- return v ~= "dualface" -- 当值等于 dualface 时过滤掉该值
- end)
- -- 输出修改后的表格内容
- for k, v in pairs(t) do
- print(k, v)
- end
- -- 输出
- -- comp chukong
- ~~~
- fn 参数指定的函数具有两个参数,并且返回一个 boolean 值。原型如下:
- ~~~ lua
- function map_function(value, key)
- return true or false
- end
- ~~~
- @param table t 表格
- @param function fn 函数
- ]]
- function table.filter(t, fn)
- for k, v in pairs(t) do
- if not fn(v, k) then t[k] = nil end
- end
- end
- --[[--
- 遍历表格,确保其中的值唯一
- ~~~ lua
- local t = {"a", "a", "b", "c"} -- 重复的 a 会被过滤掉
- local n = table.unique(t)
- for k, v in pairs(n) do
- print(v)
- end
- -- 输出
- -- a
- -- b
- -- c
- ~~~
- @param table t 表格
- @return table 包含所有唯一值的新表格
- ]]
- function table.unique(t)
- local check = {}
- local n = {}
- for k, v in pairs(t) do
- if not check[v] then
- n[k] = v
- check[v] = true
- end
- end
- return n
- end
- -- 随机洗牌
- function table.shuffle(t)
- local n = #t
- while n >= 2 do
- -- n is now the last pertinent index
- local k = math.random(n) -- 1 <= k <= n
- -- Quick swap
- t[n], t[k] = t[k], t[n]
- n = n - 1
- end
- return t
- end
- -- 切片
- function table.slice(t, begin, offset)
- local res = {}
- -- default values for range
- local n = #t
- begin = begin or 1
- if begin < 1 or begin > n then
- return res
- end
- offset = offset or n
- if offset < 0 then
- offset = n + offset + 1
- elseif offset > n then
- offset = n
- end
- local k = 1
- for i = begin, begin + offset - 1 do
- res[k] = t[i]
- k = k + 1
- end
- return res
- end
- string._htmlspecialchars_set = {}
- string._htmlspecialchars_set["&"] = "&"
- string._htmlspecialchars_set["\""] = """
- string._htmlspecialchars_set["'"] = "'"
- string._htmlspecialchars_set["<"] = "<"
- string._htmlspecialchars_set[">"] = ">"
- --[[--
- 将特殊字符转为 HTML 转义符
- ~~~ lua
- print(string.htmlspecialchars("<ABC>"))
- -- 输出 <ABC>
- ~~~
- @param string input 输入字符串
- @return string 转换结果
- ]]
- function string.htmlspecialchars(input)
- for k, v in pairs(string._htmlspecialchars_set) do
- input = string.gsub(input, k, v)
- end
- return input
- end
- function string.startwith(s,start)
- return string.sub(s,1,string.len(start))==start
- end
- --[[--
- 将 HTML 转义符还原为特殊字符,功能与 string.htmlspecialchars() 正好相反
- ~~~ lua
- print(string.restorehtmlspecialchars("<ABC>"))
- -- 输出 <ABC>
- ~~~
- @param string input 输入字符串
- @return string 转换结果
- ]]
- function string.restorehtmlspecialchars(input)
- for k, v in pairs(string._htmlspecialchars_set) do
- input = string.gsub(input, v, k)
- end
- return input
- end
- --[[--
- 将字符串中的 \n 换行符转换为 HTML 标记
- ~~~ lua
- print(string.nl2br("Hello\nWorld"))
- -- 输出
- -- Hello<br />World
- ~~~
- @param string input 输入字符串
- @return string 转换结果
- ]]
- function string.nl2br(input)
- return string.gsub(input, "\n", "<br />")
- end
- --[[--
- 将字符串中的特殊字符和 \n 换行符转换为 HTML 转移符和标记
- ~~~ lua
- print(string.nl2br("<Hello>\nWorld"))
- -- 输出
- -- <Hello><br />World
- ~~~
- @param string input 输入字符串
- @return string 转换结果
- ]]
- function string.text2html(input)
- input = string.gsub(input, "\t", " ")
- input = string.htmlspecialchars(input)
- input = string.gsub(input, " ", " ")
- input = string.nl2br(input)
- return input
- end
- --[[--
- 用指定字符或字符串分割输入字符串,返回包含分割结果的数组
- ~~~ lua
- local input = "Hello,World"
- local res = string.split(input, ",")
- -- res = {"Hello", "World"}
- local input = "Hello-+-World-+-Quick"
- local res = string.split(input, "-+-")
- -- res = {"Hello", "World", "Quick"}
- ~~~
- @param string input 输入字符串
- @param string delimiter 分割标记字符或字符串
- @return array 包含分割结果的数组
- ]]
- function string.split(input, delimiter)
- input = tostring(input)
- delimiter = tostring(delimiter)
- if (delimiter=='') then return false end
- local pos,arr = 0, {}
- -- for each divider found
- for st,sp in function() return string.find(input, delimiter, pos, true) end do
- table.insert(arr, string.sub(input, pos, st - 1))
- pos = sp + 1
- end
- table.insert(arr, string.sub(input, pos))
- return arr
- end
- --[[--
- 去除输入字符串头部的空白字符,返回结果
- ~~~ lua
- local input = " ABC"
- print(string.ltrim(input))
- -- 输出 ABC,输入字符串前面的两个空格被去掉了
- ~~~
- 空白字符包括:
- - 空格
- - 制表符 \t
- - 换行符 \n
- - 回到行首符 \r
- @param string input 输入字符串
- @return string 结果
- @see string.rtrim, string.trim
- ]]
- function string.ltrim(input)
- return string.gsub(input, "^[ \t\n\r]+", "")
- end
- function string.fromhex(str)
- return (str:gsub('..', function (cc)
- return string.char(tonumber(cc, 16))
- end))
- end
- function string.tohex(str)
- return (str:gsub('.', function (c)
- return string.format('%02X', string.byte(c))
- end))
- end
- --[[--
- 去除输入字符串尾部的空白字符,返回结果
- ~~~ lua
- local input = "ABC "
- print(string.ltrim(input))
- -- 输出 ABC,输入字符串最后的两个空格被去掉了
- ~~~
- @param string input 输入字符串
- @return string 结果
- @see string.ltrim, string.trim
- ]]
- function string.rtrim(input)
- return string.gsub(input, "[ \t\n\r]+$", "")
- end
- --[[--
- 去掉字符串首尾的空白字符,返回结果
- @param string input 输入字符串
- @return string 结果
- @see string.ltrim, string.rtrim
- ]]
- function string.trim(input)
- input = string.gsub(input, "^[ \t\n\r]+", "")
- return string.gsub(input, "[ \t\n\r]+$", "")
- end
- --[[--
- 将字符串的第一个字符转为大写,返回结果
- ~~~ lua
- local input = "hello"
- print(string.ucfirst(input))
- -- 输出 Hello
- ~~~
- @param string input 输入字符串
- @return string 结果
- ]]
- function string.ucfirst(input)
- return string.upper(string.sub(input, 1, 1)) .. string.sub(input, 2)
- end
- local function urlencodechar(char)
- return "%" .. string.format("%02X", string.byte(char))
- end
- --[[--
- 将字符串转换为符合 URL 传递要求的格式,并返回转换结果
- ~~~ lua
- local input = "hello world"
- print(string.urlencode(input))
- -- 输出
- -- hello%20world
- ~~~
- @param string input 输入字符串
- @return string 转换后的结果
- @see string.urldecode
- ]]
- function string.urlencode(input)
- -- convert line endings
- input = string.gsub(tostring(input), "\n", "\r\n")
- -- escape all characters but alphanumeric, '.' and '-'
- input = string.gsub(input, "([^%w%.%- ])", urlencodechar)
- -- convert spaces to "+" symbols
- return string.gsub(input, " ", "+")
- end
- --[[--
- 将 URL 中的特殊字符还原,并返回结果
- ~~~ lua
- local input = "hello%20world"
- print(string.urldecode(input))
- -- 输出
- -- hello world
- ~~~
- @param string input 输入字符串
- @return string 转换后的结果
- @see string.urlencode
- ]]
- function string.urldecode(input)
- input = string.gsub (input, "+", " ")
- input = string.gsub (input, "%%(%x%x)", function(h) return string.char(checknumber(h,16)) end)
- input = string.gsub (input, "\r\n", "\n")
- return input
- end
- --[[--
- 计算 UTF8 字符串的长度,每一个中文算一个字符
- ~~~ lua
- local input = "你好World"
- print(string.utf8len(input))
- -- 输出 7
- ~~~
- @param string input 输入字符串
- @return integer 长度
- ]]
- function string.utf8len(input)
- local len = string.len(input)
- local left = len
- local cnt = 0
- local arr = {0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc}
- while left ~= 0 do
- local tmp = string.byte(input, -left)
- local i = #arr
- while arr[i] do
- if tmp >= arr[i] then
- left = left - i
- break
- end
- i = i - 1
- end
- cnt = cnt + 1
- end
- return cnt
- end
- --[[--
- 将数值格式化为包含千分位分隔符的字符串
- ~~~ lua
- print(string.formatnumberthousands(1924235))
- -- 输出 1,924,235
- ~~~
- @param number num 数值
- @return string 格式化结果
- ]]
- function string.formatnumberthousands(num)
- local formatted = tostring(checknumber(num))
- local k
- while true do
- formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
- if k == 0 then break end
- end
- return formatted
- end
|