Readline

من موسوعة حسوب
مراجعة 07:03، 19 نوفمبر 2018 بواسطة رهف-النجار (نقاش | مساهمات) (تعديل عنوان الصفحة)


الاستقرار 2: مستقر

توفّر الوحدة readline واجهةً برمجيةً لقراءة سطر واحد من البيانات من المجرى القابل للقراءة (Readable) (مثل process.stdin) كل مرَّة على حدة. يمكن الوصول إليها باستخدام الأمر التالي:

const readline = require('readline');

يوضح المثال البسيط التالي الاستخدام الأساسي للوحدة readline:

const readline = require('readline');

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

rl.question('What do you think of Node.js? ', (answer) => {


‎‎‎  // في قاعدة بيانات (answer) للتنفيذ: سجل الجواب
  
console.log(`Thank you for your valuable feedback: ${answer}`);

  rl.close();
});

حالما تُستدعى هذه الشيفرة، لن ينتهي تطبيق Node.js حتى تُغلَق الواجهة readline.Interface لأنّها تنتظر أن تُستقبل البيانات في المجرى input.

الصنف Interface

أُضيف في الإصدار: 0.1.104.

تُنشَأ نُسَخ الصنف readline.Interface باستخدام التابع readline.createInterface()‎. ترفق كل نسخة مع مجرى الدخل input الوحيد القابل للقراءة ومجرى الخرج output الوحيد القابل للكتابة. يُستخدم المجرى output لطباعة الأوامر التي يدخلها المستخدم والتي تصل وتُقرأ من مجرى input.

الحدث 'close'

أُضيف في الإصدار: 0.1.98.

يُطلق الحدث 'close' عندما يحدث واحد مما يلي:

  • يُستدعى التابع rl.close()‎ وقد تخلّت نسخة readline.Interface عن التحكم بمجريي الدخل input والخرج output.
  • يستقبل المجرى input الحدث 'end' الخاص به.
  • يستقبل مجرى الدخل input الإشارة EOT (نهاية الإرسال [end-of-transmission]) عبر ضغط المستخدم على المفتاحين  ‎<ctrl>-D‎.
  • يستقبل مجرى الدخل input الإشارة SIGINT عبر ضغط المستخدم على المفتاحين ctrl>-C> ولا يوجد مستمع للحدث 'SIGINT' مُسجَّل على نسخة readline.Interface.

تُستدعَى الدالة المستمعة (listener) دون تمرير أي وسائط.

تُنهى النسخة readline.Interface حالما يُطلق الحدث 'close'.

الحدث 'line'

أُضيف في الإصدار:0.1.98.

يُطلق الحدث 'line' كلّما استقبل المجرى input محرف نهاية السطر (المحرف n\ أو r\ أو ‎\r\n). هذا يحدث عادةً عندما يضغط المستخدم المفاتيح <Enter> أو <Return>.

تُستدعى الدالة المستمعة مع سلسلة نصية متضمنةً سطرًا وحيدًا من الدخل المُستقبل.

rl.on('line', (input) => {
  console.log(`Received: ${input}`);
});

الحدث 'pause'

أُضيف في الإصدار: 0.7.5.

يُطلق الحدث 'pause' عندما  يحدث واحد مما يلي:

  • يُوقف المجرى input مؤقتًا.
  • لم يُوقف المجرى input ولكنه استقبل الحدث 'SIGCONT'. (انظر الحدثين 'SIGTSTP' و 'SIGCONT'.)

تُستدعى الدالة المستمعة دون تمرير أي وسائط.

rl.on('pause', () => {
  console.log('Readline paused.');
});

الحدث 'resume'

أضيف في الإصدار: 0.7.5.

يُطلق الحدث 'resume' عندما يُستأنَف المجرى input.

تُستدعى الدالة المستمعة دون تمرير أي وسائط.

rl.on('resume', () => {
  console.log('Readline resumed.');
});

الحدث 'SIGCONT'

أضيف في الإصدار: 0.7.5.

يُطلق الحدث 'SIGCONT' عندما تكون عملية Node.js قد انتقلت مسبقًا إلى الخلفية باستخدام المفتاحين ‎<ctrl>-Z (أي عبر استلام الإشارة SIGTSTP) ثمَّ أعيدت إلى المقدمة باستخدام الدالة fg(1p‎)‎.

إذا كان مجرى الدخل input متوقف مؤقتًا قبل استلام الإشارة SIGTSTP، فلن يُطلق هذا الحدث.

تُستدعى الدالة المستمعة دون تمرير أي وسائط.

rl.on('SIGCONT', () => {

 //  المجرى تلقائيًا‎‎‎`prompt`‎‎ سوف تستأنف 


rl.prompt();
});

الحدث 'SIGCONT' ليس مدعومًا على أنظمة ويندوز.

الحدث 'SIGINT'

أضيف في الإصدار: 0.3.0.

يُطلق الحدث 'SIGINT' عندما يستلم المجرى input الإشارة SIGINT عبر الضغط على المفتاحين ‎<ctrl>-C‎. إذا لم يوجد هناك أي مستمع مسجل للحدث 'SIGINT' عندما يتلقى المجرى input الإشارة SIGINT، فسيُطلق الحدث 'pause'.

تُستدعى دالة المنصت دون تمرير أي وسائط.

rl.on('SIGINT', () => {
  rl.question('Are you sure you want to exit? ', (answer) => {
    if (answer.match(/^y(es)?$/i)) rl.pause();
  });
});

الحدث 'SIGTSTP'

أُضيف في الإصدار: 0.7.5.

يُطلق الحدث 'SIGTSTP' عندما يتلقى المجرى input الإشارة SIGTSTP عبر ضغط المفتاحين ‎<ctrl>-Z‎‎. إذا لم يوجد هناك أي مستمع مسجل للحدث 'SIGTSTP' عندما يتلقى المجرى input الإشارة SIGTSTP، فستُرسل عملية Node.js إلى الخلفية.

عندما يُستأنَف البرنامج باستخدام الدالة fg(1p)‎، سيُطلق الحدثان 'pause' و 'SIGCONT'. يمكن استخدام ذلك لاستئناف المجرى input.

لن يُطلَق الحدثان 'pause' و'SIGCONT' إذا أُوقِف المجرى input قبل أن تُرسَل العملية إلى الخلفية.

تُستدعَى الدالة المستمعة دون تمرير أي وسائط.

rl.on('SIGTSTP', () => {

  // ويمنع البرنامج من الذهاب إلى الخلفية SIGTSTP هذا سيتجاهل الإشارة 
  console.log('Caught SIGTSTP.');

});

الحدث 'SIGTSTP' ليس مدعومًا على نظام ويندوز.

rl.close‎()‎

أضيف في الإصدار: 0.1.98.

يغلق التابع rl.close‎()‎ النسخة readline.Interface ويتخلى عن التحكم بمجريي الدخل input و الخرج output. عندما استدعائه، سيُطلَق الحدث 'close'.

لن يوقف استدعاء التابع rl.close‎()‎ إطلاق بقية الأحداث (بما فيها الحدث 'line') مباشرةً  من قِبَل النسخة readline.Interface.

rl.pause‎()‎

أضيف في الإصدار: 0.3.4.

يوقف التابع rl.pause‎()‎ المجرى input مؤقتًا، سامحًا له بالاستئناف لاحقًا عند الحاجة.

لن يوقف استدعاء rl.pause‎()‎ إطلاق بقية الأحداث (بما فيها الحدث 'line') مباشرةً  من قِبَل النسخة readline.Interface.

rl.prompt([preserveCursor‎]‎)‎

أضيف في الإصدار: 0.1.98.

  • preserveCursor:‏ <boolean> قيمة منطقية إن كانت true، فستمنع المؤشر من إعادة تعيين موضعه إلى 0 (بداية المجرى).

يكتب التابع rl.prompt‎(‎)‎ النُسخ readline.Interface التي ضُبِط فيها المحث prompt إلى سطر جديد في المجرى output من أجل تزويد المستخدم بموقع جديد ليدخل البيانات فيه.

عند استدعاء التابع rl.prompt‎(‎)‎، سيستأنف المجرى input إذا كان قد أُوقف مؤقتًا.

إذا أُنشئت النسخة readline.Interface مع ضبط المجرى output إلى القيمة null أو undefined، فلن يُكتَب على المحث (prompt).

rl.question(query, callback‎)‎

أضيف في الإصدار: 0.3.3.

  • Query:‏ <string> عبارة أو استعلام يرا كتابتها على المجرى output، مضافةً إلى بداية المِحَث.
  • Callback:‏ <Function> دالة رد نداء التي يراد استدعاؤها مع مدخلات المستخدم عند الاستجابة إلى الاستعلام query.

يعرض التابع rl.question()‎ قيمة الاستعلام query عن طريق كتابته على المجرى output، بعد أن ينتظر مدخلات المستخدم ليكتبها على المجرى input. بعد ذلك، يستدعي التابع دالة رد النداء مُمرّرًا المدخلات المستلمة من الستخدم إليها كأول وسيط.

عند استدعائه، سوف يستأنف rl.question()‎ المجرى input إذا كان قد أُوقف مؤقتًا.

ذا أُنشئت النسخة readline.Interface مع ضبط المجرى output إلى القيمة null أو undefined، فلن يُكتَب الاستعلام query.

اطلع على المثال التالي الذي يشرح ما سبق:

rl.question('What is your favorite food? ', (answer) => {
  console.log(`Oh, so your favorite food is ${answer}`);
});

لا تتبع دالة رد النداء المُمررة إلى التابع rl.question()‎ النموذج القياسي لقبول الكائن Error أو null كأول وسيط لها، إذ تُستدعَى دالة رد النداء مع تمرير الجواب الذي استُلِم من المستخدم كأول وسيط لها.

rl.resume()‎

أضيف في الإصدار: 0.3.4.

يستأنف التابع rl.resume()‎ المجرى input إذا أُوقِف مؤقتًا.

rl.setPrompt(prompt)‎

أضيف في الإصدار: 0.1.98.

  • Prompt:‏ <string>

سوف يضبط التابع rl.setPrompt()‎ المِحَث الذي سيكتب على المجرى output كلما استدعي التابع rl.prompt()‎.

rl.write(data[, key]‎)‎

أضيف في الإصدار: 0.1.98.

  • data:‏ <string>
  • key:‏ <Object>
    • Ctrl:‏ <boolean> قيمة منطقية إن كانت true فستشير إلى المفتاح ‎<ctrl>‎‎.
    • Meta:‏ <boolean> قيمة منطقية إن كانت true فستشير إلى المفتاح <Meta>.
    • Shift:‏ <boolean> قيمة منطقية إن كانت true فستشير إلى المفتاح ‎<Shift>‎‎‎.
    • Name:‏ <string> سلسلة نصية تمثِّل اسم المفتاح.

سيكتب التابع rl.write()‎ إمّا قيمة المعامل data أو تسلسل المفتاح المعرّف من قبل المعامل key إلى المجرى output. الوسيط key مدعومٌ إذا كانت TTY هي طرفية كتابة فقط.

إذا كان المعامل key مُحددًا، فسيُتجاهَل المعامل data.

عند استدعاء ‎rl.write()‎، سيستأنف المجرى input إذا كان قد أُوقف مؤقتًا.

إذا أُنشئت النسخة readline.Interface مع ضبط المجرى output إلى القيمة null أو undefined، فلن تُكتَب قيمة المعامل data أو المعامل key.

rl.write('Delete this!');
// لحذف السطر المكتوب سابقًا Ctrl+u محاكي ضغط المفتاحين 
rl.write(null, { ctrl: true, name: 'u' });

سوف يكتب التابع rl.write()‎ البيانات إلى المجرى input‏ كما أعطيت من قبل المستخدم تمامًا.

readline.clearLine(stream, dir)‎

أُضيف في الإصدار: 0.7.7.

  • stream:‏ <stream.Writable>
  • dir:‏ <number>
    • 1-: على يسار المؤشر
    • 1: على يمين المؤشر
    • 0: كل السطر

يمسح التابع readline.clearLine()‎ السطر الحالي من المجرى TTY المُعطى انطلاقًا من مؤضع المؤشر الحالي وباتجاهٍ محدَّدٍ مُعرّف عبر المعامل dir.

readline.clearScreenDown(stream)‎

أُضيف في الإصدار: 0.7.7.

  • stream:‏ <stream.Writable>

يمسح التابع readline.clearScreenDown()‎ مجرى TTY المعُطى من موقع المؤشر الحالي وبالاتجاه الأسفل.

readline.createInterface(options)‎

سجل التغييرات

الإصدار التغييرات
8.3.0, 6.11.4 إزالة الحدود الأعظمية للخيار crlfDelay.
6.6.0 أصبح الخيار crlfDelay مدعومًا الآن.
6.3.0 أصبح الخيار prompt مدعومًا الآن.
6.0.0 يمكن الآن أن يأخذ الخيار historySize القيمة 0.
0.1.98 أُضيف هذا التابع.
  • options:‏ <Object>
    • input:‏ <stream.Readable> المجرى القابل للقراءة المراد الاستماع إليه. هذا الخيار مطلوب.
    • output:‏ <stream.Writable> المجرى القابل للكتابة لكتابة بيانات سطرية فيه.
    • completer:‏ <Function> دالة اختيارية تستخدم للإكمال التلقائي عبر الضغط على المفتاح Tap.
    • terminal:‏ <boolean> قيمة منطقية تكون true إذا كان ينبغي معاملة المجريين output و input كأنهما TTY وأن يمتلكا شيفرات الهروب ANSI/VT100  مكتوبة له. القيمة الإفتراضية تكون بناءً على التحقق من isTTY على المجرى output.
    • historySize:‏ <number> العدد الأعظمي لأسطر التاريخ المحفوظة. لتعطيل التاريخ، اضبط قيمة هذا الخيار إلى 0. هذا الخيار له معنى فقط إذا كان الخيار terminal مضبوطًا إلى القيمة true من قبل المستخدم أو من قبل فحص المجرى output الداخلي. فيما عدا ذلك، لن تُهيأ آلية تخزين التاريخ مطلقًا. القيمة الإفتراضية: 30.
    • prompt:‏ <string> السلسلة النصية المراد استعمالها مع المِحَث. القيمة الإفتراضية هي:‎'‎>‎ ‎'‎.
    • crlfDelay:‏ <number>  إذا تجاوز التأخير بين المحرف ‎\r‎ والمحرف ‎\n‎ القيمة crlfDelay المعطاة بالميلي ثانية، فسيعاملان كمحرفي نهاية سطر منفصلين. لا يجب أن تقل قيمة الخيار crlfDelay عن 100 وستستعمل هذه القيمة إن كانت القيمة المعطاة أقل منها. يمكن أن يُضبَط الخيار إلى القيمة Infinity (قيمة لا نهائية)؛ في هذه الحالة، سيُعدُّ المحرف r\ عندما يُتبَع بالمحرف ‎\n‎ سطرًا جديدًا مفردًا دائمًا (هذا قد يكون منطقيًا من أجل قراءة الملفات مع الفاصل السطري ‎\‎r\‎n‎). القيمة الإفتراضية هي: 100.
    • removeHistoryDuplicates:‏ <boolean> قيمة منطقية إذا كانت true، فسيُزال سطر التأريخ القديم من القائمة عندما يضاف سطرٌ جديدٌ مماثل له (أي عند تكراره). القيمة الإفتراضية: false.

ينشئ التابع readline.createInterface()‎ نسخةً جديدةً من الصنف readline.Interface.

const readline = require('readline');
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

حالما تُنشأ النسخة readline.Interface، تكون الحالة الأكثر شيوعًا هي الاستماع إلى الحدث 'line':

rl.on('line', (line) => {
  console.log(`Received: ${line}`);
});

إذا كانت قيمة الخيار terminal هي true من أجل هذه النسخة، سيحصل المجرى output على أفضل توافق  إذا/أو عند تغير الأعمدة إذا عُرِّفت الخاصية output.columns وأطلق الحدث 'resize' على المجرى output (يفعل المجرى process.stdout ذلك تلقائيًا عندما يكون TTY).

استخدام دالة `completer`

تأخذ الدالة completer السطر الحالي المُدخَل من قبل المستخدم كوسيط، وتعيد المصفوفة Array مع مدخلتين:

مصفوفة Array مع مدخلات مطابقة للإكمال، و

السلاسل النصية الفرعية التي استخدمت للمطابقة.

على سبيل المثال: [[substr1, substr2, ...], originalsubstring].

function completer(line) {
  const completions = '.help .error .exit .quit .q'.split(' ');
  const hits = completions.filter((c) => c.startsWith(line));
  // تعرض كل احتمالات الإكمال إذا وجدت
  return [hits.length ? hits : completions, line];
}

يمكن أن تُستدعَى الدالة completer بشكل غير متزامن إذا قبلت وسيطين مثل:

function completer(linePartial, callback) {
  callback(null, [['123'], linePartial]);
}

readline.cursorTo(stream, x, y)‎

أُضيف في الإصدار: 0.7.7.

  • stream:‏ <stream.Writable>
  • x:‏ <number>
  • y:‏ <number>

ينقل التابع readline.cursorTo()‎ المؤشر إلى موضع محدد في المجرى stream المعطى الذي من النوع TTY.

readline.emitKeypressEvents(stream[, interface]‎)‎

أُضيف في الإصدار: 0.7.7.

  • stream:‏ <stream.Readable>
  • interface:‏ <readline.Interface>

يسبب التابع readline.emitKeypressEvents()‎ ببدء المجرى القابل للقراءة إطلاق الأحداث 'keypress' بما يتوافق مع المدخلات المُستقبلَة.

اختياريًّا، يحدِّد المعامل interface النسخة readline.Interface التي سيُعطَل الإكمال التلقائي فيها عندما تكتشف عملية نسخٍ ولصقٍ للمدخلات.

إذا كان المجرى stream من النوع TTY، فيجب حينئذٍ أن تكون في الوضع الخام (raw mode).

يُستدعى هذا التابع تلقائيًا من قبل أية نسخة من النوع readline على المجرى input الخاص بها إذا كان المجرى input هو طرفية. لن يوقف إغلاق النسخة readline المجرى input من إطلاق الأحداث 'keypress'.

readline.emitKeypressEvents(process.stdin);
if (process.stdin.isTTY)
  process.stdin.setRawMode(true);

readline.moveCursor(stream, dx, dy)‎

أُضيف في الإصدار: 0.7.7.

  • stream:‏ <stream.Writable>
  • dx:‏ <number>
  • dy:‏ <number>

ينقل التابع readline.moveCursor()‎ المؤشر نسبةً إلى موقعه الحالي في المجرى stream المعطى الذي من النوع TTY.

أمثلة

واجهة سطر أوامر صغيرة (Tiny CLI)

يوضّح المثال التالي كيفية استخدام الصنف readline.Interface لإنشاء واجهة سطر أوامر صغيرة:

const readline = require('readline');
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
  prompt: 'OHAI> '
});

rl.prompt();

rl.on('line', (line) => {
  switch (line.trim()) {
    case 'hello':
      console.log('world!');
      break;
    default:
      console.log(`Say what? I might have heard '${line.trim()}'`);
      break;
  }
  rl.prompt();
}).on('close', () => {
  console.log('Have a great day!');
  process.exit(0);
});

قراءة مجرى ملف سطرًا بسطر

الاستخدام الشائع للنسخة readline هو قراءة الدخل من مجرًى قابلٍ للقراءة لنظام الملفات سطرًا واحدًا في كل مرة:

const readline = require('readline');
const fs = require('fs');

const rl = readline.createInterface({
  input: fs.createReadStream('sample.txt'),
  crlfDelay: Infinity
});

rl.on('line', (line) => {
  console.log(`Line from file: ${line}`);
});

مصادر

صفحة الوحدة Readline في توثيق Node.js الرسمي.