functions.lua 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598
  1. --[[
  2. Copyright (c) 2011-2014 chukong-inc.com
  3. Permission is hereby granted, free of charge, to any person obtaining a copy
  4. of this software and associated documentation files (the "Software"), to deal
  5. in the Software without restriction, including without limitation the rights
  6. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. copies of the Software, and to permit persons to whom the Software is
  8. furnished to do so, subject to the following conditions:
  9. The above copyright notice and this permission notice shall be included in
  10. all copies or substantial portions of the Software.
  11. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  12. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  13. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  14. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  15. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  16. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  17. THE SOFTWARE.
  18. ]]
  19. --[[--
  20. 提供一组常用函数,以及对 Lua 标准库的扩展
  21. ]]
  22. --[[--
  23. 输出格式化字符串
  24. ~~~ lua
  25. printf("The value = %d", 100)
  26. ~~~
  27. @param string fmt 输出格式
  28. @param [mixed ...] 更多参数
  29. ]]
  30. function printf(fmt, ...)
  31. print(string.format(tostring(fmt), ...))
  32. end
  33. local skynet = require "skynet"
  34. local cjson = require "cjson"
  35. function table.empty(tlb)
  36. local t = tlb or {}
  37. for k, v in pairs(tlb) do
  38. return false
  39. end
  40. return true
  41. end
  42. function table.array(array)
  43. return setmetatable(array, cjson.empty_array_mt)
  44. end
  45. function table.dumpdebug(value, desciption, nesting)
  46. if type(nesting) ~= "number" then nesting = 3 end
  47. local lookupTable = {}
  48. local result = {}
  49. local function _v(v)
  50. if type(v) == "string" then
  51. v = "\"" .. v .. "\""
  52. end
  53. return tostring(v)
  54. end
  55. local traceback = string.split(debug.traceback("", 2), "\n")
  56. if not ngx then
  57. skynet.error("dump from: " .. string.trim(traceback[3]))
  58. else
  59. ngx.log(ngx.DEBUG,"dump from: " .. string.trim(traceback[3]))
  60. end
  61. local function _dump(value, desciption, indent, nest, keylen)
  62. desciption = desciption or "<var>"
  63. spc = ""
  64. if type(keylen) == "number" then
  65. spc = string.rep(" ", keylen - string.len(_v(desciption)))
  66. end
  67. if type(value) ~= "table" then
  68. result[#result +1 ] = string.format("%s%s%s = %s", indent, _v(desciption), spc, _v(value))
  69. elseif lookupTable[value] then
  70. result[#result +1 ] = string.format("%s%s%s = *REF*", indent, desciption, spc)
  71. else
  72. lookupTable[value] = true
  73. if nest > nesting then
  74. result[#result +1 ] = string.format("%s%s = *MAX NESTING*", indent, desciption)
  75. else
  76. result[#result +1 ] = string.format("%s%s = {", indent, _v(desciption))
  77. local indent2 = indent.." "
  78. local keys = {}
  79. local keylen = 0
  80. local values = {}
  81. for k, v in pairs(value) do
  82. keys[#keys + 1] = k
  83. local vk = _v(k)
  84. local vkl = string.len(vk)
  85. if vkl > keylen then keylen = vkl end
  86. values[k] = v
  87. end
  88. table.sort(keys, function(a, b)
  89. if type(a) == "number" and type(b) == "number" then
  90. return a < b
  91. else
  92. return tostring(a) < tostring(b)
  93. end
  94. end)
  95. for i, k in ipairs(keys) do
  96. _dump(values[k], k, indent2, nest + 1, keylen)
  97. end
  98. result[#result +1] = string.format("%s}", indent)
  99. end
  100. end
  101. end
  102. _dump(value, desciption, "- ", 1)
  103. for i, line in ipairs(result) do
  104. if not ngx then
  105. skynet.error(line)
  106. else
  107. ngx.log(ngx.DEBUG,line)
  108. end
  109. end
  110. end
  111. string.split = function(s, p)
  112. local rt= {}
  113. string.gsub(s, '[^'..p..']+', function(w) table.insert(rt, w) end )
  114. return rt
  115. end
  116. --[[--
  117. 检查并尝试转换为数值,如果无法转换则返回 0
  118. @param mixed value 要检查的值
  119. @param [integer base] 进制,默认为十进制
  120. @return number
  121. ]]
  122. function checknumber(value, base)
  123. return tonumber(value, base) or 0
  124. end
  125. --[[--
  126. 检查并尝试转换为整数,如果无法转换则返回 0
  127. @param mixed value 要检查的值
  128. @return integer
  129. ]]
  130. function checkint(value)
  131. return math.round(checknumber(value))
  132. end
  133. --[[--
  134. 检查并尝试转换为布尔值,除了 nil 和 false,其他任何值都会返回 true
  135. @param mixed value 要检查的值
  136. @return boolean
  137. ]]
  138. function checkbool(value)
  139. return (value ~= nil and value ~= false)
  140. end
  141. --[[--
  142. 检查值是否是一个表格,如果不是则返回一个空表格
  143. @param mixed value 要检查的值
  144. @return table
  145. ]]
  146. function checktable(value)
  147. if type(value) ~= "table" then value = {} end
  148. return value
  149. end
  150. --[[--
  151. 如果表格中指定 key 的值为 nil,或者输入值不是表格,返回 false,否则返回 true
  152. @param table hashtable 要检查的表格
  153. @param mixed key 要检查的键名
  154. @return boolean
  155. ]]
  156. function isset(hashtable, key)
  157. local t = type(hashtable)
  158. return (t == "table" or t == "userdata") and hashtable[key] ~= nil
  159. end
  160. --[[--
  161. 深度克隆一个值
  162. ~~~ lua
  163. -- 下面的代码,t2 是 t1 的引用,修改 t2 的属性时,t1 的内容也会发生变化
  164. local t1 = {a = 1, b = 2}
  165. local t2 = t1
  166. t2.b = 3 -- t1 = {a = 1, b = 3} <-- t1.b 发生变化
  167. -- clone() 返回 t1 的副本,修改 t2 不会影响 t1
  168. local t1 = {a = 1, b = 2}
  169. local t2 = clone(t1)
  170. t2.b = 3 -- t1 = {a = 1, b = 2} <-- t1.b 不受影响
  171. ~~~
  172. @param mixed object 要克隆的值
  173. @return mixed
  174. ]]
  175. function clone(object)
  176. local lookup_table = {}
  177. local function _copy(object)
  178. if type(object) ~= "table" then
  179. return object
  180. elseif lookup_table[object] then
  181. return lookup_table[object]
  182. end
  183. local new_table = {}
  184. lookup_table[object] = new_table
  185. for key, value in pairs(object) do
  186. new_table[_copy(key)] = _copy(value)
  187. end
  188. return setmetatable(new_table, getmetatable(object))
  189. end
  190. return _copy(object)
  191. end
  192. --[[--
  193. 创建一个类
  194. ~~~ lua
  195. -- 定义名为 Shape 的基础类
  196. local Shape = class("Shape")
  197. -- ctor() 是类的构造函数,在调用 Shape.new() 创建 Shape 对象实例时会自动执行
  198. function Shape:ctor(shapeName)
  199. self.shapeName = shapeName
  200. printf("Shape:ctor(%s)", self.shapeName)
  201. end
  202. -- 为 Shape 定义个名为 draw() 的方法
  203. function Shape:draw()
  204. printf("draw %s", self.shapeName)
  205. end
  206. --
  207. -- Circle 是 Shape 的继承类
  208. local Circle = class("Circle", Shape)
  209. function Circle:ctor()
  210. -- 如果继承类覆盖了 ctor() 构造函数,那么必须手动调用父类构造函数
  211. -- 类名.super 可以访问指定类的父类
  212. Circle.super.ctor(self, "circle")
  213. self.radius = 100
  214. end
  215. function Circle:setRadius(radius)
  216. self.radius = radius
  217. end
  218. -- 覆盖父类的同名方法
  219. function Circle:draw()
  220. printf("draw %s, raidus = %0.2f", self.shapeName, self.raidus)
  221. end
  222. --
  223. local Rectangle = class("Rectangle", Shape)
  224. function Rectangle:ctor()
  225. Rectangle.super.ctor(self, "rectangle")
  226. end
  227. --
  228. local circle = Circle.new() -- 输出: Shape:ctor(circle)
  229. circle:setRaidus(200)
  230. circle:draw() -- 输出: draw circle, radius = 200.00
  231. local rectangle = Rectangle.new() -- 输出: Shape:ctor(rectangle)
  232. rectangle:draw() -- 输出: draw rectangle
  233. ~~~
  234. ### 高级用法
  235. class() 除了定义纯 Lua 类之外,还可以从 C++ 对象继承类。
  236. 比如需要创建一个工具栏,并在添加按钮时自动排列已有的按钮,那么我们可以使用如下的代码:
  237. ~~~ lua
  238. -- 从 CCNode 对象派生 Toolbar 类,该类具有 CCNode 的所有属性和行为
  239. local Toolbar = class("Toolbar", function()
  240. return display.newNode() -- 返回一个 CCNode 对象
  241. end)
  242. -- 构造函数
  243. function Toolbar:ctor()
  244. self.buttons = {} -- 用一个 table 来记录所有的按钮
  245. end
  246. -- 添加一个按钮,并且自动设置按钮位置
  247. function Toolbar:addButton(button)
  248. -- 将按钮对象加入 table
  249. self.buttons[#self.buttons + 1] = button
  250. -- 添加按钮对象到 CCNode 中,以便显示该按钮
  251. -- 因为 Toolbar 是从 CCNode 继承的,所以可以使用 addChild() 方法
  252. self:addChild(button)
  253. -- 按照按钮数量,调整所有按钮的位置
  254. local x = 0
  255. for _, button in ipairs(self.buttons) do
  256. button:setPosition(x, 0)
  257. -- 依次排列按钮,每个按钮之间间隔 10 点
  258. x = x + button:getContentSize().width + 10
  259. end
  260. end
  261. ~~~
  262. class() 的这种用法让我们可以在 C++ 对象基础上任意扩展行为。
  263. 既然是继承,自然就可以覆盖 C++ 对象的方法:
  264. ~~~ lua
  265. function Toolbar:setPosition(x, y)
  266. -- 由于在 Toolbar 继承类中覆盖了 CCNode 对象的 setPosition() 方法
  267. -- 所以我们要用以下形式才能调用到 CCNode 原本的 setPosition() 方法
  268. getmetatable(self).setPosition(self, x, y)
  269. printf("x = %0.2f, y = %0.2f", x, y)
  270. end
  271. ~~~
  272. **注意:** Lua 继承类覆盖的方法并不能从 C++ 调用到。也就是说通过 C++ 代码调用这个 CCNode 对象的 setPosition() 方法时,并不会执行我们在 Lua 中定义的 Toolbar:setPosition() 方法。
  273. @param string classname 类名
  274. @param [mixed super] 父类或者创建对象实例的函数
  275. @return table
  276. ]]
  277. function class(classname, super)
  278. local superType = type(super)
  279. local cls
  280. if superType ~= "function" and superType ~= "table" then
  281. superType = nil
  282. super = nil
  283. end
  284. if superType == "function" or (super and super.__ctype == 1) then
  285. -- inherited from native C++ Object
  286. cls = {}
  287. if superType == "table" then
  288. -- copy fields from super
  289. for k,v in pairs(super) do cls[k] = v end
  290. cls.__create = super.__create
  291. cls.super = super
  292. else
  293. cls.__create = super
  294. cls.ctor = function() end
  295. end
  296. cls.__cname = classname
  297. cls.__ctype = 1
  298. function cls.new(...)
  299. local instance = cls.__create(...)
  300. -- copy fields from class to native object
  301. for k,v in pairs(cls) do instance[k] = v end
  302. instance.class = cls
  303. instance:ctor(...)
  304. return instance
  305. end
  306. else
  307. -- inherited from Lua Object
  308. if super then
  309. cls = {}
  310. setmetatable(cls, {__index = super})
  311. cls.super = super
  312. else
  313. cls = {ctor = function() end}
  314. end
  315. cls.__cname = classname
  316. cls.__ctype = 2 -- lua
  317. cls.__index = cls
  318. function cls.new(...)
  319. local instance = setmetatable({}, cls)
  320. instance.class = cls
  321. instance:ctor(...)
  322. return instance
  323. end
  324. end
  325. return cls
  326. end
  327. -- 提供假名以避免和 moonscript 发生冲突
  328. function quick_class(classname, super)
  329. return class(classname, super)
  330. end
  331. --[[--
  332. 如果对象是指定类或其子类的实例,返回 true,否则返回 false
  333. ~~~ lua
  334. local Animal = class("Animal")
  335. local Duck = class("Duck", Animal)
  336. print(iskindof(Duck.new(), "Animal")) -- 输出 true
  337. ~~~
  338. @param mixed obj 要检查的对象
  339. @param string classname 类名
  340. @return boolean
  341. ]]
  342. function iskindof(obj, classname)
  343. local t = type(obj)
  344. local mt
  345. if t == "table" then
  346. mt = getmetatable(obj)
  347. elseif t == "userdata" then
  348. mt = tolua.getpeer(obj)
  349. end
  350. while mt do
  351. if mt.__cname == classname then
  352. return true
  353. end
  354. mt = mt.super
  355. end
  356. return false
  357. end
  358. --[[--
  359. 载入一个模块
  360. import() 与 require() 功能相同,但具有一定程度的自动化特性。
  361. 假设我们有如下的目录结构:
  362. ~~~
  363. app/
  364. app/classes/
  365. app/classes/MyClass.lua
  366. app/classes/MyClassBase.lua
  367. app/classes/data/Data1.lua
  368. app/classes/data/Data2.lua
  369. ~~~
  370. MyClass 中需要载入 MyClassBase 和 MyClassData。如果用 require(),MyClass 内的代码如下:
  371. ~~~ lua
  372. local MyClassBase = require("app.classes.MyClassBase")
  373. local MyClass = class("MyClass", MyClassBase)
  374. local Data1 = require("app.classes.data.Data1")
  375. local Data2 = require("app.classes.data.Data2")
  376. ~~~
  377. 假如我们将 MyClass 及其相关文件换一个目录存放,那么就必须修改 MyClass 中的 require() 命令,否则将找不到模块文件。
  378. 而使用 import(),我们只需要如下写:
  379. ~~~ lua
  380. local MyClassBase = import(".MyClassBase")
  381. local MyClass = class("MyClass", MyClassBase)
  382. local Data1 = import(".data.Data1")
  383. local Data2 = import(".data.Data2")
  384. ~~~
  385. 当在模块名前面有一个"." 时,import() 会从当前模块所在目录中查找其他模块。因此 MyClass 及其相关文件不管存放到什么目录里,我们都不再需要修改 MyClass 中的 import() 命令。这在开发一些重复使用的功能组件时,会非常方便。
  386. 我们可以在模块名前添加多个"." ,这样 import() 会从更上层的目录开始查找模块。
  387. ~
  388. 不过 import() 只有在模块级别调用(也就是没有将 import() 写在任何函数中)时,才能够自动得到当前模块名。如果需要在函数中调用 import(),那么就需要指定当前模块名:
  389. ~~~ lua
  390. # MyClass.lua
  391. # 这里的 ... 是隐藏参数,包含了当前模块的名字,所以最好将这行代码写在模块的第一行
  392. local CURRENT_MODULE_NAME = ...
  393. local function testLoad()
  394. local MyClassBase = import(".MyClassBase", CURRENT_MODULE_NAME)
  395. # 更多代码
  396. end
  397. ~~~
  398. @param string moduleName 要载入的模块的名字
  399. @param [string currentModuleName] 当前模块名
  400. @return module
  401. ]]
  402. function import(moduleName, currentModuleName)
  403. local currentModuleNameParts
  404. local moduleFullName = moduleName
  405. local offset = 1
  406. while true do
  407. if string.byte(moduleName, offset) ~= 46 then -- .
  408. moduleFullName = string.sub(moduleName, offset)
  409. if currentModuleNameParts and #currentModuleNameParts > 0 then
  410. moduleFullName = table.concat(currentModuleNameParts, ".") .. "." .. moduleFullName
  411. end
  412. break
  413. end
  414. offset = offset + 1
  415. if not currentModuleNameParts then
  416. if not currentModuleName then
  417. local n,v = debug.getlocal(3, 1)
  418. currentModuleName = v
  419. end
  420. currentModuleNameParts = string.split(currentModuleName, ".")
  421. end
  422. table.remove(currentModuleNameParts, #currentModuleNameParts)
  423. end
  424. return require(moduleFullName)
  425. end
  426. --[[--
  427. 将 Lua 对象及其方法包装为一个匿名函数
  428. 在 quick-cocos2d-x 中,许多功能需要传入一个 Lua 函数做参数,然后在特定事件发生时就会调用传入的函数。例如触摸事件、帧事件等等。
  429. ~~~ lua
  430. local MyScene = class("MyScene", function()
  431. return display.newScene("MyScene")
  432. end)
  433. function MyScene:ctor()
  434. self.frameTimeCount = 0
  435. -- 注册帧事件
  436. self:addEventListener(cc.ENTER_FRAME_EVENT, self.onEnterFrame)
  437. end
  438. function MyScene:onEnterFrame(dt)
  439. self.frameTimeCount = self.frameTimeCount + dt
  440. end
  441. ~~~
  442. 上述代码执行时将出错,报告"Invalid self" ,这就是因为 C++ 无法识别 Lua 对象方法。因此在调用我们传入的 self.onEnterFrame 方法时没有提供正确的参数。
  443. 要让上述的代码正常工作,就需要使用 handler() 进行一下包装:
  444. ~~~ lua
  445. function MyScene:ctor()
  446. self.frameTimeCount = 0
  447. -- 注册帧事件
  448. self:addEventListener(cc.ENTER_FRAME_EVENT, handler(self, self.onEnterFrame))
  449. end
  450. ~~~
  451. 实际上,除了 C++ 回调 Lua 函数之外,在其他所有需要回调的地方都可以使用 handler()。
  452. @param mixed obj Lua 对象
  453. @param function method 对象方法
  454. @return function
  455. ]]
  456. function handler(obj, method)
  457. return function(...)
  458. return method(obj, ...)
  459. end
  460. end
  461. --[[--
  462. 根据系统时间初始化随机数种子,让后续的 math.random() 返回更随机的值
  463. ]]
  464. function math.newrandomseed()
  465. local ok, socket = pcall(function()
  466. return require("socket")
  467. end)
  468. if ok then
  469. -- 如果集成了 socket 模块,则使用 socket.gettime() 获取随机数种子
  470. math.randomseed(socket.gettime() * 1000)
  471. else
  472. math.randomseed(os.time())
  473. end
  474. math.random()
  475. math.random()
  476. math.random()
  477. math.random()
  478. end
  479. --[[--
  480. 对数值进行四舍五入,如果不是数值则返回 0
  481. @param number value 输入值
  482. @return number
  483. ]]
  484. function math.round(value)
  485. return math.floor(value + 0.5)
  486. end
  487. function math.angle2radian(angle)
  488. return angle*math.pi/180
  489. end
  490. function math.radian2angle(radian)
  491. return radian/math.pi*180
  492. end
  493. --[[--
  494. 检查指定的文件或目录是否存在,如果存在返回 true,否则返回 false
  495. 可以使用 CCFileUtils:fullPathForFilename() 函数查找特定文件的完整路径,例如:
  496. ~~~ lua
  497. local path = CCFileUtils:sharedFileUtils():fullPathForFilename("gamedata.txt")
  498. if io.exists(path) then
  499. ....
  500. end
  501. ~~~
  502. @param string path 要检查的文件或目录的完全路径
  503. @return boolean
  504. ]]
  505. function io.exists(path)
  506. local file = io.open(path, "r")
  507. if file then
  508. io.close(file)
  509. return true
  510. end
  511. return false
  512. end
  513. --[[--
  514. 读取文件内容,返回包含文件内容的字符串,如果失败返回 nil
  515. io.readfile() 会一次性读取整个文件的内容,并返回一个字符串,因此该函数不适宜读取太大的文件。
  516. @param string path 文件完全路径
  517. @return string
  518. ]]
  519. function io.readfile(path)
  520. local file = io.open(path, "r")
  521. if file then
  522. local content = file:read("*a")
  523. io.close(file)
  524. return content
  525. end
  526. return nil
  527. end
  528. --[[--
  529. 以字符串内容写入文件,成功返回 true,失败返回 false
  530. "mode 写入模式" 参数决定 io.writefile() 如何写入内容,可用的值如下:
  531. - "w+" : 覆盖文件已有内容,如果文件不存在则创建新文件
  532. - "a+" : 追加内容到文件尾部,如果文件不存在则创建文件
  533. 此外,还可以在 "写入模式" 参数最后追加字符 "b" ,表示以二进制方式写入数据,这样可以避免内容写入不完整。
  534. **Android 特别提示:** 在 Android 平台上,文件只能写入存储卡所在路径,assets 和 data 等目录都是无法写入的。
  535. @param string path 文件完全路径
  536. @param string content 要写入的内容
  537. @param [string mode] 写入模式,默认值为 "w+b"
  538. @return boolean
  539. ]]
  540. function io.writefile(path, content, mode)
  541. mode = mode or "w+b"
  542. local file = io.open(path, mode)
  543. if file then
  544. if file:write(content) == nil then return false end
  545. io.close(file)
  546. return true
  547. else
  548. return false
  549. end
  550. end
  551. --[[--
  552. 拆分一个路径字符串,返回组成路径的各个部分
  553. ~~~ lua
  554. local pathinfo = io.pathinfo("/var/app/test/abc.png")
  555. -- 结果:
  556. -- pathinfo.dirname = "/var/app/test/"
  557. -- pathinfo.filename = "abc.png"
  558. -- pathinfo.basename = "abc"
  559. -- pathinfo.extname = ".png"
  560. ~~~
  561. @param string path 要分拆的路径字符串
  562. @return table
  563. ]]
  564. function io.pathinfo(path)
  565. local pos = string.len(path)
  566. local extpos = pos + 1
  567. while pos > 0 do
  568. local b = string.byte(path, pos)
  569. if b == 46 then -- 46 = char "."
  570. extpos = pos
  571. elseif b == 47 then -- 47 = char "/"
  572. break
  573. end
  574. pos = pos - 1
  575. end
  576. local dirname = string.sub(path, 1, pos)
  577. local filename = string.sub(path, pos + 1)
  578. extpos = extpos - pos
  579. local basename = string.sub(filename, 1, extpos - 1)
  580. local extname = string.sub(filename, extpos)
  581. return {
  582. dirname = dirname,
  583. filename = filename,
  584. basename = basename,
  585. extname = extname
  586. }
  587. end
  588. --[[--
  589. 返回指定文件的大小,如果失败返回 false
  590. @param string path 文件完全路径
  591. @return integer
  592. ]]
  593. function io.filesize(path)
  594. local size = false
  595. local file = io.open(path, "r")
  596. if file then
  597. local current = file:seek()
  598. size = file:seek("end")
  599. file:seek("set", current)
  600. io.close(file)
  601. end
  602. return size
  603. end
  604. --[[--
  605. 计算表格包含的字段数量
  606. Lua table 的 "#" 操作只对依次排序的数值下标数组有效,table.nums() 则计算 table 中所有不为 nil 的值的个数。
  607. @param table t 要检查的表格
  608. @return integer
  609. ]]
  610. function table.nums(t)
  611. local count = 0
  612. for k, v in pairs(t) do
  613. count = count + 1
  614. end
  615. return count
  616. end
  617. --[[--
  618. 返回指定表格中的所有键
  619. ~~~ lua
  620. local hashtable = {a = 1, b = 2, c = 3}
  621. local keys = table.keys(hashtable)
  622. -- keys = {"a", "b", "c"}
  623. ~~~
  624. @param table hashtable 要检查的表格
  625. @return table
  626. ]]
  627. function table.keys(hashtable)
  628. local keys = {}
  629. for k, v in pairs(hashtable) do
  630. keys[#keys + 1] = k
  631. end
  632. return keys
  633. end
  634. --[[--
  635. 返回指定表格中的所有值
  636. ~~~ lua
  637. local hashtable = {a = 1, b = 2, c = 3}
  638. local values = table.values(hashtable)
  639. -- values = {1, 2, 3}
  640. ~~~
  641. @param table hashtable 要检查的表格
  642. @return table
  643. ]]
  644. function table.values(hashtable)
  645. local values = {}
  646. for k, v in pairs(hashtable) do
  647. values[#values + 1] = v
  648. end
  649. return values
  650. end
  651. --[[--
  652. 将来源表格中所有键及其值复制到目标表格对象中,如果存在同名键,则覆盖其值
  653. ~~~ lua
  654. local dest = {a = 1, b = 2}
  655. local src = {c = 3, d = 4}
  656. table.merge(dest, src)
  657. -- dest = {a = 1, b = 2, c = 3, d = 4}
  658. ~~~
  659. @param table dest 目标表格
  660. @param table src 来源表格
  661. ]]
  662. function table.merge(dest, src)
  663. for k, v in pairs(src) do
  664. dest[k] = v
  665. end
  666. end
  667. --[[--
  668. 在目标表格的指定位置插入来源表格,如果没有指定位置则连接两个表格
  669. ~~~ lua
  670. local dest = {1, 2, 3}
  671. local src = {4, 5, 6}
  672. table.insertto(dest, src)
  673. -- dest = {1, 2, 3, 4, 5, 6}
  674. dest = {1, 2, 3}
  675. table.insertto(dest, src, 5)
  676. -- dest = {1, 2, 3, nil, 4, 5, 6}
  677. ~~~
  678. @param table dest 目标表格
  679. @param table src 来源表格
  680. @param [integer begin] 插入位置
  681. ]]
  682. function table.insertto(dest, src, begin)
  683. begin = checkint(begin)
  684. if begin <= 0 then
  685. begin = #dest + 1
  686. end
  687. local len = #src
  688. for i = 0, len - 1 do
  689. dest[i + begin] = src[i + 1]
  690. end
  691. end
  692. --[[
  693. 从表格中查找指定值,返回其索引,如果没找到返回 false
  694. ~~~ lua
  695. local array = {"a", "b", "c"}
  696. print(table.indexof(array, "b")) -- 输出 2
  697. ~~~
  698. @param table array 表格
  699. @param mixed value 要查找的值
  700. @param [integer begin] 起始索引值
  701. @return integer
  702. ]]
  703. function table.indexof(array, value, begin)
  704. for i = begin or 1, #array do
  705. if array[i] == value then return i end
  706. end
  707. return false
  708. end
  709. --[[--
  710. 从表格中查找指定值,返回其 key,如果没找到返回 nil
  711. ~~~ lua
  712. local hashtable = {name = "dualface", comp = "chukong"}
  713. print(table.keyof(hashtable, "chukong")) -- 输出 comp
  714. ~~~
  715. @param table hashtable 表格
  716. @param mixed value 要查找的值
  717. @return string 该值对应的 key
  718. ]]
  719. function table.keyof(hashtable, value)
  720. for k, v in pairs(hashtable) do
  721. if v == value then return k end
  722. end
  723. return nil
  724. end
  725. --[[--
  726. 从表格中删除指定值,返回删除的值的个数
  727. ~~~ lua
  728. local array = {"a", "b", "c", "c"}
  729. print(table.removebyvalue(array, "c", true)) -- 输出 2
  730. ~~~
  731. @param table array 表格
  732. @param mixed value 要删除的值
  733. @param [boolean removeall] 是否删除所有相同的值
  734. @return integer
  735. ]]
  736. function table.removebyvalue(array, value, removeall)
  737. local c, i, max = 0, 1, #array
  738. while i <= max do
  739. if array[i] == value then
  740. table.remove(array, i)
  741. c = c + 1
  742. i = i - 1
  743. max = max - 1
  744. if not removeall then break end
  745. end
  746. i = i + 1
  747. end
  748. return c
  749. end
  750. --[[--
  751. 对表格中每一个值执行一次指定的函数,并用函数返回值更新表格内容
  752. ~~~ lua
  753. local t = {name = "dualface", comp = "chukong"}
  754. table.map(t, function(v, k)
  755. -- 在每一个值前后添加括号
  756. return "[" .. v .. "]"
  757. end)
  758. -- 输出修改后的表格内容
  759. for k, v in pairs(t) do
  760. print(k, v)
  761. end
  762. -- 输出
  763. -- name [dualface]
  764. -- comp [chukong]
  765. ~~~
  766. fn 参数指定的函数具有两个参数,并且返回一个值。原型如下:
  767. ~~~ lua
  768. function map_function(value, key)
  769. return value
  770. end
  771. ~~~
  772. @param table t 表格
  773. @param function fn 函数
  774. ]]
  775. function table.map(t, fn)
  776. for k, v in pairs(t) do
  777. t[k] = fn(v, k)
  778. end
  779. end
  780. --[[--
  781. 对表格中每一个值执行一次指定的函数,但不改变表格内容
  782. ~~~ lua
  783. local t = {name = "dualface", comp = "chukong"}
  784. table.walk(t, function(v, k)
  785. -- 输出每一个值
  786. print(v)
  787. end)
  788. ~~~
  789. fn 参数指定的函数具有两个参数,没有返回值。原型如下:
  790. ~~~ lua
  791. function map_function(value, key)
  792. end
  793. ~~~
  794. @param table t 表格
  795. @param function fn 函数
  796. ]]
  797. function table.walk(t, fn)
  798. for k,v in pairs(t) do
  799. fn(v, k)
  800. end
  801. end
  802. --[[--
  803. 对表格中每一个值执行一次指定的函数,如果该函数返回 false,则对应的值会从表格中删除
  804. ~~~ lua
  805. local t = {name = "dualface", comp = "chukong"}
  806. table.filter(t, function(v, k)
  807. return v ~= "dualface" -- 当值等于 dualface 时过滤掉该值
  808. end)
  809. -- 输出修改后的表格内容
  810. for k, v in pairs(t) do
  811. print(k, v)
  812. end
  813. -- 输出
  814. -- comp chukong
  815. ~~~
  816. fn 参数指定的函数具有两个参数,并且返回一个 boolean 值。原型如下:
  817. ~~~ lua
  818. function map_function(value, key)
  819. return true or false
  820. end
  821. ~~~
  822. @param table t 表格
  823. @param function fn 函数
  824. ]]
  825. function table.filter(t, fn)
  826. for k, v in pairs(t) do
  827. if not fn(v, k) then t[k] = nil end
  828. end
  829. end
  830. --[[--
  831. 遍历表格,确保其中的值唯一
  832. ~~~ lua
  833. local t = {"a", "a", "b", "c"} -- 重复的 a 会被过滤掉
  834. local n = table.unique(t)
  835. for k, v in pairs(n) do
  836. print(v)
  837. end
  838. -- 输出
  839. -- a
  840. -- b
  841. -- c
  842. ~~~
  843. @param table t 表格
  844. @return table 包含所有唯一值的新表格
  845. ]]
  846. function table.unique(t)
  847. local check = {}
  848. local n = {}
  849. for k, v in pairs(t) do
  850. if not check[v] then
  851. n[k] = v
  852. check[v] = true
  853. end
  854. end
  855. return n
  856. end
  857. -- 随机洗牌
  858. function table.shuffle(t)
  859. local n = #t
  860. while n >= 2 do
  861. -- n is now the last pertinent index
  862. local k = math.random(n) -- 1 <= k <= n
  863. -- Quick swap
  864. t[n], t[k] = t[k], t[n]
  865. n = n - 1
  866. end
  867. return t
  868. end
  869. -- 切片
  870. function table.slice(t, begin, offset)
  871. local res = {}
  872. -- default values for range
  873. local n = #t
  874. begin = begin or 1
  875. if begin < 1 or begin > n then
  876. return res
  877. end
  878. offset = offset or n
  879. if offset < 0 then
  880. offset = n + offset + 1
  881. elseif offset > n then
  882. offset = n
  883. end
  884. local k = 1
  885. for i = begin, begin + offset - 1 do
  886. res[k] = t[i]
  887. k = k + 1
  888. end
  889. return res
  890. end
  891. string._htmlspecialchars_set = {}
  892. string._htmlspecialchars_set["&"] = "&amp;"
  893. string._htmlspecialchars_set["\""] = "&quot;"
  894. string._htmlspecialchars_set["'"] = "&#039;"
  895. string._htmlspecialchars_set["<"] = "&lt;"
  896. string._htmlspecialchars_set[">"] = "&gt;"
  897. --[[--
  898. 将特殊字符转为 HTML 转义符
  899. ~~~ lua
  900. print(string.htmlspecialchars("<ABC>"))
  901. -- 输出 &lt;ABC&gt;
  902. ~~~
  903. @param string input 输入字符串
  904. @return string 转换结果
  905. ]]
  906. function string.htmlspecialchars(input)
  907. for k, v in pairs(string._htmlspecialchars_set) do
  908. input = string.gsub(input, k, v)
  909. end
  910. return input
  911. end
  912. function string.startwith(s,start)
  913. return string.sub(s,1,string.len(start))==start
  914. end
  915. --[[--
  916. 将 HTML 转义符还原为特殊字符,功能与 string.htmlspecialchars() 正好相反
  917. ~~~ lua
  918. print(string.restorehtmlspecialchars("&lt;ABC&gt;"))
  919. -- 输出 <ABC>
  920. ~~~
  921. @param string input 输入字符串
  922. @return string 转换结果
  923. ]]
  924. function string.restorehtmlspecialchars(input)
  925. for k, v in pairs(string._htmlspecialchars_set) do
  926. input = string.gsub(input, v, k)
  927. end
  928. return input
  929. end
  930. --[[--
  931. 将字符串中的 \n 换行符转换为 HTML 标记
  932. ~~~ lua
  933. print(string.nl2br("Hello\nWorld"))
  934. -- 输出
  935. -- Hello<br />World
  936. ~~~
  937. @param string input 输入字符串
  938. @return string 转换结果
  939. ]]
  940. function string.nl2br(input)
  941. return string.gsub(input, "\n", "<br />")
  942. end
  943. --[[--
  944. 将字符串中的特殊字符和 \n 换行符转换为 HTML 转移符和标记
  945. ~~~ lua
  946. print(string.nl2br("<Hello>\nWorld"))
  947. -- 输出
  948. -- &lt;Hello&gt;<br />World
  949. ~~~
  950. @param string input 输入字符串
  951. @return string 转换结果
  952. ]]
  953. function string.text2html(input)
  954. input = string.gsub(input, "\t", " ")
  955. input = string.htmlspecialchars(input)
  956. input = string.gsub(input, " ", "&nbsp;")
  957. input = string.nl2br(input)
  958. return input
  959. end
  960. --[[--
  961. 用指定字符或字符串分割输入字符串,返回包含分割结果的数组
  962. ~~~ lua
  963. local input = "Hello,World"
  964. local res = string.split(input, ",")
  965. -- res = {"Hello", "World"}
  966. local input = "Hello-+-World-+-Quick"
  967. local res = string.split(input, "-+-")
  968. -- res = {"Hello", "World", "Quick"}
  969. ~~~
  970. @param string input 输入字符串
  971. @param string delimiter 分割标记字符或字符串
  972. @return array 包含分割结果的数组
  973. ]]
  974. function string.split(input, delimiter)
  975. input = tostring(input)
  976. delimiter = tostring(delimiter)
  977. if (delimiter=='') then return false end
  978. local pos,arr = 0, {}
  979. -- for each divider found
  980. for st,sp in function() return string.find(input, delimiter, pos, true) end do
  981. table.insert(arr, string.sub(input, pos, st - 1))
  982. pos = sp + 1
  983. end
  984. table.insert(arr, string.sub(input, pos))
  985. return arr
  986. end
  987. --[[--
  988. 去除输入字符串头部的空白字符,返回结果
  989. ~~~ lua
  990. local input = " ABC"
  991. print(string.ltrim(input))
  992. -- 输出 ABC,输入字符串前面的两个空格被去掉了
  993. ~~~
  994. 空白字符包括:
  995. - 空格
  996. - 制表符 \t
  997. - 换行符 \n
  998. - 回到行首符 \r
  999. @param string input 输入字符串
  1000. @return string 结果
  1001. @see string.rtrim, string.trim
  1002. ]]
  1003. function string.ltrim(input)
  1004. return string.gsub(input, "^[ \t\n\r]+", "")
  1005. end
  1006. function string.fromhex(str)
  1007. return (str:gsub('..', function (cc)
  1008. return string.char(tonumber(cc, 16))
  1009. end))
  1010. end
  1011. function string.tohex(str)
  1012. return (str:gsub('.', function (c)
  1013. return string.format('%02X', string.byte(c))
  1014. end))
  1015. end
  1016. --[[--
  1017. 去除输入字符串尾部的空白字符,返回结果
  1018. ~~~ lua
  1019. local input = "ABC "
  1020. print(string.ltrim(input))
  1021. -- 输出 ABC,输入字符串最后的两个空格被去掉了
  1022. ~~~
  1023. @param string input 输入字符串
  1024. @return string 结果
  1025. @see string.ltrim, string.trim
  1026. ]]
  1027. function string.rtrim(input)
  1028. return string.gsub(input, "[ \t\n\r]+$", "")
  1029. end
  1030. --[[--
  1031. 去掉字符串首尾的空白字符,返回结果
  1032. @param string input 输入字符串
  1033. @return string 结果
  1034. @see string.ltrim, string.rtrim
  1035. ]]
  1036. function string.trim(input)
  1037. input = string.gsub(input, "^[ \t\n\r]+", "")
  1038. return string.gsub(input, "[ \t\n\r]+$", "")
  1039. end
  1040. --[[--
  1041. 将字符串的第一个字符转为大写,返回结果
  1042. ~~~ lua
  1043. local input = "hello"
  1044. print(string.ucfirst(input))
  1045. -- 输出 Hello
  1046. ~~~
  1047. @param string input 输入字符串
  1048. @return string 结果
  1049. ]]
  1050. function string.ucfirst(input)
  1051. return string.upper(string.sub(input, 1, 1)) .. string.sub(input, 2)
  1052. end
  1053. local function urlencodechar(char)
  1054. return "%" .. string.format("%02X", string.byte(char))
  1055. end
  1056. --[[--
  1057. 将字符串转换为符合 URL 传递要求的格式,并返回转换结果
  1058. ~~~ lua
  1059. local input = "hello world"
  1060. print(string.urlencode(input))
  1061. -- 输出
  1062. -- hello%20world
  1063. ~~~
  1064. @param string input 输入字符串
  1065. @return string 转换后的结果
  1066. @see string.urldecode
  1067. ]]
  1068. function string.urlencode(input)
  1069. -- convert line endings
  1070. input = string.gsub(tostring(input), "\n", "\r\n")
  1071. -- escape all characters but alphanumeric, '.' and '-'
  1072. input = string.gsub(input, "([^%w%.%- ])", urlencodechar)
  1073. -- convert spaces to "+" symbols
  1074. return string.gsub(input, " ", "+")
  1075. end
  1076. --[[--
  1077. 将 URL 中的特殊字符还原,并返回结果
  1078. ~~~ lua
  1079. local input = "hello%20world"
  1080. print(string.urldecode(input))
  1081. -- 输出
  1082. -- hello world
  1083. ~~~
  1084. @param string input 输入字符串
  1085. @return string 转换后的结果
  1086. @see string.urlencode
  1087. ]]
  1088. function string.urldecode(input)
  1089. input = string.gsub (input, "+", " ")
  1090. input = string.gsub (input, "%%(%x%x)", function(h) return string.char(checknumber(h,16)) end)
  1091. input = string.gsub (input, "\r\n", "\n")
  1092. return input
  1093. end
  1094. --[[--
  1095. 计算 UTF8 字符串的长度,每一个中文算一个字符
  1096. ~~~ lua
  1097. local input = "你好World"
  1098. print(string.utf8len(input))
  1099. -- 输出 7
  1100. ~~~
  1101. @param string input 输入字符串
  1102. @return integer 长度
  1103. ]]
  1104. function string.utf8len(input)
  1105. local len = string.len(input)
  1106. local left = len
  1107. local cnt = 0
  1108. local arr = {0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc}
  1109. while left ~= 0 do
  1110. local tmp = string.byte(input, -left)
  1111. local i = #arr
  1112. while arr[i] do
  1113. if tmp >= arr[i] then
  1114. left = left - i
  1115. break
  1116. end
  1117. i = i - 1
  1118. end
  1119. cnt = cnt + 1
  1120. end
  1121. return cnt
  1122. end
  1123. --[[--
  1124. 将数值格式化为包含千分位分隔符的字符串
  1125. ~~~ lua
  1126. print(string.formatnumberthousands(1924235))
  1127. -- 输出 1,924,235
  1128. ~~~
  1129. @param number num 数值
  1130. @return string 格式化结果
  1131. ]]
  1132. function string.formatnumberthousands(num)
  1133. local formatted = tostring(checknumber(num))
  1134. local k
  1135. while true do
  1136. formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
  1137. if k == 0 then break end
  1138. end
  1139. return formatted
  1140. end