Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

io.Reader و io.Writer

جزء من قسم البرمجة كائنية التوجه في رحلة GO على Coddy. الدرس 73 من 107.

تُبرز المكتبة القياسية في Go التصميم القائم على الواجهات من خلال واجهتين أساسيتين: io.Reader وio.Writer. وتُشغّل هاتان الواجهتان البسيطتان كل شيء بدءًا من عمليات الملفات ووصولًا إلى الاتصالات الشبكية.

تتطلب الواجهة io.Reader طريقة واحدة فقط:

type Reader interface {
    Read(p []byte) (n int, err error)
}

يقرأ ما يصل إلى len(p) من bytes في الشريحة ويُرجع عدد bytes التي تمت قراءتها. وبالمثل، يعرّف io.Writer ما يلي:

type Writer interface {
    Write(p []byte) (n int, err error)
}

أي نوع implementing هذه الأساليب يعمل مع منظومة I/O بأكملها.

إليك نوعًا مخصصًا يطبّق io.Reader:

type RepeatReader struct {
    Char  byte
    Count int
    pos   int
}

func (r *RepeatReader) Read(p []byte) (int, error) {
    if r.pos >= r.Count {
        return 0, io.EOF
    }
    n := 0
    for n < len(p) && r.pos < r.Count {
        p[n] = r.Char
        n++
        r.pos++
    }
    return n, nil
}

يعمل هذا القارئ المخصّص الآن مع أي function تتوقع io.Reader:

reader := &RepeatReader{Char: 'A', Count: 5}
data, _ := io.ReadAll(reader)
fmt.Println(string(data))  // AAAAA

يتيح لك هذا التصميم كتابة دوال تقبل io.Reader أو io.Writer، مما يجعلها تعمل مع الملفات أو اتصالات الشبكة أو المخازن المؤقتة أو أي تطبيق مخصص.

challenge icon

التحدي

سهل

لنَبْنِ معالجًا مخصصًا لتدفق النصوص ينفّذ الواجهتين io.Reader وio.Writer! ستنشئ أنواعًا يمكنها تحويل النص أثناء مروره خلالها، موضحًا كيف تتيح هذه الواجهات الأساسية تركيبًا قويًا لعمليات الإدخال/الإخراج.

ستنظّم شيفرتك عبر ملفين:

  • streams.go: عرّف أنواع القارئ والكاتب المخصصة لديك.

    أنشئ بنية CountingReader تغلّف سلسلة نصية وتتتبّع عدد bytes التي تمت قراءتها. يجب أن تحتوي على حقول للسلسلة المصدر ومتتبّع position. نفّذ الأسلوب Read(p []byte) (int, error) الذي يقرأ bytes من المصدر إلى الشريحة المقدّمة، ويحدّث position، ويعيد io.EOF عند استنفاد المصدر.

    أنشئ بنية UppercaseWriter تجمع data المكتوبة وتحولها إلى أحرف كبيرة. يجب أن تخزّن النتيجة المتراكمة داخليًا. نفّذ الأسلوب Write(p []byte) (int, error) الذي يحوّل bytes الواردة إلى أحرف كبيرة ويخزّنها. أضف أسلوب Result() string لاسترداد النص المتراكم بالأحرف الكبيرة.

    أنشئ دوال المُنشئ NewCountingReader(source string) *CountingReader وNewUppercaseWriter() *UppercaseWriter لتهيئة أنواعك بصورة صحيحة.

  • main.go: اعرض عمل أنواع الإدخال/الإخراج المخصصة لديك مع المكتبة القياسية.

    اقرأ سلسلة نصية input، ثم استخدم CountingReader مع io.ReadAll لقراءة كل data. مرّر تلك data عبر UppercaseWriter باستخدام أسلوب Write الخاص به.

    اطبع النتائج بهذا التنسيق:

    Original: [input]
    Uppercase: [result from writer]
    Bytes read: [total bytes]

سيتم توفير input التالي:

  • سطر واحد من النص

على سبيل المثال، given:

Hello World

يجب أن يكون الناتج:

Original: Hello World
Uppercase: HELLO WORLD
Bytes read: 11

وبالنسبة إلى:

Go interfaces are powerful

يجب أن يكون الناتج:

Original: Go interfaces are powerful
Uppercase: GO INTERFACES ARE POWERFUL
Bytes read: 26

الفكرة الأساسية هي أن CountingReader يعمل بسلاسة مع io.ReadAll لأنه implements io.Reader. ستعمل أي function في منظومة Go تقبل io.Reader مع نوعك المخصص: تكمن قوة التصميم القائم على interface في ذلك.

جرّب بنفسك

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
)

func main() {
	// اقرأ الإدخال
	reader := bufio.NewReader(os.Stdin)
	input, _ := reader.ReadString('\n')
	// أزل السطر الجديد اللاحق إن وُجد
	if len(input) > 0 && input[len(input)-1] == '\n' {
		input = input[:len(input)-1]
	}

	// TODO: أنشئ CountingReader باستخدام سلسلة الإدخال
	// countingReader := NewCountingReader(input)

	// TODO: استخدم io.ReadAll لقراءة جميع البيانات من CountingReader الخاص بك
	// data, err := io.ReadAll(countingReader)

	// TODO: أنشئ UppercaseWriter
	// upperWriter := NewUppercaseWriter()

	// TODO: اكتب البيانات إلى UppercaseWriter الخاص بك
	// upperWriter.Write(data)

	// TODO: اطبع النتائج بالتنسيق المطلوب:
	// Original: [input]
	// الأحرف الكبيرة: [النتيجة من الكاتب]
	// البايتات المقروءة: [إجمالي البايتات]

	// عنصر نائب لتجنب خطأ الاستيراد غير المستخدم - أزله عند التنفيذ
	_ = io.EOF
	fmt.Println("Original:", input)
	// fmt.Println("Uppercase:", upperWriter.Result())
	// fmt.Println("Bytes read:", countingReader.Position)
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم Go عبر الإنترنت