0Pricing
Lua Academy · Урок

Закрытие файлов и обработка ошибок

Корректно закрывайте файловые дескрипторы и обрабатывайте ошибки io с помощью pcall.

«Закрытие файлов и обработка ошибок» — бесплатный урок Lua Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Lua Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Lua Academy содержит 4 уроков всего.

Всегда закрывайте файлы

Каждый файл, открытый с помощью io.open, необходимо закрыть вызовом f:close(). Незакрытые файлы приводят к утечке файловых дескрипторов. Сборщик мусора Lua со временем закроет их, но до этого можно исчерпать ограничения OS на количество открытых файлов. Всегда закрывайте файл в той функции, которая его открыла.

local function readFile(path)
  local f, err = io.open(path, "r")
  if not f then return nil, err end
  local content = f:read("a")
  f:close()   -- always close
  return content
end

local data, err = readFile("config.txt")
if not data then print("Error:", err) end

Безопасный ввод-вывод с помощью pcall

Оборачивайте операции с файлами в pcall, чтобы перехватывать неожиданные ошибки: переполнение диска, отказ в разрешении во время записи и другие. Это позволяет корректно обрабатывать ошибки вместо аварийного завершения программы.

local function safeCopy(src, dst)
  local ok, err = pcall(function()
    local f = io.open(src, "rb")
    if not f then error("Cannot open source: " .. src) end
    local content = f:read("a")
    f:close()
    local g = io.open(dst, "wb")
    if not g then error("Cannot open dest: " .. dst) end
    g:write(content)
    g:close()
  end)
  return ok, err
end

local ok, msg = safeCopy("input.txt", "output.txt")
if not ok then print("Failed:", msg) end

Проверка успешности записи

В системах с заполненным диском или проблемами с разрешениями f:write() может завершиться ошибкой даже после успешного выполнения io.open. На критически важных участках проверяйте возвращаемое значение операций записи.

local f = io.open("important.txt", "w")
if not f then
  print("Cannot create file")
  return
end

local ok, err = f:write("critical data\n")
if not ok then
  print("Write failed:", err)
  f:close()
  return
end

f:close()
print("Saved successfully")

Шаблон finally с pcall

В Lua нет блока finally, но его можно имитировать с помощью pcall. Оберните потенциально опасный код в pcall, а после возврата pcall — независимо от успеха или ошибки — безусловно выполните очистку, прежде чем повторно вызвать ошибку или обработать её.

local function withFile(path, mode, fn)
  local f, err = io.open(path, mode)
  if not f then return nil, err end
  local ok, result = pcall(fn, f)
  f:close()   -- always close, even on error
  if not ok then
    return nil, result
  end
  return result
end

local content, err = withFile("data.txt", "r", function(f)
  return f:read("a")
end)
print(content or err)

Сообщения об ошибках от io.open

io.open возвращает nil, сообщение системной ошибки и код ошибки. Сообщение содержит путь и причину ошибки OS («No such file», «Permission denied» и т. д.). Всегда включайте сообщение об ошибке в отчёт, который показываете пользователю.

local paths = {"config.txt", "missing.txt", "/root/secret.txt"}

for _, path in ipairs(paths) do
  local f, err, code = io.open(path, "r")
  if f then
    print(path, "-> ok, size:", f:seek("end"))
    f:close()
  else
    print(path, "-> ERROR:", err, "(code "..tostring(code)..")")
  end
end

Дескриптор файла как объект

Дескриптор файла Lua — это userdata с методами. При сборке мусора для дескриптора файл автоматически закрывается. Однако не полагайтесь на сборщик мусора, если закрытие должно произойти вовремя: всегда закрывайте файл явно. Проверить, закрыт ли файл, можно с помощью f:read(): для закрытого дескриптора эта операция вызывает ошибку.

local f = io.open("test.txt", "w")
f:write("test\n")

-- Explicit close
f:close()

-- After close, operations fail
local ok, err = pcall(function()
  f:read()   -- attempt to use closed file
end)
print(ok, err)   -- false  [closed file]

Шаблон безопасной обработки файлов

Универсальный шаблон безопасной обработки файлов: открыть файл с проверкой ошибки, выполнить обработку с помощью pcall, закрыть файл во всех случаях. Последовательно возвращайте результаты и ошибки, используя соглашение nil плюс сообщение.

local function processFile(path, processor)
  local f, err = io.open(path, "r")
  if not f then return nil, "open failed: " .. err end
  
  local ok, result = pcall(processor, f)
  f:close()
  
  if not ok then
    return nil, "processing failed: " .. result
  end
  return result
end

local lineCount, err = processFile("data.txt", function(f)
  local n = 0
  for _ in f:lines() do n = n + 1 end
  return n
end)
print(lineCount or err)

os.remove и os.rename

os.remove(path) удаляет файл. os.rename(old, new) переименовывает или перемещает файл. При успехе обе функции возвращают true, а при ошибке — nil, errMessage. Это основные функции стандартного Lua для управления файловой системой.

-- Delete a file
local ok, err = os.remove("temp.txt")
if not ok then print("Remove failed:", err) end

-- Rename / move
ok, err = os.rename("old_name.txt", "new_name.txt")
if not ok then print("Rename failed:", err) end

-- Atomic update pattern
os.rename("config.new", "config.txt")

io.close и f:close

io.close(f) эквивалентна f:close(). Вызов io.close() без аргумента закрывает файл вывода по умолчанию. В большинстве случаев используйте f:close() для большей ясности.

local f = io.open("test.txt", "w")
f:write("hello\n")

-- Both are equivalent:
-- f:close()
io.close(f)

-- io.close() with no arg closes default output
io.output("out.txt")
io.write("to file\n")
io.close()   -- closes out.txt, restores stdout

Надёжное копирование файлов

Копирование файла для рабочего приложения с обработкой ошибок на каждом этапе: открыть исходный файл, открыть файл назначения, копировать данные блоками, обработать ошибки записи и закрыть оба файла.

local function copyFile(src, dst, chunkSize)
  chunkSize = chunkSize or 65536
  local fsrc, err1 = io.open(src, "rb")
  if not fsrc then return nil, err1 end
  local fdst, err2 = io.open(dst, "wb")
  if not fdst then fsrc:close(); return nil, err2 end
  
  local ok = true
  local chunk = fsrc:read(chunkSize)
  while chunk and ok do
    local w, werr = fdst:write(chunk)
    if not w then ok = false; err2 = werr end
    chunk = fsrc:read(chunkSize)
  end
  fsrc:close(); fdst:close()
  if not ok then os.remove(dst); return nil, err2 end
  return true
end

Быстрая проверка

Как лучше всего гарантировать, что файл в Lua всегда будет закрыт, даже если произойдёт ошибка?

Повторение: закрытие и обработка ошибок

Краткое резюме:

  • Всегда явно вызывайте f:close() — не полагайтесь на сборщик мусора
  • Оборачивайте операции ввода-вывода в pcall для надёжной обработки ошибок
  • Используйте шаблон «открыть → pcall → закрыть»
  • Применяйте os.remove и os.rename для операций с файловой системой
  • На критически важных участках проверяйте возвращаемые значения записи

Часто задаваемые вопросы

Урок «Закрытие файлов и обработка ошибок» бесплатный?

Да — полный текст урока «Закрытие файлов и обработка ошибок» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Lua Academy, подпишись на CoddyKit PRO. Курс Lua Academy содержит 4 уроков всего.

Чему я научусь в уроке «Закрытие файлов и обработка ошибок»?

Корректно закрывайте файловые дескрипторы и обрабатывайте ошибки io с помощью pcall. Ты практикуешь Lua Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Lua Academy?

Предыдущий опыт не требуется. Lua Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Закрытие файлов и обработка ошибок»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Lua Academy?

Да. Каждый урок Lua Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Открытие файлов с io.open
  2. Чтение содержимого файлов
  3. Запись и добавление в файлы
  4. Закрытие файлов и обработка ошибок
← Назад к Lua Academy