أول مشروع لي في Python: كيف قمت بتحويل ملف نصي غير منظم إلى ملف CSV أنيق

لذلك قررت أن أتعلم بايثون. تبين أن لغة برمجة الكمبيوتر هذه ليست صعبة للغاية (حسنًا ، حتى حصلت على هذا المشروع!: P).

في غضون ثوان ، وقعت في حب تركيبها السهل والنقي والمسافة البادئة التلقائية أثناء الكتابة. لقد شعرت بالذهول عندما علمت أنه يمكن إنشاء هياكل البيانات مثل القوائم والجداول والقاموس وتهيئتها ديناميكيًا باستخدام سطر واحد (مثل ، اسم القائمة = []).

علاوة على ذلك ، يمكن الوصول إلى القيم الموجودة في هذه باستخدام الفهارس وبدونها. هذا يجعل الكود سهل القراءة حيث يتم استبدال الفهرس بكلمة إنجليزية يختارها المرء.

حسنًا ، ما يكفي من الكلام عن اللغة. دعني أريكم ما يطلبه المشروع.

أعطاني أخي هذا المشروع. لقد صادف ملفًا نصيًا يحتوي على آلاف الكلمات. تشترك العديد من الكلمات في نفس المعنى تقريبًا. كان لكل كلمة تعريفها وجمل مثال بجانبها ولكن بطريقة غير منظمة. كانت هناك مسافات وأسطر جديدة بين الكلمة وجملتها. بعض الجوانب كانت مفقودة من الكلمات. فيما يلي مقتطفات من الملف النصي الذي أتحدث عنه:

أراد أن تكون جوانب النص موحدة. من أجل ذلك ، احتاج إلي أن أصنف بدقة كل الكلمات ذات المعاني المتشابهة بجانب الموضوع. أخبرني أنه يمكن تحقيق ذلك من خلال التقاط جميع البيانات الموجودة في النص في قاموس بالتنسيق التالي:

ثم كتابتها في ملف CSV (قيم مفصولة بفواصل).

سألني عما إذا كان بإمكاني اعتبار هذا المشروع الأول لي ، بعد أن تعلمت الأساسيات. لقد شعرت بسعادة غامرة للتوصل إلى المنطق ووافقت على الفور. عندما سئل عن الموعد النهائي ، أعطاني وقتًا لائقًا ليومين للانتهاء.

للأسف ، انتهى بي الأمر بأخذ ضعف الوقت لأنني كافحت لتصحيح الشفرة المكتوبة بشكل صحيح. بصراحة ، إذا لم تكن زيارات أخي القصيرة إلى غرفتي لإلقاء نظرة على التقدم والتلميح إلى الافتراضات الخاطئة التي قمت بها أثناء كتابة الشروط ، فقد كنت مقدرًا لإنهاء المشروع في الأبدية: P

لقد بدأت بإنشاء مهام صغيرة داخل البرنامج الذي سعيت لإنهائه قبل إنشاء البرنامج بأكمله. كانت هذه على النحو الوارد أدناه:

1. تكوين Regex لمطابقة رقم والكلمة المجاورة له.

فحصت الملف النصي ولاحظت أن كل موضوع (يشار إليه هنا باسم "مفتاح") كان له رقم يسبقه. لذلك ، كتبت بضعة أسطر من التعليمات البرمجية لإنشاء regex (تعبير عادي - أداة قوية لاستخراج النص) للنمط على النحو التالي:

ومع ذلك ، عندما قمت بتشغيل هذا ، تلقيت خطأ ، UnicodeDecodeError ، على وجه الدقة مما يعني أنه لم يكن لدي حق الوصول إلى الملف النصي. لقد بحثت عنه في //stackoverflow.com وبعد بحث طويل دون حظ ، جاء أخي ووجد حلاً. تم تصحيح الخطأ على النحو التالي:

ومع ذلك ، لم أحصل على الإخراج المطلوب. كان هذا بسبب احتواء بعض المفاتيح على شرطات مائلة ('/') أو مسافات ('') في النص والتي تعذر على regex مطابقتها. فكرت في تحسين تعبير regex لاحقًا ولذا كتبت تعليقًا بجواره.

2. الحصول على قائمة الأسطر كسلاسل من الملف النصي

لهذا ، كتبت سطرًا واحدًا فقط من التعليمات البرمجية ولحسن الحظ ، لم تظهر أي أخطاء.

ومع ذلك ، حصلت على قائمة غير نظيفة. احتوت على أسطر جديدة ('\ n') ومسافات ('') ثم حاولت تحسين القائمة على النحو التالي:

3. استخلاص الكلمات والمعاني وأمثلة الجمل بشكل منفصل وإضافتها إلى القوائم المقابلة.

كان هذا إلى حد بعيد هو الجزء الأصعب الذي يجب القيام به لأنه تضمن المنطق السليم والحكم المناسب عن طريق التعرف على الأنماط.

ومن المثير للاهتمام ، أثناء إلقاء نظرة خاطفة على الملف النصي ، لاحظت المزيد من الأنماط. كل كلمة لها معناها في نفس السطر مفصولة بعلامة '='. أيضًا ، كل مثال سبقه كلمة رئيسية ":" و "مثال".

فكرت في الاستفادة من regex مرة أخرى. لقد وجدت حلاً بديلاً وأكثر أناقة عن طريق تقطيع الخط (الآن سلسلة في القائمة) وفقًا لوضع الرموز. التقطيع ميزة رائعة أخرى في بيثون. كتبت الكود على النحو التالي:

يقرأ الكود أعلاه تقريبًا مثل اللغة الإنجليزية. لكل سطر في القائمة النظيفة ، يتحقق ما إذا كان يحتوي على علامة '=' أو علامة ':'. إذا حدث ذلك ، فسيتم العثور على فهرس العلامة ويتم التقطيع وفقًا لذلك.

في أول "if" ، يتم تخزين الجزء قبل "=" في المتغير "كلمة" والجزء الذي يليه يتم تخزينه في "معنى". وبالمثل بالنسبة إلى "if" الثانية ("elif - else if - في هذه الحالة) ، يتم تخزين الجزء الذي يلي": "في" example ". وبعد كل تكرار ، يتم تخزين الكلمة والمعنى ومثال الجملة في القوائم المقابلة. بهذه الطريقة ، يمكن استخراج البيانات بالكامل.

حتى الان جيدة جدا. لكنني لاحظت أن الاستخراج كان يجب أن يتم بطريقة بحيث يجب تجميع كل كلمة (وجوانبها) من مفتاح معين معًا كقيمة واحدة للمفتاح. هذا يعني أنه كان مطلوبًا تخزين كل كلمة ومعنى ومثال داخل tuple. يجب تخزين كل مجموعة داخل قائمة واحدة تمثل نفسها كقيمة لمفتاح معين. هذا موضح أدناه:

لهذا ، خططت لجمع كل كلمة ومعنى وكل جملة من كل مفتاح داخل قائمة منفصلة محاطة بقائمة أخرى ، مثل قائمة المفاتيح. مرة أخرى ، ستخبرك الصورة بشكل أكثر دقة:

للقيام بذلك ، أضفت الكود التالي إلى الرمز الذي كتبته للتقطيع:

تبين أن منطق هذا الرمز (الجزء الآخر) خاطئ ، للأسف. افترضت خطأً أن شرطين فقط ('=' و ':') موجودان في النص. كان هناك العديد من الاستثناءات التي لم ألاحظها. انتهى بي الأمر بإهدار ساعات لتصحيح الأخطاء المحتملة في المنطق. لقد افترضت أن الملف النصي الكامل اتبع نفس النمط. ولكن لم يكن هذا هو الحال ببساطة.

غير قادر على إحراز تقدم ، انتقلت إلى الجزء التالي من البرنامج. اعتقدت أنه يمكنني استخدام بعض المساعدة من أخي بعد الانتهاء من الأجزاء الأخرى. : ص

يتبع…

4. إنشاء قيم للمفاتيح باستخدام وظيفة Zip وتفريغ المعلمات.

في هذه المرحلة ، لم أكن متأكدًا تمامًا مما سأفعله حتى بعد تحقيق التكوين أعلاه للقوائم. لقد تعلمت عن وظيفة 'Zip' و 'Parameter Unpacking' خلال إحدى محادثات التكنولوجيا لأخي ، والتي ضغطت حرفياً القوائم التي تم تمريرها إليها ، مثل:

لذلك اعتقدت أنه يمكنني بطريقة ما الجمع بين هاتين الميزتين لتحقيق النتيجة المرجوة. لقد نجحت بعد قليل من العمل والاطلاع ، واختبار الميزات والعمل على قوائم وهمية. لقد أنشأت ملفًا منفصلاً (تجريبيًا) لهذه المهمة ، المقتطف أدناه:

يمكن معرفة عمل الكود أعلاه من خلال إلقاء نظرة على الإخراج:

تعمل الوظيفة zip () على ضغط القوائم أو القيم المقابلة داخل القوائم وإرفاقها في مجموعة. يتم بعد ذلك تحويل المجموعات الموجودة داخل القوائم إلى قوائم لتفريغ محتوياتها وضغطها بشكل أكبر. أخيرًا ، يتم الحصول على الإخراج المطلوب.

شعرت بارتياح كبير لأن الكود يعمل هذه المرة. كنت سعيدًا لأنني تمكنت من معالجة البيانات التي سيتم استخراجها وصياغتها بالتنسيق المطلوب. قمت بنسخ الكود إلى الملف الرئيسي الذي كنت أعمل فيه وقمت بتعديل أسماء المتغيرات وفقًا لذلك. الآن كل ما تبقى هو تعيين قيم للمفاتيح في القاموس (وبالطبع جزء الاستخراج!).

5. تعيين قيم للمفاتيح في القاموس.

لهذا ، توصلت إلى هذا الحل بعد بعض التجارب مع الكود:

أنتج هذا الناتج المطلوب على النحو التالي:

كان البرنامج على وشك الانتهاء. تكمن المشكلة الرئيسية في جزء استخراج البيانات.

... استمرار من القسم 3

بعد ساعات وساعات من التصحيح ، أصبحت محبطًا أكثر فأكثر بسبب عدم نجاح هذا الشيء اللعين. اتصلت بأخي وأعطاني تلميحًا خفيًا حول الافتراضات التي قدمتها أثناء تحديد الحلقات الشرطية وعبارات if-else. فحصنا الملف النصي ولاحظنا أن بعض الكلمات تحتوي على أمثلة في سطرين بدلاً من سطر واحد.

وفقًا لمنطق الكود الخاص بي ، نظرًا لعدم وجود علامة ':' في السطر الثاني (ولا علامة '=' ، لهذه المسألة) ، لن يتم التعامل مع المحتويات الموجودة في السطر كجزء من المثال. نتيجة لذلك ، ستجعل هذه العبارة الجزء الأخير من "else" صحيحًا وتنفذ الكود المكتوب فيه. بالنظر إلى كل هذا ، قمت بتعديل الكود على النحو التالي:

هنا ، hasNumbers () هي وظيفة تتحقق من وجود أرقام في سطر معين. عرفته على النحو التالي:

ما يفعله هذا هو أنه يجمع السطر الثاني من المثال إذا فشلت جميع الشروط الأخرى ، ويجمعه مع السطر الأول ثم يضيفه إلى القائمة المقابلة كما كان من قبل.

لخيبة أملي ، لم ينجح هذا وبدلاً من ذلك أظهر خطأً أن الفهرس كان خارج النطاق. كنت مذهولًا ، حيث بدا أن كل سطر من التعليمات البرمجية صحيح منطقيًا من وجهة نظري.

بعد ساعات من الجنون ، أراني أخي طريقة لجلب أرقام الأسطر التي حدث فيها الخطأ. إحدى المهارات الأساسية في البرمجة هي القدرة على تصحيح أخطاء البرنامج ، للتحقق بشكل صحيح من الأخطاء المحتملة والحفاظ على التدفق المستمر.

ومن المثير للاهتمام ، أن الإضافة التالية إلى الكود ذكرت أن الخطأ حدث بالقرب من السطر رقم 1750 من الملف النصي.

هذا يعني أن البرنامج يعمل بشكل جيد حتى رقم السطر وأن الكود الخاص بي كان صحيحًا! تكمن المشاكل في افتراضاتي الخاطئة وكذلك في الملف النصي بسبب عدم تجانسه.

هذه المرة ، لاحظت أن بعض المفاتيح لم تكن بأرقامها مما تسبب في مشاكل في التدفق المنطقي. قمت بتصحيح الأخطاء عن طريق إجراء مزيد من التعديل على الكود على النحو التالي:

نجح هذا الأمر جيدًا حتى السطر 4428 من الملف النصي ولكنه تعطل بعد ذلك مباشرة. لقد تحققت من رقم السطر في الملف النصي نفسه ولكن ذلك لم يساعد كثيرًا. ثم أدركت ، من دواعي سعادتي ، أنه يجب أن يكون السطر الأخير. عمل البرنامج بأكمله على القائمة النظيفة التي كانت خالية من الأسطر الجديدة والمسافات. لقد قمت بطباعة السطر الأخير من القائمة النظيفة وقارنتها بالسطر الأخير من الملف النصي. تطابقوا!

كنت سعيدًا جدًا بمعرفة هذا لأنه يعني أن البرنامج تم تنفيذه حتى النهاية. السبب الوحيد وراء تعطلها هو أنه بعد الجملة الأخيرة لم يكن أي من الكود منطقيًا. تم تصميم الشروط الشرطية الخاصة بي في كل مرة للتحقق من السطر التالي أيضًا ، جنبًا إلى جنب مع السطر الحالي. نظرًا لعدم وجود خط بعد السطر الأخير ، فقد تعطل.

لذلك كتبت سطرًا إضافيًا من التعليمات البرمجية لتغطية ذلك:

كل شيء يعمل الآن. أخيرا! الآن كل ما كان عليّ فعله هو تعيين المفاتيح للقيم المقابلة وهذا كل شيء! أخذت استراحة في هذه اللحظة ، معتبرا أن مشروعي قد انتهى أخيرًا. أود أن أضيف بعض اللمسات الأخيرة إليها لاحقًا.

ولكن قبل أخذ استراحة ، قررت أن أرفق كل رمز داخل وظائف مختلفة لجعل الكود يبدو أنيقًا. لقد واجهت بالفعل صعوبة كبيرة في التنقل لأعلى وأسفل سطور التعليمات البرمجية. لذلك قررت أن آخذ استراحة بعد القيام بذلك.

ومع ذلك ، بعد القيام بذلك ، بدأ البرنامج بإعطاء أخطاء متغيرة النطاق. أدركت أن هذا يرجع إلى أن المتغيرات المعلنة داخل الوظائف لا يمكن استدعاؤها مباشرة من خارج الوظيفة كما هي في مساحة الاسم المحلية. غير راغب في إجراء المزيد من التغييرات بسبب هذا الخطأ الأعرج ، قررت العودة إلى نفس الرمز الذي كنت أصطدم به منذ البداية.

ومع ذلك ، ولصدقتي المطلقة ، لم يعمل البرنامج بنفس الطريقة التي كان يعمل بها من قبل. في الواقع ، لم تنجح على الإطلاق! أنا ببساطة لا أستطيع معرفة السبب (وما زلت لا أستطيع!). شعرت بالاكتئاب التام لبقية اليوم. كان الأمر أشبه بتجربة كابوس حتى قبل النوم!

لحسن الحظ وبأعجوبة ، عملت الشفرة في اليوم التالي بعد أن أجريت بعض التغييرات الدقيقة. لقد تأكدت من أنني قمت بعمل العديد من ملفات بيتا (لكل تغيير يتم إجراؤه) بعد ذلك لتجنب مثل هذه الفوضى غير الضرورية.

بعد بضع ساعات أخرى ، تمكنت أخيرًا من إكمال برنامجي (ولكن ليس حتى استهلكت 4 أيام كاملة). لقد أجريت بعض التغييرات الأخرى مثل:

ط) تعديل وظيفة "hasNumbers" إلى وظيفة "hasNumbersDot" واستبعاد regex التي قمت بإنشائها مسبقًا في البرنامج. هذا يطابق المفاتيح بشكل أكثر كفاءة لأنه لا يحتوي على افتراضات وبالتالي لا استثناءات. الكود الخاص بها كما يلي:

ب) استبدال شرط regex ورمز الحصول على مفاتيح من القائمة النظيفة.

ج) الجمع بين شروط "إذا" في جزء "استخراج الأمثلة"

د) تجسيد رمز تخصيص مفتاح القاموس

أيضًا ، بعد بعض التجارب والخطأ ، تمكنت من تحويل البيانات التي تم الحصول عليها إلى ملف CSV منظم بشكل جميل:

يمكنك التحقق من مستودع github الخاص بي في ملفي الشخصي لعرض الكود الكامل للبرنامج بما في ذلك الملف النصي وملف csv.

عموما كانت تجربة رائعة. لقد تعلمت الكثير من هذا المشروع. كما أنني اكتسبت ثقة أكبر في مهاراتي. على الرغم من بعض الأحداث المؤسفة (تتضمن البرمجة مثل هذه الأشياء: P) ، تمكنت أخيرًا من إكمال المهمة المحددة.

شيء أخير! في الآونة الأخيرة ، صادفت ميمًا مضحكًا فيما يتعلق بمراحل تصحيح الأخطاء التي ترتبط بتجربتي لدرجة أنني لا أستطيع مقاومة المشاركة. وجه ضاحك

شكرًا لك على الوصول إلى هنا (حتى لو تخطيت معظمها للتحقق من النتيجة النهائية: P).