I denna lektion lär du dig hur du kan hantera fel, undantag och felsöka dina Lua-program.
När du skriver kod kommer du oundvikligen att stöta på fel. Det finns flera typer av fel i programmering:
I Lua är felhantering viktig för att skapa robusta program som inte kraschar vid oväntade situationer.
Du kan medvetet generera ett fel i Lua med funktionen error():
function divide(a, b)
if b == 0 then
error("Division med noll är inte tillåtet")
end
return a / b
end
-- Använda funktionen
print(divide(10, 2)) -- Fungerar: 5
print(divide(10, 0)) -- Genererar ett fel: "Division med noll är inte tillåtet"
Du kan även ange en nivå som andra parameter till error() för att påverka var i anropsstacken felet visas:
function validateAge(age)
if age < 0 then
error("Ålder kan inte vara negativ", 2) -- Nivå 2 pekar på den som anropar funktionen
end
return true
end
För att förhindra att ditt program kraschar när fel uppstår kan du använda pcall (protected call):
-- Försöker köra en funktion som kan generera fel
local success, result = pcall(divide, 10, 0)
if success then
print("Resultatet är: " .. result)
else
print("Ett fel uppstod: " .. result)
end
pcall returnerar två värden:
Detta är liknande try/catch i andra språk:
-- Använda pcall för att fånga fel vid filöppning
local function readFile(filename)
local file = assert(io.open(filename, "r"))
local content = file:read("*all")
file:close()
return content
end
local success, content = pcall(readFile, "finns_inte.txt")
if success then
print("Filinnehåll: " .. content)
else
print("Kunde inte läsa filen: " .. content)
end
För mer kontroll över felhanteringen kan du använda xpcall, som låter dig ange en felhanteringsfunktion:
-- Definiera en felhanteringsfunktion som visar felmeddelande och stack trace
local function errorHandler(err)
print("ERROR: " .. tostring(err))
print("Stack trace:")
print(debug.traceback("", 2)) -- Visa stack trace, hoppa över denna funktion
return "Felhantering slutförd"
end
-- Funktionen som kan generera fel
local function riskyFunction(n)
print("Startar riskfylld funktion...")
assert(type(n) == "number", "Parameter måste vara ett nummer")
print("n = " .. n)
print(n / 0) -- Detta genererar ett fel
print("Detta körs aldrig") -- Kod efter fel körs aldrig
end
-- Använd xpcall med vår felhanterare
local success, result = xpcall(riskyFunction, errorHandler, 10)
print("xpcall resultat: " .. tostring(success))
print("Returvärde: " .. tostring(result))
debug.traceback() kräver debug-biblioteket som kanske inte är tillgängligt i alla Lua-miljöer.
assert är en praktisk funktion för att kontrollera villkor och generera fel om villkoret inte uppfylls:
-- Verifierar att ett villkor är sant, annars genererar det ett fel
function sqrt(x)
assert(x >= 0, "Kan inte beräkna roten ur ett negativt tal")
return x^0.5
end
-- Exempel på användning
print(sqrt(16)) -- 4
print(sqrt(-1)) -- Fel: "Kan inte beräkna roten ur ett negativt tal"
assert tar två parametrar:
Dette är ett enkelt sätt att validera indata och parametrar:
function processUser(user)
assert(type(user) == "table", "User måste vara en tabell")
assert(user.name, "User saknar namnfält")
assert(type(user.age) == "number", "User.age måste vara ett nummer")
assert(user.age >= 0, "User.age kan inte vara negativ")
-- Om alla assertions passerar, fortsätt med funktionen
print("Bearbetar användare: " .. user.name)
end
När du behöver felsöka Lua-kod finns det flera tillvägagångssätt:
Den enklaste formen av debugging är att lägga till print-uttalanden för att se värden och programmets flöde:
function komplexFunktion(a, b)
print("komplexFunktion startad med a=" .. a .. ", b=" .. b)
local resultat = a * b
print("Efter multiplikation: " .. resultat)
if resultat > 100 then
print("Resultat > 100, ökar med 50")
resultat = resultat + 50
else
print("Resultat <= 100, minskar med 10")
resultat = resultat - 10
end
print("Slutligt resultat: " .. resultat)
return resultat
end
Lua har ett inbyggt debug-bibliotek för mer avancerad felsökning:
-- Visa lokal variabelinformation
function debugLocals()
local variables = {}
local i = 1
while true do
local name, value = debug.getlocal(2, i)
if not name then break end
variables[name] = value
i = i + 1
end
return variables
end
function testFunction()
local a = 10
local b = 20
local c = "test"
local vars = debugLocals()
for name, value in pairs(vars) do
print(name .. " = " .. tostring(value))
end
end
testFunction()
När fel uppstår kan du generera en stack trace för att se var i koden felet inträffade:
function skrivStackTrace()
print("Stack trace:")
print(debug.traceback())
end
function funktionA()
print("I funktionA")
funktionB()
end
function funktionB()
print("I funktionB")
funktionC()
end
function funktionC()
print("I funktionC")
skrivStackTrace()
end
funktionA()
-- Exempel på bra felhantering med resursfrigivning
function readAndProcessFile(filename)
-- Validera indata
assert(type(filename) == "string", "Filnamnet måste vara en sträng")
-- Försök öppna filen med pcall
local success, file = pcall(io.open, filename, "r")
if not success then
return nil, "Kunde inte öppna filen: " .. file
end
-- Använd pcall för att läsa filen och fånga eventuella fel
local success, content = pcall(function()
local data = file:read("*all")
file:close() -- Stäng filen när vi är klara
return data
end)
-- Om fel uppstod vid läsning, se till att filen fortfarande stängs
if not success then
pcall(file.close, file) -- Försök stänga filen även om ett fel uppstod
return nil, "Fel vid läsning av filen: " .. content
end
-- Bearbeta innehållet (fiktiv funktion)
local success, result = pcall(processContent, content)
if not success then
return nil, "Fel vid bearbetning av innehåll: " .. result
end
return result
end
Skapa en funktion safeDivide som utför division men hanterar fel på ett robust sätt:
-- Robust division med felhantering
function safeDivide(numerator, denominator)
-- Kontrollera att båda parametrarna är nummer
if type(numerator) ~= "number" then
return nil, "Täljaren måste vara ett nummer"
end
if type(denominator) ~= "number" then
return nil, "Nämnaren måste vara ett nummer"
end
-- Kontrollera division med noll
if denominator == 0 then
return nil, "Division med noll är inte tillåtet"
end
-- Utför divisionen säkert
return numerator / denominator
end
-- Testprogram
function testSafeDivide(a, b)
print("Försöker dela " .. tostring(a) .. " med " .. tostring(b))
local result, error_msg = safeDivide(a, b)
if result then
print("Resultat: " .. result)
else
print("Fel: " .. error_msg)
end
print("---")
end
-- Testa med giltiga värden
testSafeDivide(10, 2) -- Ska ge 5
testSafeDivide(-10, 5) -- Ska ge -2
testSafeDivide(7, 3.5) -- Ska ge 2
-- Testa med ogiltiga värden
testSafeDivide(10, 0) -- Division med noll
testSafeDivide("10", 5) -- Fel täljare
testSafeDivide(10, "5") -- Fel nämnare
testSafeDivide({}, true) -- Båda parametrarna är fel
Skapa ett program för filhantering som använder felhantering på ett robust sätt:
copyFile som kopierar innehållet från en fil till en annan-- En robust filkopieringsfunktion med felhantering
function copyFile(sourceFile, targetFile)
-- Validera indata
if type(sourceFile) ~= "string" or type(targetFile) ~= "string" then
return false, "Filnamnen måste vara strängar"
end
-- Öppna källfilen
local sourceHandle, sourceError = io.open(sourceFile, "rb")
if not sourceHandle then
return false, "Kunde inte öppna källfilen: " .. sourceError
end
-- Använd pcall för att garantera att källfilen stängs
local sourceContent, contentError = (function()
local content = sourceHandle:read("*all")
sourceHandle:close()
return content
end)()
if not sourceContent then
return false, "Kunde inte läsa från källfilen: " .. tostring(contentError)
end
-- Öppna målfilen
local targetHandle, targetError = io.open(targetFile, "wb")
if not targetHandle then
return false, "Kunde inte öppna målfilen för skrivning: " .. targetError
end
-- Använd pcall för att garantera att målfilen stängs
local success, writeError = pcall(function()
targetHandle:write(sourceContent)
targetHandle:close()
end)
if not success then
-- Försök stänga filen om den fortfarande är öppen
pcall(function() targetHandle:close() end)
return false, "Kunde inte skriva till målfilen: " .. tostring(writeError)
end
return true, "Filen kopierades framgångsrikt"
end
-- Testprogram
function testCopyFile(source, target)
print("Försöker kopiera '" .. source .. "' till '" .. target .. "'")
local success, message = copyFile(source, target)
if success then
print("Framgång: " .. message)
else
print("Fel: " .. message)
end
print("---")
end
-- Testa med olika scenarier
testCopyFile("exempeltext.txt", "kopia.txt") -- Antar att denna fil finns
testCopyFile("finns_inte.txt", "kopia.txt") -- Fil som inte finns
testCopyFile("exempeltext.txt", "/otillåten/sökväg/kopia.txt") -- Ogiltig sökväg
testCopyFile(123, "kopia.txt") -- Ogiltigt filnamn