لقراءة مدخلات المستخدم في Node.js، أنشئ واجهة readline على process.stdin واسأل عبر rl.question(). لا توجد prompt() في Node.js: نسخة المتصفح تفتح نافذة حوار، وبرنامج الطرفية لا يملك نافذة. الشكل الحديث يستخدم readline/promises، حيث يعيد await rl.question('Name? ') السطر الذي كتبه المستخدم. أغلق الواجهة حين تنتهي، وإلا بقي البرنامج ينتظر مزيدًا من المدخلات.
طبعت question() سؤالها، وانتظرت سطرًا واحدًا من المدخلات، وأعادته دون محرف السطر الجديد، فطبع البرنامج Next year you will be 37. لطرح عدة أسئلة، انتظر question() واحدة تلو الأخرى.
الدالة question() في readline مع callback
الواجهة الأصلية، الموجودة في كل نسخ Node.js، تمرر الإجابة إلى دالة callback. والأسئلة المتتابعة تعني دوال callback متداخلة:
طبع السؤال، ثم You picked blue. الإجابة نص عادي، فقصّ المسافات منها قبل المقارنة، وإلا فالمستخدم الذي يكتب مسافة بعد الكلمة يحصل على 'blue '، وهي لا تساوي 'blue'.
القراءة سطرًا سطرًا
لمعالجة كل سطر حتى تنتهي المدخلات، مرّ على الواجهة بـ for await. هكذا يقرأ البرنامج قائمة يكتبها المستخدم (ينهيها بـ Ctrl+D) أو ملفًا يُمرَّر إليه بـ node index.js < numbers.txt. الشيفرة الأقدم تفعل الشيء نفسه بالأحداث، rl.on('line', fn) لكل سطر وrl.on('close', fn) في النهاية؛ والحلقة هي هذان الحدثان مكتوبين بالترتيب من الأعلى إلى الأسفل:
طبع 3 numbers, total 49، وانتهت الحلقة من تلقاء نفسها عند انتهاء المدخلات، فلا حاجة إلى close(). أضف سطر إدخال رابعًا، 1، فتصبح النتيجة 4 numbers, total 50.
قراءة stdin كاملًا دفعة واحدة
للمدخلات القادمة عبر أنبوب من برنامج آخر (cat data.txt | node index.js)، القراءة في استدعاء واحد أبسط. واصف الملف 0 هو الإدخال القياسي. الملف المحفوظ على Windows قد تنتهي أسطره بـ \r\n، لذا قسّم على /\r?\n/ حين قد تأتي المدخلات من هناك:
طبع العناصر الثلاثة في عمود. يحجب readFileSync(0) التنفيذ حتى تكتمل المدخلات، وهذا مناسب لسكربت يقرأ من أنبوب ومناسب للاختبارات، لكنه خطأ في أداة تفاعلية، لأن المستخدم سيكتب كل شيء قبل أن يرى أي سؤال.
أيها تستخدم
الطرق الأربع تقرأ الإدخال القياسي نفسه. الفرق في متى تحصل شيفرتك على النص: إجابة في كل مرة، أو سطرًا في كل مرة، أو كل شيء في النهاية.
| الحاجة | استخدم |
|---|---|
| طرح بضعة أسئلة بالترتيب | readline/promises مع await rl.question() |
| دعم نسخ Node.js الأقدم من 17 | readline مع callback في question() |
| معالجة كل سطر فور وصوله | for await (const line of rl) |
| قراءة أنبوب أو ملف كامل من stdin | fs.readFileSync(0, 'utf8') |
| قيم معروفة قبل بدء البرنامج | process.argv بدلًا من ذلك |
أخطاء شائعة
نسيان rl.close(). تُبقي الواجهة الإدخال القياسي مفتوحًا، فلا ينتهي البرنامج أبدًا بعد السؤال الأخير. أغلقها بعد أن تحصل على كل الإجابات.
استخدام الإجابة كرقم. مثل كل ما يُكتب في الطرفية، الإجابة نص. '36' + 1 تساوي '361'؛ حوّل بـ Number() أولًا.
تمرير عدة إجابات عبر أنبوب إلى عدة أسئلة. حين تأتي المدخلات من أنبوب لا من لوحة المفاتيح، تقرأ readline كل شيء دفعة واحدة، والسطر الذي يصل قبل طرح سؤاله عبر question() يضيع. للمدخلات القادمة عبر أنبوب، استخدم حلقة for await أو readFileSync(0).
إغفال الخيار output. دون output: process.stdout لا تطبع question() سؤالها أبدًا، ويحدّق المستخدم في طرفية فارغة. حلقة القراءة سطرًا سطرًا لا تحتاجه، لأنها لا تطبع شيئًا.
الأسئلة الشائعة
لماذا prompt غير معرّفة في Node.js؟
prompt() دالة في المتصفح تفتح نافذة حوار، ولا يملك Node.js نافذة يفتحها فيها. استخدم readline في Node.js، أو حزمة مثل prompts أو inquirer للقوائم وحقول كلمات المرور.
كيف أقرأ كلمة مرور دون إظهارها؟
تعرض readline كل مفتاح يُضغط. تخفي حزم مثل prompts أو @inquirer/password المدخلات؛ وفعل ذلك بنفسك يعني تحويل الطرفية إلى الوضع الخام بـ process.stdin.setRawMode(true) ومعالجة كل مفتاح.
هل أستخدم readline أم process.argv؟
استخدم process.argv حين تكون القيم معروفة قبل بدء البرنامج، فيمكنه العمل في سكربت دون وجود شخص. واستخدم readline للأدوات التفاعلية التي تطرح سؤالًا تلو الآخر.