Lua中的loadfile,dofile,require使用,最后還有調試


1、loadfile---只編譯,不運行。

  loadfile編譯代碼成中間碼並且返回編譯后的chunk作為一個函數,而不執行代碼;另外loadfile不會拋出錯誤信息而是返回錯誤代號。

  loadstring與loadfile相似,只不過它不是從文件里讀入chunk,而是從一個串中讀入。

2、dofile----編譯、運行。

  dofile,其實首先是利用loadfile進行編譯,然后再運行代碼。我們可以定義dofile如下:

function dofile (filename)
  local f = assert(loadfile(filename))
  return f()
end

如果loadfile失敗assert會拋出錯誤。

完成簡單的功能dofile比較方便,他讀入文件編譯並且執行。然而loadfile更加靈活。在發生錯誤的情況下,loadfile返回nil和錯誤信息,這樣我們就可以自定義錯誤處理。另外,如果我們運行一個文件多次的話,loadfile只需要編譯一次,但可多次運行。dofile卻每次都要編譯。

3、require函數--只加載一次。

  require和dofile有點像,不過又很不一樣,require在第一次加載文件的時候,會執行里面的代碼。

  但是,它和dofile有兩點不同:

  1. require會搜索目錄加載文件
  2. require會判斷是否文件已經加載避免重復加載同一文件。由於上述特征,require在Lua中是加載庫的更好的函數。

require使用的路徑和普通我們看到的路徑還有些區別,我們一般見到的路徑都是一個目錄列表。require的路徑是一個模式列表,每一個模式指明一種由虛文件名(require的參數)轉成實文件名的方法。更明確地說,每一個模式是一個包含可選的問號的文件名。匹配的時候Lua會首先將問號用虛文件名替換,然后看是否有這樣的文件存在。如果不存在繼續用同樣的方法用第二個模式匹配。例如,路徑如下:

?;?.lua;c:\windows\?;/usr/local/lua/?/?.lua

調用require "lili"時會試着打開這些文件:

lili
lili.lua
c:\windows\lili
/usr/local/lua/lili/lili.lua

require關注的問題只有分號(模式之間的分隔符)和問號,其他的信息(目錄分隔符,文件擴展名)在路徑中定義。

為了確定路徑,Lua首先檢查全局變量LUA_PATH是否為一個字符串,如果是則認為這個串就是路徑;否則require檢查環境變量LUA_PATH的值,如果兩個都失敗require使用固定的路徑(典型的"?;?.lua")。

require的另一個功能是避免重復加載同一個文件兩次。Lua保留一張所有已經加載的文件的列表(使用table保存)。如果一個加載的文件在表中存在require簡單的返回;表中保留加載的文件的虛名,而不是實文件名。所以如果你使用不同的虛文件名require同一個文件兩次,將會加載兩次該文件。比如require "foo"和require "foo.lua",路徑為"?;?.lua"將會加載foo.lua兩次。我們也可以通過全局變量_LOADED訪問文件名列表,這樣我們就可以判斷文件是否被加載過;同樣我們也可以使用一點小技巧讓require加載一個文件兩次。比如,require "foo"之后_LOADED["foo"]將不為nil,我們可以將其賦值為nil,require "foo.lua"將會再次加載該文件。

一個路徑中的模式也可以不包含問號而只是一個固定的路徑,比如:

?;?.lua;/usr/local/default.lua

這種情況下,require沒有匹配的時候就會使用這個固定的文件(當然這個固定的路徑必須放在模式列表的最后才有意義)。在require運行一個chunk以前,它定義了一個全局變量_REQUIREDNAME用來保存被required的虛文件的文件名。我們可以通過使用這個技巧擴展require的功能。舉個極端的例子,我們可以把路徑設為"/usr/local/lua/newrequire.lua",這樣以后每次調用require都會運行newrequire.lua,這種情況下可以通過使用_REQUIREDNAME的值去實際加載required的文件。(其實這段我木有懂

 

調試 

另外,如果在Lua中需要處理錯誤,需要使用pcall函數封裝你的代碼。

  假定你想運行一段Lua代碼,這段代碼運行過程中可以捕捉所有的異常和錯誤。

  第一步:將這段代碼封裝在一個函數內

function foo ()
  ...
  if unexpected_condition then error() end
  ...
  print(a[i]) -- potential error: `a' may not be a table
  ...
end

  第二步:使用pcall調用這個函數

if pcall(foo) then
-- no errors while running `foo'
...
else
-- `foo' raised an error: take appropriate actions
...
end

  當然也可以用匿名函數的方式調用pcall:

if pcall(function () ... end) then ...
else ...

pcall在保護模式下調用他的第一個參數並運行,因此可以捕獲所有的異常和錯誤。如果沒有異常和錯誤,pcall返回true和調用返回的任何值;否則返回nil加錯誤信息。
錯誤信息不一定非要是一個字符串(下面的例子是一個table),傳遞給error的任何信息都會被pcall返回:

local status, err = pcall(function () error({code=121}) end)
print(err.code) --> 121

  這種機制提供了我們在Lua中處理異常和錯誤的所需要的全部內容。我們通過error拋出異常,然后通過pcall捕獲他。

雖然你可以使用任何類型的值作為錯誤信息,通常情況下,我們使用字符串來描述遇到的錯誤信息。如果遇到內部錯誤(比如對一個非table的值使用索引下表訪問)Lua將自己產生錯誤信息,否則Lua使用傳遞給error函數的參數作為錯誤信息。不管在什么情況下,Lua都盡可能清楚的描述發生的錯誤。

local status, err = pcall(function () a = 'a'+1 end)
print(err)
--> stdin:1: attempt to perform arithmetic on a string value

local status, err = pcall(function () error("my error") end)
print(err)
--> stdin:1: my error

例子中錯誤信息給出了文件名(stdin)加上行號。
函數error還可以有第二個參數,表示錯誤的運行級別。有了這個參數你就無法抵賴錯誤是別人的了,比如,加入你寫了一個函數用來檢查error是否被正確的調用:

function foo (str)
if type(str) ~= "string" then
  error("string expected")
end
...
end

可能有人這樣調用這個函數:
  foo({x=1})
Lua會指出發生錯誤的是foo而不是error,實際的錯誤是調用error時產生的,為了糾正這個問題修改前面的代碼讓error報告錯誤發生在第二級(你自己的函數是第一級)如下:

function foo (str)
if type(str) ~= "string" then
  error("string expected", 2)
end
...
end

當錯誤發生的時候,我們常常需要更多的錯誤發生相關的信息,而不單單是錯誤發生的位置。至少期望有一個完整的顯示導致錯誤發生的調用棧的tracebacks,當pcall返回錯誤信息的時候他已經釋放了保存錯誤發生情況的棧的信息。因此,如果我們想得到tracebacks我們必須在pcall返回以前獲取。Lua提供了xpcall來實現這個功能,xpcall接受兩個參數:調用函數和錯誤處理函數。當錯誤發生時。Lua會在棧釋放以前調用錯誤處理函數,因此可以使用debug庫收集錯誤相關的信息。有兩個常用的debug處理函數:debug。debug和debug.traceback,前者給出Lua的提示符,你可以自己動手察看錯誤發生時的情況;后者通過traceback創建更多的錯誤信息,后者是控制台解釋器用來構建錯誤信息的函數。你可以在任何時候調用debug.traceback獲取當前運行的traceback信息:

print(debug.traceback())

 

最后想說一下,程序里,有很多 pcall(dofile, "**.lua")的代碼,還有很多xpcall(func)的代碼,真的很方便。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM