utils.lua 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. --- 模块功能:常用工具类接口
  2. -- @module utils
  3. -- @author openLuat
  4. -- @license MIT
  5. -- @copyright openLuat
  6. -- @release 2019.01.05
  7. module(..., package.seeall)
  8. --- 将Lua字符串转成HEX字符串,如"123abc"转为"313233616263"
  9. -- @string str 输入字符串
  10. -- @string[opt=""] separator 输出的16进制字符串分隔符
  11. -- @return hexstring 16进制组成的串
  12. -- @return len 输入的字符串长度
  13. -- @usage
  14. -- string.toHex("\1\2\3") -> "010203" 3
  15. -- string.toHex("123abc") -> "313233616263" 6
  16. -- string.toHex("123abc"," ") -> "31 32 33 61 62 63 " 6
  17. function string.toHex(str, separator)
  18. return str:gsub('.', function(c)
  19. return string.format("%02X" .. (separator or ""), string.byte(c))
  20. end)
  21. end
  22. --- 将HEX字符串转成Lua字符串,如"313233616263"转为"123abc", 函数里加入了过滤分隔符,可以过滤掉大部分分隔符(可参见正则表达式中\s和\p的范围)。
  23. -- @string hex 16进制组成的串
  24. -- @return charstring,字符组成的串
  25. -- @return len,输出字符串的长度
  26. -- @usage
  27. -- string.fromHex("010203") -> "\1\2\3"
  28. -- string.fromHex("313233616263:) -> "123abc"
  29. function string.fromHex(hex)
  30. --滤掉分隔符
  31. local hex = hex:gsub("[%s%p]", ""):upper()
  32. return hex:gsub("%x%x", function(c)
  33. return string.char(tonumber(c, 16))
  34. end)
  35. end
  36. -- 返回字符串tonumber的转义字符串(用来支持超过31位整数的转换)
  37. -- @string str 输入字符串
  38. -- @return str 转换后的lua 二进制字符串
  39. -- @return len 转换了多少个字符
  40. -- @usage
  41. -- string.toValue("123456") -> "\1\2\3\4\5\6" 6
  42. -- string.toValue("123abc") -> "\1\2\3\a\b\c" 6
  43. function string.toValue(str)
  44. return string.fromHex(str:gsub("%x", "0%1"))
  45. end
  46. --- 返回utf8编码字符串的长度
  47. -- @string str utf8编码的字符串,支持中文
  48. -- @return number,返回字符串长度
  49. -- @usage local cnt = string.utf8Len("中国a"),cnt == 3
  50. function string.utf8Len(str)
  51. local _, count = string.gsub(str, "[^\128-\193]", "")
  52. return count
  53. end
  54. --- 返回utf8编码字符串的单个utf8字符的table
  55. -- @string str utf8编码的字符串,支持中文
  56. -- @return table,utf8字符串的table
  57. -- @usage local t = string.utf8ToTable("中国2018")
  58. function string.utf8ToTable(str)
  59. local tab = {}
  60. for uchar in string.gfind(str, "[%z\1-\127\194-\244][\128-\191]*") do
  61. tab[#tab + 1] = uchar
  62. end
  63. return tab
  64. end
  65. --- 返回字符串的 RFC3986 编码
  66. -- @string str 要转换编码的字符串,支持UTF8编码中文
  67. -- @return str, RFC3986 编码的字符串
  68. -- @usage local str = string.rawurlEncode("####133") ,str == "%23%23%23%23133"
  69. -- @usage local str = string.rawurlEncode("中国2018") , str == "%e4%b8%ad%e5%9b%bd2018"
  70. function string.rawurlEncode(str)
  71. local t = str:utf8ToTable()
  72. for i = 1, #t do
  73. if #t[i] == 1 then
  74. t[i] = string.gsub(string.gsub(t[i], "([^%w_%~%.%- ])", function(c) return string.format("%%%02X", string.byte(c)) end), " ", "%%20")
  75. else
  76. t[i] = string.gsub(t[i], ".", function(c) return string.format("%%%02X", string.byte(c)) end)
  77. end
  78. end
  79. return table.concat(t)
  80. end
  81. --- 返回字符串的urlEncode编码
  82. -- @string str 要转换编码的字符串,支持UTF8编码中文
  83. -- @return str,urlEncode编码的字符串
  84. -- @usage local str = string.urlEncode("####133") ,str == "%23%23%23%23133"
  85. -- @usage local str = string.urlEncode("中国2018") , str == "%e4%b8%ad%e5%9b%bd2018"
  86. function string.urlEncode(str)
  87. local t = str:utf8ToTable()
  88. for i = 1, #t do
  89. if #t[i] == 1 then
  90. t[i] = string.gsub(string.gsub(t[i], "([^%w_%*%.%- ])", function(c) return string.format("%%%02X", string.byte(c)) end), " ", "+")
  91. else
  92. t[i] = string.gsub(t[i], ".", function(c) return string.format("%%%02X", string.byte(c)) end)
  93. end
  94. end
  95. return table.concat(t)
  96. end
  97. --- 返回一个迭代器函数,每次调用函数都会返回hash表的排序后的键值对
  98. -- @table t 要排序的hash表
  99. -- @param f 自定义排序函数
  100. -- @return function.
  101. -- @usage test = {a=1,f=9,d=2,c=8,b=5}
  102. -- @usage for name,line in pairsByKeys(test) do print(name,line) end
  103. function table.gsort(t, f)
  104. local a = {}
  105. for n in pairs(t) do a[#a + 1] = n end
  106. table.sort(a, f)
  107. local i = 0
  108. return function()
  109. i = i + 1
  110. return a[i], t[a[i]]
  111. end
  112. end
  113. --- table.concat的增强版,支持嵌套字符串数组
  114. -- @table l 嵌套字符串数组
  115. -- @return string
  116. -- @usage print(table.rconcat({"a",{" nice "}," and ", {{" long "},{" list "}}}))
  117. function table.rconcat(l)
  118. if type(l) ~= "table" then return l end
  119. local res = {}
  120. for i = 1, #l do
  121. res[i] =table.rconcat(l[i])
  122. end
  123. return table.concat(res)
  124. end
  125. --- 返回数字的千位符号格式
  126. -- @number num 数字
  127. -- @return string,千位符号的数字字符串
  128. -- @usage loca s = string.formatNumberThousands(1000) ,s = "1,000"
  129. function string.formatNumberThousands(num)
  130. local k, formatted
  131. formatted = tostring(tonumber(num))
  132. while true do
  133. formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
  134. if k == 0 then break end
  135. end
  136. return formatted
  137. end
  138. --- 按照指定分隔符分割字符串
  139. -- @string str 输入字符串
  140. -- @string delimiter 分隔符
  141. -- @return 分割后的字符串列表
  142. -- @usage "123,456,789":split(',') -> {'123','456','789'}
  143. function string.split(str, delimiter)
  144. local strlist, tmp = {}, string.byte(delimiter)
  145. if delimiter == "" then
  146. for i = 1, #str do strlist[i] = str:sub(i, i) end
  147. else
  148. for substr in string.gmatch(str .. delimiter, "(.-)" .. (((tmp > 96 and tmp < 123) or (tmp > 64 and tmp < 91) or (tmp > 47 and tmp < 58)) and delimiter or "%" .. delimiter)) do
  149. table.insert(strlist, substr)
  150. end
  151. end
  152. return strlist
  153. end
  154. -- 和校验
  155. -- @string str 需要校验的字符串
  156. -- @string number 1为返回1个字节,2为返回2个字节
  157. -- @retrun 返回和校验结果
  158. -- @usage string.checkSum("1234",1)
  159. function string.checkSum(str, num)
  160. assert(type(str) == "string", "The first argument is not a string!")
  161. local sum = 0
  162. for i = 1, #str do
  163. sum = sum + str:sub(i, i):byte()
  164. end
  165. if num == 2 then
  166. return sum % 0x10000
  167. else
  168. return sum % 0x100
  169. end
  170. end
  171. --- 判断文件是否存在
  172. -- @string path 文件全名,例如:"/lua/call.mp3"
  173. -- @return bool,存在为true,不存在为false
  174. -- @usage local ex = io.exists("/lua/call.mp3")
  175. function io.exists(path)
  176. local file = io.open(path, "r")
  177. if file then
  178. io.close(file)
  179. return true
  180. end
  181. return false
  182. end
  183. --- 读取文件中的所有内容
  184. -- @string path 文件全名,例如:"/lua/call.txt"
  185. -- @return string,文件的内容,文件不存在返回nil
  186. -- @usage local c = io.readFile("/lua/call.txt")
  187. function io.readFile(path)
  188. local file = io.open(path, "rb")
  189. if file then
  190. local content = file:read("*a")
  191. io.close(file)
  192. return content
  193. end
  194. end
  195. --- 写入文件指定的内容,默认为覆盖二进制模式
  196. -- @string path 文件全名,例如:"/lua/call.txt"
  197. -- @string content 文件内容
  198. -- @string mode 文件写入模式,支持如下几种(默认"w+b"):
  199. -- "w"或者"w+b":空文件写入模式,如果文件不存在,则新建文件,然后从起始位置开始写入;如果文件存在,则删除已有内容,然后从起始位置开始写入
  200. -- "a"或者"a+b":追加写入模式,如果文件不存在,则新建文件,然后从起始位置开始写入;如果文件存在,则从文件末尾开始追加写入
  201. -- @return boolean result,文件写入结果,true表示写入成功;false表示写入失败
  202. -- @usage local c = io.writeFile("/lua/call.txt","test")
  203. function io.writeFile(path, content, mode)
  204. local mode = mode or "w+b"
  205. local file = io.open(path, mode)
  206. if file then
  207. if file:write(content) == nil then return false end
  208. io.close(file)
  209. return true
  210. else
  211. return false
  212. end
  213. end
  214. --- 将文件路径分解为table信息
  215. -- @string path 文件路径全名,例如:"/lua/call.txt"
  216. -- @return table,{dirname="/lua/",filename="call.txt",basename="call",extname=".txt"}
  217. -- @usage loca p = io.pathInfo("/lua/call.txt")
  218. function io.pathInfo(path)
  219. local pos = string.len(path)
  220. local extpos = pos + 1
  221. while pos > 0 do
  222. local b = string.byte(path, pos)
  223. if b == 46 then -- 46 = char "."
  224. extpos = pos
  225. elseif b == 47 then -- 47 = char "/"
  226. break
  227. end
  228. pos = pos - 1
  229. end
  230. local dirname = string.sub(path, 1, pos)
  231. local filename = string.sub(path, pos + 1)
  232. extpos = extpos - pos
  233. local basename = string.sub(filename, 1, extpos - 1)
  234. local extname = string.sub(filename, extpos)
  235. return {
  236. dirname = dirname,
  237. filename = filename,
  238. basename = basename,
  239. extname = extname
  240. }
  241. end
  242. --- 返回文件大小
  243. -- @string path 文件路径全名,例如:"/lua/call.txt"
  244. -- @return number ,文件大小
  245. -- @usage locan cnt = io.fileSize("/lua/call.txt")
  246. function io.fileSize(path)
  247. local size = 0
  248. local file = io.open(path, "r")
  249. if file then
  250. local current = file:seek()
  251. size = file:seek("end")
  252. file:seek("set", current)
  253. io.close(file)
  254. end
  255. return size
  256. end
  257. --- 返回指定位置读取的字符串
  258. -- @string path 文件路径全名,例如:"/lua/call.txt"
  259. -- @number offset 要读取的指定位置,相对于文件开头的偏移位置
  260. -- @number len 要读取的字节数
  261. -- @return string,返回要读取的数据,读取失败返回nil
  262. function io.readStream(path, offset, len)
  263. local file, str = io.open(path, "r")
  264. if file then
  265. local current = file:seek()
  266. file:seek("set", offset)
  267. str = file:read(len)
  268. file:seek("set", current)
  269. io.close(file)
  270. end
  271. return str
  272. end