Menu
Coddy logo textTech

io.Reader ve io.Writer

Coddy'nin GO Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 73 / 107.

Go'nun standart kütüphanesi, iki temel arayüz aracılığıyla arayüz tabanlı tasarımı sergiler: io.Reader ve io.Writer. Bu basit arayüzler, dosya işlemlerinden ağ iletişimine kadar her şeye güç verir.

io.Reader arayüzü sadece bir metot gerektirir:

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

Dilime len(p) bayta kadar veri okur ve kaç bayt okunduğunu döndürür. Benzer şekilde, io.Writer şunu tanımlar:

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

Bu metotları uygulayan herhangi bir tür, tüm I/O ekosistemiyle çalışır.

İşte io.Reader arayüzünü uygulayan özel bir tür:

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
}

Artık bu özel okuyucu, io.Reader bekleyen herhangi bir fonksiyonla çalışır:

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

Bu tasarım, io.Reader veya io.Writer kabul eden fonksiyonlar yazmanıza olanak tanıyarak; bunların dosyalar, ağ bağlantıları, tamponlar veya herhangi bir özel uygulama ile çalışmasını sağlar.

challenge icon

Görev

Kolay

io.Reader ve io.Writer arayüzlerini uygulayan özel bir metin akışı işlemcisi oluşturalım! Metin içinden akarken onu dönüştürebilen türler oluşturacak ve bu temel arayüzlerin güçlü G/Ç (I/O) kompozisyonunu nasıl mümkün kıldığını göstereceksiniz.

Kodunuzu iki dosya halinde düzenleyeceksiniz:

  • streams.go: Özel okuyucu (reader) ve yazıcı (writer) türlerinizi tanımlayın.

    Bir dizeyi (string) saran ve kaç baytın okunduğunu takip eden bir CountingReader yapısı (struct) oluşturun. Kaynak dize ve bir konum izleyici için alanları olmalıdır. Kaynaktan gelen baytları sağlanan dilime (slice) okuyan, konumu güncelleyen ve kaynak tükendiğinde io.EOF döndüren Read(p []byte) (int, error) metodunu uygulayın.

    Yazılan verileri toplayan ve bunları büyük harfe dönüştüren bir UppercaseWriter yapısı oluşturun. Biriken sonucu dahili olarak saklamalıdır. Gelen baytları büyük harfe dönüştüren ve saklayan Write(p []byte) (int, error) metodunu uygulayın. Biriken büyük harfli metni almak için bir Result() string metodu ekleyin.

    Türlerinizi düzgün bir şekilde başlatmak için NewCountingReader(source string) *CountingReader ve NewUppercaseWriter() *UppercaseWriter yapıcı (constructor) fonksiyonlarını oluşturun.

  • main.go: Standart kütüphane ile çalışan özel G/Ç türlerinizi gösterin.

    Bir dize girişi okuyun, ardından tüm verileri okumak için io.ReadAll ile birlikte CountingReader yapınızı kullanın. Bu veriyi Write metodunu kullanarak UppercaseWriter yapınızdan geçirin.

    Sonuçları şu formatta yazdırın:

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

Aşağıdaki girdi sağlanacaktır:

  • Tek bir satır metin

Örneğin, şu verildiğinde:

Hello World

Çıktınız şöyle olmalıdır:

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

Ve şu verildiğinde:

Go interfaces are powerful

Çıktınız şöyle olmalıdır:

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

Buradaki temel nokta, CountingReader yapınızın io.Reader arayüzünü uyguladığı için io.ReadAll ile sorunsuz çalışmasıdır. Go ekosisteminde io.Reader kabul eden herhangi bir fonksiyon, özel türünüzle çalışacaktır; arayüz tabanlı tasarımın gücü budur.

Kopya kağıdı

Go'nun io.Reader ve io.Writer arayüzleri, G/Ç (I/O) işlemleri için temeldir:

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

Read metodu, dilim (slice) içine len(p) bayta kadar okuma yapar ve okunan bayt sayısını döndürür. Write metodu, dilimdeki baytları yazar ve yazılan bayt sayısını döndürür.

Bu metodları uygulayan herhangi bir tür, Go'nun tüm G/Ç ekosistemiyle çalışabilir. Örnek özel okuyucu:

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
}

Özel okuyucunun standart kütüphane fonksiyonlarıyla kullanımı:

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

Bu tasarım, io.Reader veya io.Writer kabul eden fonksiyonların dosyalar, ağ bağlantıları, arabellekler veya herhangi bir özel uygulama ile çalışmasına olanak tanır.

Kendin dene

package main

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

func main() {
	// Girdiyi oku
	reader := bufio.NewReader(os.Stdin)
	input, _ := reader.ReadString('\n')
	// Varsa sondaki yeni satır karakterini kaldır
	if len(input) > 0 && input[len(input)-1] == '\n' {
		input = input[:len(input)-1]
	}

	// TODO: Giriş dizesiyle bir CountingReader oluşturun
	// countingReader := NewCountingReader(input)

	// TODO: CountingReader'ınızdaki tüm verileri okumak için io.ReadAll kullanın
	// data, err := io.ReadAll(countingReader)

	// TODO: Bir UppercaseWriter oluşturun
	// upperWriter := NewUppercaseWriter()

	// TODO: Verileri UppercaseWriter'ınıza yazın
	// upperWriter.Write(data)

	// TODO: Sonuçları istenen formatta yazdırın:
	// Original: [input]
	// Uppercase: [result from writer]
	// Bytes read: [total bytes]

	// Kullanılmayan içe aktarma hatasını önlemek için yer tutucu - uygularken kaldırın
	_ = io.EOF
	fmt.Println("Original:", input)
	// fmt.Println("Uppercase:", upperWriter.Result())
	// fmt.Println("Bytes read:", countingReader.Position)
}
quiz iconKendini test et

Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.

Nesne Yönelimli Programlama bölümündeki tüm dersler