اختار المؤلف Society of Women Engineers لتلقي تبرع كجزء من برنامج الكتابة من أجل التبرعات.
مقدمة
A CSV is a plain text file format for storing tabular data. The CSV file uses a comma delimiter to separate values in table cells, and a new line delineates where rows begin and end. Most spreadsheet programs and databases can export and import CSV files. Because CSV is a plain-text file, any programming language can parse and write to a CSV file. Node.js has many modules that can work with CSV files, such as node-csv
, fast-csv
, and papaparse
.
في هذا البرنامج التعليمي، ستستخدم وحدة node-csv
لقراءة ملف CSV باستخدام تيارات Node.js، مما يتيح لك قراءة مجموعات بيانات كبيرة دون استهلاك الكثير من الذاكرة. ستقوم بتعديل البرنامج لنقل البيانات المحللة من ملف CSV إلى قاعدة بيانات SQLite. ستقوم أيضًا باسترداد البيانات من قاعدة البيانات، وتحليلها باستخدام node-csv
، واستخدام تيارات Node.js لكتابتها إلى ملف CSV بشكل متقطع.
نشّر تطبيقات Node الخاصة بك من GitHub باستخدام منصة التطبيقات DigitalOcean. دع DigitalOcean تركز على توسيع تطبيقك.
المتطلبات المسبقة
لمتابعة هذا البرنامج التعليمي، ستحتاج إلى:
-
تثبيت Node.js على بيئتك المحلية أو الخادم. اتبع كيفية تثبيت Node.js وإنشاء بيئة تطوير محلية لتثبيت Node.js.
-
تم تثبيت SQLite على بيئتك المحلية أو الخادم، والذي يمكنك تثبيته عن طريق اتباع الخطوة 1 في كيفية تثبيت واستخدام SQLite على أوبونتو 20.04. يكون المعرفة في كيفية استخدام SQLite مفيدة ويمكن تعلمها في الخطوات 2-7 من دليل التثبيت.
-
المعرفة بكتابة برنامج Node.js. انظر كيفية كتابة وتشغيل برنامجك الأول في Node.js.
-
المعرفة بتدفقات Node.js. انظر كيفية العمل مع الملفات باستخدام تدفقات في Node.js.
الخطوة 1 — إعداد دليل المشروع
في هذا القسم، ستقوم بإنشاء دليل المشروع وتنزيل الحزم لتطبيقك. ستقوم أيضًا بتنزيل مجموعة بيانات CSV من إحصاءات نيوزيلندا، التي تحتوي على بيانات الهجرة الدولية في نيوزيلندا.
للبدء، قم بإنشاء دليل يسمى csv_demo
وانتقل إلى الدليل:
ثم، قم بتهيئة الدليل كمشروع npm باستخدام الأمر npm init
:
الخيار -y
يخبر npm init
بالموافقة على جميع الاستجوابات. ينشئ هذا الأمر package.json
بالقيم الافتراضية التي يمكنك تغييرها في أي وقت.
بعد أن تكون الدليل مهيأً كمشروع npm، يمكنك الآن تثبيت التبعيات اللازمة: node-csv
و node-sqlite3
.
أدخل الأمر التالي لتثبيت node-csv
:
يعد وحدة node-csv
مجموعة من الوحدات التي تسمح لك بتحليل وكتابة البيانات في ملف CSV. يقوم الأمر بتثبيت جميع الوحدات الأربعة التي تتكون منها حزمة node-csv
: csv-generate
، csv-parse
، csv-stringify
، و stream-transform
. ستستخدم وحدة csv-parse
لتحليل ملف CSV ووحدة csv-stringify
لكتابة البيانات إلى ملف CSV.
بعد ذلك، قم بتثبيت وحدة node-sqlite3
:
تتيح لك وحدة node-sqlite3
التفاعل مع قاعدة البيانات SQLite في تطبيقك.
بعد تثبيت الحزم في مشروعك، قم بتنزيل ملف CSV لهجرة نيوزيلندا باستخدام الأمر wget
:
اسم ملف CSV الذي قمت بتنزيله طويل. لتسهيل العمل معه، قم بإعادة تسمية اسم الملف إلى اسم أقصر باستخدام الأمر mv
:
اسم ملف CSV الجديد، migration_data.csv
، أقصر وأسهل للعمل معه.
باستخدام nano
، أو محرر النصوص المفضل لديك، افتح الملف:
بمجرد فتحه، سترى محتويات مشابهة لهذه:
year_month,month_of_release,passenger_type,direction,sex,age,estimate,standard_error,status
2001-01,2020-09,Long-term migrant,Arrivals,Female,0-4 years,344,0,Final
2001-01,2020-09,Long-term migrant,Arrivals,Male,0-4 years,341,0,Final
...
السطر الأول يحتوي على أسماء الأعمدة، وجميع السطور التالية تحتوي على البيانات المقابلة لكل عمود. يفصل كل قطعة بيانات فاصلة. هذا الحرف يُعرف بالفاصل لأنه يفصل الحقول. لست مقيدًا باستخدام الفواصل. تتضمن الفواصل الشائعة الأخرى النقاط والفواصل (:
)، والفواصل المنقوطة (;
)، والعلامات (\td
). يجب عليك معرفة الفاصل المستخدم في الملف لأن معظم الوحدات يتطلبونه لتحليل الملفات.
بعد مراجعة الملف وتحديد الفاصل، اخرج من ملف migration_data.csv
باستخدام CTRL+X
.
لقد قمت الآن بتثبيت التبعيات اللازمة لمشروعك. في القسم التالي، ستقوم بقراءة ملف CSV.
الخطوة 2 — قراءة ملفات CSV
في هذا القسم، ستستخدم node-csv
لقراءة ملف CSV وتسجيل محتواه في وحدة التحكم. ستستخدم طريقة createReadStream()
في وحدة fs
لقراءة البيانات من ملف CSV وإنشاء تيار قابل للقراءة. بعد ذلك، ستقوم بتوجيه التيار إلى تيار آخر تم إعداده بوحدة csv-parse
لتحليل أجزاء البيانات. بمجرد تحليل أجزاء البيانات، يمكنك تسجيلها في وحدة التحكم.
قم بإنشاء وفتح ملف readCSV.js
في محرر النصوص المفضل لديك:
في ملفك readCSV.js
، استورد وحدتي fs
و csv-parse
عن طريق إضافة الأسطر التالية:
في السطر الأول، قم بتعريف المتغير fs
وتعيينه بكائن fs
الذي تعيده طريقة require()
في Node.js عند استيراد الوحدة.
في السطر الثاني، تقوم بإستخراج الطريقة parse
من الكائن الذي يُعيدُه الطريقة require()
إلى المتغير parse
باستخدام بنية الإفنتاح.
أضف الأسطر التالية لقراءة ملف CSV:
تقبل الطريقة createReadStream()
من الوحدة fs
وسيطًا يكون اسم الملف الذي تريد قراءته، والذي هو migration_data.csv
هنا. بعد ذلك، تنشئ مجرىً قابلًا للقراءة، الذي يأخذ ملفًا كبيرًا ويقسمه إلى قطع صغيرة. يتيح لك مجرى القراءة فقط قراءة البيانات منه وليس كتابتها.
بعد إنشاء مجرى القراءة، تقوم طريقة pipe()
في Node بتوجيه قطع البيانات من مجرى القراءة إلى مجرى آخر. يتم إنشاء المجرى الثاني عند استدعاء طريقة parse()
في وحدة csv-parse
داخل طريقة pipe()
. تقوم وحدة csv-parse
بتنفيذ مجرى تحويل (مجرى قابل للقراءة والكتابة)، يأخذ قطعة بيانات ويحوّلها إلى شكل آخر. على سبيل المثال، عندما تتلقى قطعة مثل 2001-01,2020-09,المهاجر على المدى الطويل,وصول,أنثى,0-4 سنوات,344
، ستقوم طريقة parse()
بتحويلها إلى مصفوفة.
تقوم طريقة parse()
بأخذ كائن يقبل الخصائص. يقوم الكائن بتكوين وتوفير مزيد من المعلومات حول البيانات التي ستُحَلل بواسطة الطريقة. يأخذ الكائن الخصائص التالية:
-
يحدد
delimiter
الحرف الذي يفصل كل حقل في الصف. القيمة,
تخبر المحلل بأن الفواصل تفصل بين الحقول. -
from_line
يحدد السطر الذي يجب أن يبدأ المحلل في تحليل الصفوف منه. باستخدام القيمة2
، سيتخطى المحلل السطر 1 ويبدأ من السطر 2. نظرًا لأنك ستقوم بإدراج البيانات في قاعدة البيانات لاحقًا، تساعدك هذه الخاصية في تجنب إدراج أسماء الأعمدة في السطر الأول من قاعدة البيانات.
بعد ذلك، قم بإرفاق حدث تدفق باستخدام طريقة Node.js on()
. يسمح حدث التدفق للطريقة باستهلاك جزء من البيانات إذا تم بث حدث معين. يتم تشغيل حدث data
عندما تكون البيانات المحولة من طريقة parse()
جاهزة للاستهلاك. للوصول إلى البيانات، قم بتمرير استدعاء للرد على طريقة on()
، التي تأخذ معلمة تسمى row
. تُمثّل معلمة row
جزءًا من البيانات تم تحويلها إلى مصفوفة. ضمن الاستدعاء، قم بتسجيل البيانات في وحدة التحكم باستخدام طريقة console.log()
.
قبل تشغيل الملف، ستقوم بإضافة المزيد من أحداث التيار. تتعامل هذه الأحداث مع الأخطاء وتكتب رسالة نجاح إلى وحدة التحكم عند استهلاك كل البيانات في ملف CSV.
ما زلت في ملف readCSV.js
، أضف الكود المظلل التالي:
تتم إرسال حدث end
عند قراءة كل البيانات في ملف CSV. عند حدوث ذلك، يتم استدعاء الوظيفة الاستدعائية ويتم تسجيل رسالة تقول أنه تم الانتهاء.
إذا حدث خطأ في أي مكان أثناء قراءة وتحليل بيانات CSV، يتم إرسال حدث error
، الذي يستدعي الوظيفة الاستدعائية ويسجل رسالة الخطأ في وحدة التحكم.
يجب أن يبدو ملفك الكامل الآن على النحو التالي:
احفظ واخرج من ملف readCSV.js
باستخدام CTRL+X
.
بعد ذلك، قم بتشغيل الملف باستخدام الأمر node
:
سيبدو الإخراج مشابهًا لهذا (محرر للتبسيط):
Output[
'2001-01',
'2020-09',
'Long-term migrant',
'Arrivals',
'Female',
'0-4 years',
'344',
'0',
'Final'
]
...
[
'2021-09',
...
'70',
'Provisional'
]
finished
تم تحويل جميع الصفوف في ملف CSV إلى مصفوفات باستخدام تحويل التيار csv-parse
. نظرًا لأن تسجيل البيانات يحدث في كل مرة يتم فيها استقبال مقطع من التيار، يبدو البيانات كما لو كانت تُنزل بدلاً من عرضها دفعة واحدة.
في هذه الخطوة، قمت بقراءة البيانات في ملف CSV وتحويلها إلى مصفوفات. في الخطوة التالية، ستقوم بإدراج البيانات من ملف CSV إلى قاعدة البيانات.
الخطوة 3 — إدراج البيانات في قاعدة البيانات
إدراج البيانات من ملف CSV في قاعدة البيانات باستخدام Node.js يمنحك وصولًا إلى مكتبة واسعة من الوحدات التي يمكنك استخدامها لمعالجة أو تنظيف أو تحسين البيانات قبل إدراجها في قاعدة البيانات.
في هذا القسم، ستقوم بإنشاء اتصال مع قاعدة بيانات SQLite باستخدام وحدة node-sqlite3
. ستقوم بعد ذلك بإنشاء جدول في قاعدة البيانات، ونسخ ملف readCSV.js
، وتعديله لإدراج جميع البيانات المقروءة من ملف CSV في قاعدة البيانات.
أنشئ وافتح ملف db.js
في محرر النصوص الخاص بك:
في ملف db.js
الخاص بك، أضف الأسطر التالية لاستيراد الوحدات fs
و node-sqlite3
:
في السطر الثالث، قم بتعريف مسار قاعدة البيانات SQLite وتخزينه في المتغير filepath
. ملف قاعدة البيانات لا يوجد بعد، لكنه سيكون مطلوبًا لـ node-sqlite3
لإنشاء اتصال مع قاعدة البيانات.
في نفس الملف، أضف الأسطر التالية لربط Node.js بقاعدة بيانات SQLite:
هنا، تعرّف دالة تُسمى connectToDatabase()
لإنشاء اتصال بقاعدة البيانات. داخل الدالة، تقوم باستدعاء طريقة existsSync()
من وحدة fs
في عبارة if
، التي تتحقق مما إذا كان ملف قاعدة البيانات موجودًا في دليل المشروع. إذا كانت شرط if
صحيحة، فإنك تنشئ كائن فئة Database()
من وحدة node-sqlite3
باستخدام مسار قاعدة البيانات. بمجرد إنشاء الاتصال، تقوم الدالة بإرجاع كائن الاتصال وتنتهي.
ومع ذلك، إذا كانت البيانات المحققة من البيانات الصحيحة (إذا كان ملف قاعدة البيانات غير موجود)، فسيتم تخطي التنفيذ إلى كتلة else
. في كتلة else
، تقوم بإنشاء كائن فئة Database()
بوجود مُعلمتين: مسار ملف قاعدة البيانات ورد استدعاء.
المُعامل الأول هو مسار ملف قاعدة البيانات SQLite، والذي يكون ./population.db
. المُعامل الثاني هو رد استدعاء سيُستدعى تلقائيًا عندما يتم بناء الاتصال بقاعدة البيانات بنجاح أو إذا حدث خطأ. يأخذ الرد استدعاء معه مُعامل error
، الذي يكون null
إذا كان الاتصال ناجحًا. ضمن الرد استدعاء، تُحقق عبارة if
مما إذا كان مُعامل الخطأ مُعينًا. إذا كانت القيمة صحيحة، فإن الرد استدعاء يُسجل رسالة خطأ وينتهي. إذا كانت القيمة خاطئة، فإنك تُسجل رسالة نجاح تؤكد أن الاتصال قد تم بنجاح.
حالياً، ينشئ كل من كتل if
و else
كائن الاتصال. تمرر وظيفة الرد عند استدعاء فئة Database
في كتلة else
لإنشاء جدول في قاعدة البيانات، ولكن فقط إذا لم يكن ملف قاعدة البيانات موجوداً. إذا كان ملف قاعدة البيانات موجوداً بالفعل، ستقوم الوظيفة بتنفيذ كتلة if
، والاتصال بقاعدة البيانات، وإرجاع كائن الاتصال.
لإنشاء جدول إذا لم يكن ملف قاعدة البيانات موجوداً، أضف الكود المظلل:
الآن يستدعي connectToDatabase()
وظيفة createTable()
، التي تقبل كائن الاتصال المخزن في متغير db
كوسيط.
خارج وظيفة connectToDatabase()
، تعرف وظيفة createTable()
، التي تقبل كائن الاتصال db
كمعلمة. تستدعي الوظيفة exec()
على كائن الاتصال db
والتي تأخذ عبارة SQL كمعلمة. تقوم العبارة SQL بإنشاء جدول يسمى migration
بـ 7 أعمدة. أسماء الأعمدة تتطابق مع العناوين في ملف migration_data.csv
.
أخيرًا، تستدعي وظيفة connectToDatabase()
وتصدر كائن الاتصال المُرجع من الوظيفة بحيث يمكن إعادة استخدامه في ملفات أخرى.
احفظ واخرج ملف db.js
.
بعد تأسيس اتصال قاعدة البيانات، ستقوم الآن بنسخ وتعديل ملف readCSV.js
لإدراج الصفوف التي قامت وحدة csv-parse
بتحليلها إلى قاعدة البيانات.
انسخ الملف وغيّر اسمه إلى insertData.js
باستخدام الأمر التالي:
افتح ملف insertData.js
في محرر النصوص:
أضف الكود المظلل التالي:
في السطر الثالث، قم بإستيراد كائن الاتصال من ملف db.js
وقم بتخزينه في المتغير db
.
في داخل دالة الاستدعاء المرتبطة بحدث data
المرفق بوحدة الـ fs
، قم بإستدعاء دالة serialize()
على كائن الاتصال. تضمن هذه الدالة إتمام عملية تنفيذ الاستعلام الخاص بقاعدة البيانات قبل بدء تنفيذ استعلام آخر، مما يمكن أن يساعد في منع حدوث سباقات قاعدة البيانات حيث يقوم النظام بتشغيل عمليات منافسة بشكل متزامن.
تقوم دالة serialize()
بإستدعاء دالة رد الاستدعاء. داخل الدالة الفرعية، قم بإستدعاء دالة run
على كائن الاتصال db
. الدالة تقبل ثلاثة وسائط:
-
الوسيط الأول هو عبارة SQL سيتم تمريرها وتنفيذها في قاعدة البيانات SQLite. تقبل دالة
run()
فقط عبارات SQL التي لا تعيد نتائج. عبارةINSERT INTO migration VALUES (?, ..., ?
تقوم بإدراج صف في الجدولmigration
، والعلامات?
هي علامات مكان تم استبدالها في وسيط الدالة الثاني لدالةrun()
. -
المعامل الثاني هو مصفوفة
[row[0], ... row[5], row[6]]
. في القسم السابق، تتلقى الطريقةparse()
شريحة من البيانات من التيار القابل للقراءة وتحولها إلى مصفوفة. نظرًا لأن البيانات تُستلم كمصفوفة، يجب عليك استخدام فهارس المصفوفة للوصول إلى قيم كل حقل مثل[row[1], ..., row[6]]
، إلخ. -
المعامل الثالث هو استدعاء رد الاتصال الذي يعمل عند إدراج البيانات أو في حالة حدوث خطأ. يقوم رد الاتصال بالتحقق مما إذا كان قد حدث خطأ ويسجل رسالة الخطأ. إذا لم يكن هناك أخطاء، فإن الوظيفة تسجل رسالة نجاح في وحدة التحكم باستخدام طريقة
console.log()
، مما يُعلمك بأن الصف تم إدراجه مع الهوية.
أخيرًا، احذف أحداث end
و error
من ملفك. بسبب الطبيعة الغير متزامنة لطرق node-sqlite3
، تُنفذ أحداث end
و error
قبل إدراج البيانات في قاعدة البيانات، لذا لم تعد مطلوبة.
احفظ وأغلق ملفك.
قم بتشغيل ملف insertData.js
باستخدام node
:
قد يستغرق الأمر بعض الوقت، ولكن يجب على node
إرجاع النتيجة التالية، اعتمادًا على نظامك:
OutputConnected to the database successfully
Inserted a row with the id: 1
Inserted a row with the id: 2
...
Inserted a row with the id: 44308
Inserted a row with the id: 44309
Inserted a row with the id: 44310
الرسالة، وخاصةً معرفات، تثبت أن الصف من ملف CSV تم حفظه في قاعدة البيانات.
يمكنك الآن قراءة ملف CSV وإدراج محتواه في قاعدة البيانات. فيما يلي، ستقوم بكتابة ملف CSV.
الخطوة 4 — كتابة ملفات CSV
في هذا القسم، ستقوم بإسترداد البيانات من قاعدة البيانات وكتابتها في ملف CSV باستخدام التدفقات.
قم بإنشاء وفتح writeCSV.js
في محرر النصوص الخاص بك:
في ملفك writeCSV.js
، أضف السطور التالية لاستيراد وحدات fs
و csv-stringify
وكائن اتصال قاعدة البيانات من db.js
:
تحويل الوحدة csv-stringify
يحول البيانات من كائن أو مصفوفة إلى تنسيق نص CSV.
ثم، أضف الأسطر التالية لتعريف متغير يحتوي على اسم ملف CSV الذي ترغب في كتابة البيانات إليه وتيار قابل للكتابة ستكتب البيانات إليه:
تأخذ طريقة createWriteStream
وسيطة تعيين اسم الملف الذي ترغب في كتابة تيار بياناتك إليه، وهو اسم الملف saved_from_db.csv
المخزن في المتغير filename
.
في السطر الرابع، تعرف متغير columns
، الذي يخزن مصفوفة تحتوي على أسماء رؤوس بيانات CSV. سيتم كتابة هذه الرؤوس في السطر الأول من ملف CSV عند بدء كتابة البيانات إلى الملف.
لا تزال في ملفك writeCSV.js
، أضف الأسطر التالية لاسترجاع البيانات من قاعدة البيانات وكتابة كل صف في ملف CSV:
أولاً، تستدعي طريقة stringify
بوجهة نظر كمعامل، والتي تنشئ تيار تحول. يقوم تيار التحويل بتحويل البيانات من كائن إلى نص CSV. الكائن الذي يتم تمريره إلى طريقة stringify()
لديه خاصيتان:
header
تقبل قيمة منطقية وتولّد رأسًا إذا تم تعيين القيمة المنطقية علىtrue
.columns
تأخذ مصفوفة تحتوي على أسماء الأعمدة التي ستكتب في السطر الأول من ملف CSV إذا تم تعيين الخيارheader
علىtrue
.
ثم، يتم استدعاء الطريقة each()
من كائن الاتصال db
بمعاملين. يكون المعامل الأول هو عبارة SQL select * from migration
التي تسترجع الصفوف واحدا تلو الآخر في قاعدة البيانات. العامل الثاني هو استدعاء رد الاتصال يتم عند كل مرة يتم فيها استرجاع صف من قاعدة البيانات. يأخذ رد الاتصال معه معاملين: كائن error
وكائن row
يحتوي على البيانات التي تم استرجاعها من صف واحد في قاعدة البيانات. داخل رد الاتصال، يتم التحقق مما إذا كان كائن الخطأ error
معينًا في البيانات الموجودة في البيانات في عبارة if
. إذا قيمت الشرط على true
، يتم تسجيل رسالة خطأ في وحدة التحكم باستخدام الطريقة console.log()
. إذا لم يحدث خطأ، يتم استدعاء الطريقة write()
على stringifier
، التي تكتب البيانات في التيار التحويلي stringifier
.
عندما تنتهي الطريقة each()
من التكرار، تبدأ الطريقة pipe()
على التيار التحويلي stringifier
في إرسال البيانات بشكل مجزأ وكتابتها في writableStream
. سيقوم التيار القابل للكتابة بحفظ كل مجزأ من البيانات في ملف saved_from_db.csv
. بمجرد كتابة جميع البيانات إلى الملف، ستقوم console.log()
بتسجيل رسالة نجاح.
الملف الكامل سيبدو الآن كما يلي:
احفظ وأغلق ملفك، ثم قم بتشغيل ملف writeCSV.js
في الطرفية:
ستتلقى الناتج التالي:
OutputFinished writing data
للتأكد من أن البيانات قد تمت الكتابة، قم بفحص محتويات الملف باستخدام أمر cat
:
القط
ستعيد كل الصفوف المكتوبة في الملف (تم تحريرها لأسباب الإيجاز):
Outputyear_month,month_of_release,passenger_type,direction,sex,age,estimate
2001-01,2020-09,Long-term migrant,Arrivals,Female,0-4 years,344
2001-01,2020-09,Long-term migrant,Arrivals,Male,0-4 years,341
2001-01,2020-09,Long-term migrant,Arrivals,Female,10-14 years,
...
يمكنك الآن استرداد البيانات من قاعدة البيانات وكتابة كل صف في ملف CSV باستخدام التيارات.
الاستنتاج
في هذه المقالة، قرأت ملف CSV وأدخلت بياناته في قاعدة بيانات باستخدام الوحدات النمطية node-csv
و node-sqlite3
. ثم قمت باسترداد البيانات من قاعدة البيانات وكتابتها في ملف CSV آخر.
يمكنك الآن قراءة وكتابة ملفات CSV. كخطوة قادمة، يمكنك الآن العمل مع مجموعات بيانات CSV كبيرة باستخدام نفس التنفيذ مع التيارات الكفوءة من حيث استخدام الذاكرة، أو قد تنظر في حزمة مثل event-stream
التي تجعل العمل مع التيارات أسهل بكثير.
لمزيد من المعلومات حول node-csv
، قم بزيارة وثائقهم مشروع CSV – حزمة CSV لـ Node.js. لمعرفة المزيد عن node-sqlite3
، قم بزيارة وثائقهم على Github. لمواصلة تطوير مهاراتك في Node.js، انظر كيفية البرمجة في سلسلة Node.js.