quick-Cocos2d/framework/functions.lua(提供了我們在Lua中創建一個自定義類的快捷方法class、克隆等功能)


提供一組常用函數,以及對 Lua 標準庫的擴展






輸出格式化字符串
printf("The value = %d", 100)
@param string fmt 輸出格式
@param [mixed ...] 更多參數

function printf(fmt, ...)
    print(string.format(tostring(fmt), ...))
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





創建一個類

-- 定義名爲 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


string._htmlspecialchars_set = {}
string._htmlspecialchars_set["&"] = "&amp;"
string._htmlspecialchars_set["\""] = "&quot;"
string._htmlspecialchars_set["'"] = "&#039;"
string._htmlspecialchars_set["<"] = "&lt;"
string._htmlspecialchars_set[">"] = "&gt;"


--[[--


將特殊字符轉爲 HTML 轉義符


~~~ lua


print(string.htmlspecialchars("<ABC>"))
-- 輸出 &lt;ABC&gt;


~~~


@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


--[[--


將 HTML 轉義符還原爲特殊字符,功能與 string.htmlspecialchars() 正好相反


~~~ lua


print(string.restorehtmlspecialchars("&lt;ABC&gt;"))
-- 輸出 <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"))
-- 輸出
-- &lt;Hello&gt;<br />World


~~~


@param string input 輸入字符串


@return string 轉換結果


]]
function string.text2html(input)
    input = string.gsub(input, "\t", "    ")
    input = string.htmlspecialchars(input)
    input = string.gsub(input, " ", "&nbsp;")
    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


--[[--


去除輸入字符串尾部的空白字符,返回結果


~~~ 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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章