CodeGym /Corsi /C# SELF /Stream di input/output: St...

Stream di input/output: Stream

C# SELF
Livello 36 , Lezione 1
Disponibile

1. Introduzione

Immagina un bollitore con l'acqua. Apri il rubinetto — l'acqua inizia a scorrere. Puoi riempire tanto e versare tutto in una volta, oppure riempire il bollitore poco a poco. Lo stesso vale per i file — non sempre è comodo o possibile caricare tutto il file in memoria subito. I file possono essere grandi, e a volte la fonte dei dati non è nemmeno un file, ma per esempio una connessione di rete, dove i dati arrivano pian piano.

Se provassimo sempre a lavorare solo con array di byte, con file grandi finiremmo subito la memoria, e per stream "infiniti" (tipo video o audio stream) questo approccio proprio non funziona. Ed è qui che arriva il concetto di stream!

In .NET uno stream è un'astrazione per accedere ai dati in modo sequenziale: non importa cosa c'è dietro — file, rete, memoria, o anche qualcosa di super strano tipo un archivio compresso. Lo stream ti permette di leggere e scrivere dati a pezzi, di solito a blocchi o byte.

Idea principale:

  • Stream — è un canale per trasferire dati. È come un nastro trasportatore: puoi "mettere" (scrivere) o "prendere" (leggere) dati, senza preoccuparti troppo di dove e come sono salvati.
  • I dati arrivano in sequenza: puoi leggere il prossimo pezzo solo dopo il precedente (o viceversa, se c'è il supporto per il seek).
  • Nella maggior parte dei casi non tieni tutti i dati in memoria (e il computer ti ringrazierà per questo).

Questa astrazione è alla base praticamente di tutte le operazioni di input/output in .NET: lavoro con file, reti, archivi, persino con la console!

2. Stream System.IO.Stream

Ereditarietà e architettura: System.IO.Stream

Quasi tutti gli stream in .NET ereditano dalla classe astratta System.IO.Stream. Questa definisce i metodi base per leggere, scrivere, spostarsi nello stream e gestirlo.


classDiagram
    class Stream {
        +Read()
        +Write()
        +Seek()
        +CanRead
        +CanWrite
        +CanSeek
        +Length
        +Position
    }
    class FileStream
    class MemoryStream
    class NetworkStream
    class CryptoStream
    Stream <|-- FileStream
    Stream <|-- MemoryStream
    Stream <|-- NetworkStream
    Stream <|-- CryptoStream
Schema di ereditarietà degli stream in .NET
  • Stream — classe base astratta
  • FileStream — per lavorare con i file
  • MemoryStream — per lavorare con dati in memoria
  • NetworkStream — per interazione di rete
  • CryptoStream — per cifrare/decifrare

Breve panoramica delle proprietà e dei metodi chiave dello stream

Proprietà / Metodo Descrizione
CanRead
Si può leggere da questo stream
CanWrite
Si può scrivere in questo stream
CanSeek
Si può spostarsi nello stream (non tutti lo supportano)
Length
Lunghezza dello stream (se supportata — non tutti gli stream ce l'hanno)
Position
Posizione attuale nello stream
Read(...)
Lettura dei dati
Write(...)
Scrittura dei dati
Seek(...)
Spostamento nello stream
Flush()
Svuota il buffer (scrive tutto quello accumulato nello stream)
Close()
/
Dispose()
Chiude lo stream e libera le risorse

Vediamo come appare "nella pratica".

3. Esempio: lettura e scrittura di file tramite Stream

Ecco un esempio minimal per vedere uno stream "in azione":


// Apriamo un file per scrivere
using var stream = new FileStream("numbers.bin", FileMode.Create);

// Supponiamo di voler scrivere i numeri da 1 a 10 nel file
for (int i = 1; i <= 10; i++)
{
    byte val = (byte)i;
    stream.WriteByte(val); // Scriviamo un byte alla volta
}

// Chiudiamo esplicitamente il file per poi riaprirlo in lettura
stream.Close(); 

// Ora proviamo a leggere questi numeri indietro
using var stream2 = new FileStream("numbers.bin", FileMode.Open);
int value;
while ((value = stream2.ReadByte()) != -1)
{
    Console.WriteLine(value); // Stampa 1, 2, ... 10
}

Qui usiamo FileStream, che è uno stream vero e proprio: leggi e scrivi dati a blocchi o a byte.

Tipi di stream: dove li puoi trovare?

Uno stream non è solo un file su disco. Ecco alcuni esempi dove si usa il concetto di stream:

  • File su disco (per esempio, FileStream — il caso più comune)
  • Stream in memoria RAM (MemoryStream — comodo per dati temporanei o intermedi)
  • Connessione di rete (NetworkStream)
  • Compressione/archiviazione (GZipStream, DeflateStream)
  • Cifratura (CryptoStream)
  • Input/output da console (sì, anche questi!) — tecnicamente, sono stream pure loro

Questo ti permette di scrivere codice senza preoccuparti della fonte/destinazione dei dati: se il tuo codice lavora con uno stream, allora è universale!

4. Dettagli utili

Lettura e scrittura sono operazioni di trasferimento dati a pezzi. Di solito tramite array di byte e i metodi Read, Write.

Esempio: lettura di un file a blocchi

byte[] buffer = new byte[1024]; // Buffer da 1024 byte (1 KB)
using var stream = new FileStream("bigfile.bin", FileMode.Open);

int bytesRead;
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
{
    // Gestiamo solo bytesRead byte dentro buffer
    int sum = 0;
    for (int i = 0; i < bytesRead; i++)
        sum += buffer[i];

    Console.WriteLine($"Somma del blocco: {sum}");
}

Questo approccio si usa ovunque — dagli antivirus ai player musicali.

Posizionamento nello stream (Position, Seek)

Nella maggior parte delle implementazioni di stream (tipo quelli dei file) puoi spostarti nei dati — leggere non solo il "prossimo pezzo", ma andare a una posizione precisa e lavorare da lì.

using var stream = new FileStream("numbers.bin", FileMode.Open);
stream.Position = 5; // Ci spostiamo al 6° byte (contando da 0)
int value = stream.ReadByte();
Console.WriteLine($"6° byte nel file: {value}");

Gli stream possono essere solo in lettura, solo in scrittura, o entrambi

Alcuni stream supportano solo una delle due modalità:

  • File aperto in scrittura: solo Write()
  • Stream per leggere dati di rete: solo Read()
  • In alcuni casi strani (tipo stream per stampante) non è proprio possibile "tornare indietro" o fare seek nello stream.

Controlla le operazioni supportate tramite le proprietà CanRead, CanWrite, CanSeek:

using var stream = new FileStream("myfile.txt", FileMode.OpenOrCreate);
if (stream.CanRead)
    Console.WriteLine("Lettura supportata");
if (stream.CanWrite)
    Console.WriteLine("Scrittura supportata");
if (stream.CanSeek)
    Console.WriteLine("Si può spostarsi nel file");

Bufferizzazione negli stream

Quasi tutti gli stream usano buffer interni per migliorare le prestazioni. Il buffering risparmia accessi a disco/rete: i dati si accumulano internamente e poi vengono dati/scritti in blocco.

Il metodo Flush() permette di svuotare il buffer (per esempio, per essere sicuri che tutto sia stato scritto su disco):

using var stream = new FileStream("log.txt", FileMode.Append);
byte[] bytes = Encoding.UTF8.GetBytes("Hello, Stream!\n");
stream.Write(bytes, 0, bytes.Length);
stream.Flush(); // Garantisce che la scrittura sia davvero fatta su disco

Se stai scrivendo dati importanti (tipo transazioni di pagamento!), chiamare Flush() è il tuo amico.

5. Errori tipici lavorando con gli stream

Molto spesso i principianti fanno questi errori:

Si dimenticano di chiudere lo stream (e si beccano memory leak, file "bloccati" e altre gioie).

Confondono stream di testo e binari — provano a scrivere una stringa con un metodo per byte, e poi ottengono "caratteri strani".

Usano un buffer troppo piccolo (o nessun buffer) — le operazioni diventano lente.

Pensano che Read() legga sempre esattamente il numero di byte richiesto — in realtà può restituire meno; bisogna sempre controllare il valore restituito.

Non considerano che non tutti gli stream supportano il seek (Seek), soprattutto quelli di rete.

Per esempio:


// Esempio sbagliato: leggere tutti i byte del file senza controllare quanti byte sono stati letti davvero
byte[] buffer = new byte[1024];
using (var stream = new FileStream("data.bin", FileMode.Open))
{
    int bytesRead = stream.Read(buffer, 0, 1024);
    // bytesRead può essere meno di 1024 se il file è più piccolo!
}
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION