Dnes navážeme na téma z minulého článku, ve kterém jsme si vysvětlili základní principy vstupů a výstupů. Ukážeme si dvě konkrétní aplikace vstupních a výstupních proudů – tvorbu filtrů vstupních dat a bufferování výstupu.

Filtrování vstupu

Následníky třídy FilterInputStream lze provádět filtrování vstupních dat pomocí uživatelsky definovaného filtru. Třída má jako parametr konstruktoru vstupní proud, ze kterého čte data určená k filtrování. Vlastní filtrování dat se provádí v metodách read(), které musí programátor implementovat.

Příklad 1: textový filtr

Následující příklad ukazuje textový filtrovací vstupní proud, který převádí znaky na velká písmena (VelkaPismenaInputStream.java):

import java.io.*;
public class VelkaPismenaInputStream extends FilterInputStream {
  // konstruktor volá standardní konstruktor
  public VelkaPismenaInputStream(InputStream in) {
    super(in);
  }
  // čtení jednoho znaku
  public int read() throws IOException {
    char ch = (char)in.read();            // načtení znaku
    return Character.toUpperCase(ch);     // převedení na velké písmeno
 }
 // čtení pole znaků
  public int read(byte[] b) throws IOException {
    int len = in.read(b, 0, b.length);
    for (int i=0; i<len; i++) b[i] = (byte)Character.toUpperCase((char)b[i]);
    return len;
  }
  // čtení části pole znaků dané počátkem a délkou
  public int read(byte[] b, int off, int len) throws IOException {
    len = in.read(b, 0, len);
    for (int i=0; i<len; i++) b[off+i] = (byte)Character.toUpperCase((char)b[off+i]);
    return len;
  } 
}

Příklad 2: použití filtru

Pro otestování filtru stačí nepatrná úprava příkladu copy z minulého článku. Jednalo se o aplikaci pro kopírování souborů (podle parametrů příkazového řádku). Pro úpravu použijeme popsané v minulém díle a řádek vstup = new FileInputStream(args[0]); nahradíme řádkem vstup = new VelkaPismenaInputStream(new FileInputStream(args[0]));. Tím vložíme do posloupnosti proudů filtr (data půjdou z FileInputStream do VelkaPismenaInputStream) a výsledkem bude program, který kopíruje soubor s převodem na velká písmena (copy2vp.java):

import java.io.*;
public class copy2vp {
  public copy2vp() {
  }
  public static void main(String[] args) {
    if (args.length<2) {
      System.out.println(„Syntaxe: java copy <soubor1> <soubor2>“);
      return;
    }
    InputStream vstup = null;
    OutputStream vystup = null;
    // otevreni prvního souboru pro čtení a založení vstupního proudu
    try {
      vstup = new VelkaPismenaInputStream(new FileInputStream(args[0]));
      } catch (IOException e) {
      System.out.println(„Soubor „+args[0]+“ se nepodarilo otevrit!“);
      return;
    }
      // otevření druhého souboru pro zápis a založení výstupního proudu
    try {
      vystup = new FileOutputStream(args[1]);
      } catch (IOException e) {
        System.out.println(„Soubor „+args[1]+“ se nepodarilo vytvorit!“);
        return;
      }
    // kopírování obsahu souboru
    byte data[] = new byte[1024];    // obsah suoboru přenášíme po 1kB blocích
    int pocet;
    try {
      while (vstup.available()>0) {
        pocet = vstup.read(data);    // počet skutečně přenesených bytů
        vystup.write(data, 0, pocet);
      }
    }
    catch (IOException ex) {
      System.out.println(„Nastala chyba pri kopirovani souboru!“);
    }
  }
}

Podobným způsobem je možné naprogramovat proud, který převádí znaky mezi různými kódováními češtiny, a z programu copy tak vytvořit převaděč znakových sad. Rovněž existuje třída FilterInputStream, která pro změnu filtruje výstupní data a v tomto příkladě by šla také použít.

Bufferování výstupu

Představte si problém: kódujete data, potřebujete poslat délku kódované zprávy a pak její obsah. Jak to ale vyřešit, pokud data zapisujete do výstupního proudu a na počátku tedy nevíte, kolik jich bude?!

Pomůže nám výstupní proud ByteArrayOutputStream. Jak pracuje? Do ByteArrayOutputStream zapisujete data, ale ten je neposílá dál, nýbrž si je uchovává ve vnitřním bufferu. Můžete je kdykoli vypsat do jiného výstupního proudu metodou writeTo(OutputStream out).

Příklad 3: kodujdelku.java

Pravděpodobně bychom takto kódovanou zprávu posílali například jiné aplikaci po síti, pro jednoduchost si však ukážeme, jak by se vypsala do souboru. Program vypíše svůj standardní vstup na standardní výstup s tím, že mu předchází počet bytů, kódovaný jako 4-bytový integer (kodujdelku.java):

import java.io.*;
public class kodujdelku {
  public kodujdelku() {
  }
  public static void main(String[] args) {
    DataOutputStream vystup = new DataOutputStream(System.out);
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    DataInputStream vstup = new DataInputStream(System.in);
    try {
      while (vstup.available()>0) buffer.write(System.in.read());
      vystup.writeInt(buffer.size());
      buffer.writeTo(vystup);
    }
    catch (IOException ex) {
      System.out.println(„Chyba pri cteni ze standardniho vstupu“);
      return;
    }
  }
}

Zkompilovaný program zavoláte z příkazového řádku: java interval.kodujdelku <vstup.bin >vystup.bin. Takto lze bufferování využívat pro počítání CRC, šifrování nebo třeba pro implementaci MP3 kodeku. Zkrátka kdekoli, kde dostáváme data postupně a chceme je zpracovat najednou.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Žádný příspěvek v diskuzi

Odpovědět