ملفات الإعدادات ini files مع بايثون, التعرف عليها, إنشاؤها, قراءتها, والتعديل عليها

الكاتب: سُلَيْمان القسيمي.
بسم الله الرحمن الرحيم
الأعزاء في هذه المجموعة الكريمة, سلام الله عليكم.
لا بد أنك بعد مرحلة ما من البذل والتطوير في أحد مشاريعك البرمجية الناجحة, خاصة تلك المشاريع التي تعتمد بشكل أو بآخر على مدخلات المستخدم, لا بد أنك حينها ستحتاج إلى ما يمكنك من تخزين هذه المدخلات والاحتفاظ بها بطريقة تتيح لبرنامجك أن يتذكر ما ينبغي تذكره من مدخلات حتى ولو تم إغلاقه. هذا بالطبع تمامًا مثل ما يحصل في برنامجنا المتواضع accessible youtube downloader حيث ترون أنه يحتفظ بأمرَين اثنَين حتى ولو إغلق البرنامج, مسار التنزيل وصيغة المقاطع الصوتية. 
كذلك الحال ينطبق على قارئنا العزيز NVDA بمختلف أجزاؤه وتفرعاته, حيث أنك بالطبع لا تحتاج إلى ضبط تفضيلات آلة النطق على سبيل المثال لا الحصر في كل مرة تعيد فيها تشغيل البرنامج.
إذًا, إن كنت تبحث عن أمر كهذا فتعال معي أطلعك على الطريقة المتبعة في بايثون لتطبيق ذلك.

بداية: ينبغي القول أن الوسيلة المدمجة في بايثون للتعامل مع الإعدادات تعتمد على ما يعرف بملفات ini
وهذه الملفات عبارة عن مستندات نصية بتركيبة معينة يتم الاعتماد عليها برمجيًا لحفظ قيم لمفاتيح معينة

تتم هيكلة ملفات ini بهذا الشكل:

[section1]
option1 = value1
option2 = value2
[section2]
option1 = value1
option2 = value2
option3 = value3

طيب ما هذا كله؟

نرى هنا أن الملف مقسم إلى ما يسمى بال sections أو فرع, تحت كل فرع نجد مجموعة من العناصر, كل عنصر يساوي قيمة.
وتلاحظون أنه تتم إحاطة مسميات الفروع بقوسين مربعين, كما أنه يتم إسناد قيم العناصر من خلال علامة التساوي = تمامًا مثل المتغيرات
هنا أنت ستعتمد على هذه المفاتيح بإعطاء كل مفتاح قيمة معينة تستطيع استدعاؤها داخل برنامجك لاتخاذ قرار معين. فعلى سبيل المثال, لو اخترت أن تحفظ مسار العمل الافتراضي لبرنامجك في المفتاح path فستستخدم هذا المفتاح دائمًا لقراءة المسار المحفوظ بل وتغييره أيضًا.
يشترط في كل ملف ini أن يحتوي على فرع section 1 على الأقل, ولا يلزم أكثر من ذلك, بمعنى أنه بالإمكان حفظ جميع الخيارات تحت قسم واحد يمكن تسميته مثلًا ب [settings]
بوسعك أنت كمبرمج إنشاء ملف الإعدادات يدويًا وتعيين عناصره وفروعه من غير كتابة الأكواد الخاصة بذلك وتكتفي فقط بقراءة الملف والكتابة عليه, لكن خذ بالاعتبار أن الملف قابل للحذف والعبث, لذا ينبغي تأمين كل هذه الأمور من خلال الكود.

الآن, وبعد أن تعرفنا عن قرب على ملفات ال iniبشكل عام, دعونا نرى كيف ننشئ واحدًا بلغة البايثون وقراءة محتوياته وتعديلها.
هناك كلاس في بايثون يدعى ConfigParser وهو أحد أبناء مكتبة configparser المدمجة, هذا الكلاس هو الذي يتولى إنشاء تعديل إضافة قراءة ملفات ini في بايثون

لذلك, قبل إجراء أي شيء يتوجب علينا استدعاء الكلاس داخل البرنامج

import ConfigParser

نريد الآن إنشاء ملف ini يحتوي على قسم واحد اسمه settings, تحت هذا القسم سنضع مفتاحين اثنَين, الأول لحفظ اللغة, والآخر لحفظ إعداد تشغيل صوت ترحيبي للبرنامج, وهي كلها مفاتيح تجريبية سنستفيد منها فقط لإجراء التجارب وليس لرؤية نتيجة فعلية.
أولًا: إنشاء كائن من كلاس ConfigParser
config = ConfigParser()


جميل. لدينا الآن كائن من ConfigParser أسميناه config
سنجري على هذا الكائن كل التعديلات والإجراءات, بعدها سننشئ ملف نحفظ كل ذلك.

ثانيًا: إنشاء فرع جديد باسم settings
config.add_section("settings")
تم استخدام الوظيفة add_section وتمرير قيمة نصية لها لإدراج فرع جديد باسم settings وهو ذاته القيمة النصية التي تم تمريرها إلى add_section
ثالثًا: إضافة القيم

تعتمد طريقة إضافة القيم هنا على نفس مبدأ عمل القواميس في بايثون, حيث أن الملف عبارة عن قاموس كبير, كل فرع عبارة عن مفتاح يساوي قاموس صغير, داخل القواميس الصغيرة تتواجد العناصر والقيم المساوية لها.
إذًا: فسنكتب الآن اسم القاموس, وهو هنا نفسه الكائن config ثم نفتح جنبه قوسًا مربعًا نكتب اسم الفرع الذي نريد إضافة المفتاح إليه, إلى جانب ذلك سنفتح قوسَين مربعَين آخرين نضع فيهما اسم المفتاح, وفي الآخير سيساوي ذلك كله قيمة المفتاح, أي بهذا الشكل
config["settings"]["language"] = "arabic"
أضفنا المفتاح language إلى الفرع settings وهنا قيمة المفتاح هي "arabic"

الآن سنضيف المفتاح الثاني بنفس الطريقة
config["settings"]["startupsound"] = "True"

جميل. كل شيء جاهز الآن ليتم كتابته على الملف
رابعًا: إنشاء الملف وكتابة الإعدادات عليه
with open("settings.ini", "w") as file:
    config.write(file)

تم فتح ملف جديد باسم settings.ini حيث أنه لو تم إيجاد الملف سابقًا فسيُفتح كما هو, وإلا فسيتم إنشاء ملف جديد بنفس المسمى, وفي كلا الحالتَين يُفتح الملف بوضع الكتابة من خلال تمرير القيمة "w" إلى المعامل الثاني من دالة open

بعد ذلك تم استخدام وظيفة write من الكائن config للكتابة على الملف


انتهى.

لحظة لحظة


لم تخبرنا بطريقة التعامل مع ملف ini موجود مسبقًا

الحقيقة ليس هناك الكثير ليختلف؛ فهنا سننشئ أيضًا كائن من الكلاس ConfigParser() غير أننا سنقوم بقراءة ملف ini موجود من خلال وظيفة read كما يلي

config = ConfigParser()
config.read("settings.ini")

سيتعقب البرنامج عن ملف باسم settings.ini وسيقوم بقراءته.

الآن لو أردنا معرفة قيمة مفتاح معين فيمكننا ببساطة كتابة اسم الكائن ثم داخل قوسين مربعين نضع اسم الفرع وبعدهما في قوسَين مربعين نضع اسم المفتاح
على سبيل المثال: سأستخرج قيمة المفتاح language وسأحفظها في متغير سأسميه مثلًا lang
lang = config["settings"]["language"]

أستطيع الآن طباعته هكذا:
print(lang)
الناتج سيكون كلمة arabic بالطبع كما خصصت سابقًا.

طيب في حال لو أردت تغيير قيمة مفتاح معين ماذا أفعل؟

الجواب: ستنشئ كائنًأ من ConfigParser كالعادة وتقرأ الملف من خلال وظيفة read كما فعلنا الآن وتكتب نفس السطر الذي كتبته عند إضافة المفتاح ولكن مع تغيير القيمة إلى المحتوى الجديد
config = ConfigParser()
config.read()
config["settings"]["startupsound"] = "False"

هنا قمنا بتبديل قيمة المفتاح startupsound من True إلى False
أخيرًأ لا ننسى كتابة التعديلات إلى ملف ال ini
with open("settings.ini", "w") as file:
    config.write(file)

ملاحظة قبل أن أختم:
يجب عليك فرضًا أن تسند المفاتيح بقيم نصية, أي بين علامتَي تنصيص, حيث أن configparser لا تفهم إلا هذا النمط ولا تعيد غيره.
بمعنى أنك حتى حين تقرأ الملفات لاستخراج أرقامًأ أو قيم من نوع Boolean سواء True أو False فيجب عليك إما أن  أدوات اللغة لتغيير نوع القيمة إلى النوع الصحيح, أو الاستعانة بأحد وظائف التحويل المدمجة مع كلاس ConfigParser كوظيفة getboolean التي تعيد القيم المنطقية بشكلها الصحيح كما ستشاهدون في المثال التطبيقي الشامل بإذن الله.

ختامًا: أسأل المولى القدير أن يوفقنا وإياكم لما فيه الخير والصلاح, والسلام عليكم ورحمة الله وبركاته.

اضغط هنا لتنزيل النموذج المرفق بالشرح

تعليقات