0Pricing
Lua Academy · درس

إغلاق الملفات ومعالجة الأخطاء

أغلِق مقابض الملفات بطريقة صحيحة وعالج أخطاء io باستخدام pcall

إغلاق الملفات ومعالجة الأخطاء درس مجاني في Lua Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Lua Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Lua Academy 4 دروس في المجموع.

أغلقوا الملفات دائمًا

يجب إغلاق كل ملف يُفتح باستخدام io.open عبر f:close(). فالملفات غير المغلقة تسرّب واصفات الملفات. وسيغلقها جامع البيانات المهملة في Lua في النهاية، لكن يمكنكم استنفاد حدود ملفات نظام التشغيل قبل تشغيل جامع البيانات المهملة. أغلقوا الملف دائمًا في الدالة التي فتحته.

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، بالإضافة إلى رسالة خطأ من النظام ورمز الخطأ. وتتضمن الرسالة المسار وسبب الخطأ من نظام التشغيل (مثل "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+message.

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() صراحةً دائمًا، ولا تعتمدوا على GC
  • غلّفوا عمليات الإدخال والإخراج في pcall لمعالجة الأخطاء بمتانة
  • استخدموا قالب "open → pcall → close"
  • استخدموا 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 يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. فتح الملفات باستخدام io.open
  2. قراءة محتويات الملفات
  3. الكتابة إلى الملفات وإلحاق البيانات بها
  4. إغلاق الملفات ومعالجة الأخطاء
← العودة إلى Lua Academy