9 tema. I/O


 System.in ir System.out
 InputStream ir OutputStream
 Klasė File
 Reader ir Writer
 Foto peržiūra
 Užduotis

System.in ir System.out

Vos pradėję gilintis į Java programų tankmes tikriausiai pastebėjote, kad duomenų įvedimą iš klaviatūros ir išvedimą į ekraną aprašydavome gana skirtingai. Iš tikrųjų, kad pamatytume skaičių ar eilutę ekrane pakakdavo užrašyti:
System.out.println(eilutė);
Tačiau norėdami net ir labai paprastus duomenis įvesti iš klaviatūros programos pradžioje rašydavome gana mįslingas eilutes:
BufferedReader br = new BufferedReader (
new InputStreamReader (System.in)); 
Kur gi čia šuo pakastas? Kodėl ir iš klaviatūros duomenų negalėtume įvedinėti naudodamiesi, pavyzdžiui, tokiomis komandomis:
System.in.read();  
Galėtume. Iš tiesų rašant Java programas galime naudotis ne tik paprastu sisteminiu duomenų išvedimo į ekraną srautu System.out bet ir analogišku duomenų įvedimo iš klaviatūros srautu System.in. Pasižiūrėkime, pavyzdžiui, kaip šie srautai ,,veikia" tokioje visai paprastoje programoje:
public class SystIvIsv
{ public static void main (String [] args)
  throws Exception 
  {int iv;
   while(true){iv=System.in.read();
   System.out.println(iv);
  } } 
Atsisiųskite, sukompiliuokite ir išbandykite programą.

Paleidę programą veikti pastebėsite, kad ji veikia kažkaip keistai. Jūs įvedate simbolius, o ekrane gaunate skaičių stulpelį. Be to tas stulpelis visada baigiasi skaičiais 13 ir 10. Kas gi čia vyksta?
O vyksta štai kas. Kuklus objektas System.in baitas po baito skaito iš mūsų įvedamų duomenų eilutės, perskaitytą baitą kaip int skaičių priskiria kintamajam iv, o po to srautas System.out šį skaičių išveda į ekraną. Mįslingi skaičiai 13 ir 10 stulpelio pabaigoje gaunami perskaičius eilutės pabaigos simbolį, kurio ekrane nematome. Taigi šioje programėlėje į ekraną yra išvedami mūsų įvedamų simbolių kodai. Kad taip ir yra galime įsitikinti įvykdę tokią šiek tiek patobulintą programą.
public class SystInOut
{ public static void main (String [] args)
 throws Exception 
  {int iv;
   String st=""; 
   while(true){iv=System.in.read();
   System.out.println((char)iv+": "+iv );}
  } } 
Ar negalėtume naudodamiesi vien srautu System.in pasiekti, kad surinkę, pavyzdžiui, klaviatūroje 123 programai perduotume ne trijų simbolių kodus, bet skaičių 123? Galime. Štai pavyzdys:
public class SystInOut1
{ public static void main (String [] args)
 throws Exception 
  {int iv;
   String st=""; 
  while ((iv=System.in.read())!=13)
  st=st+(char)iv;
  iv=Integer.parseInt(st);
  System.out.println(iv);
  System.in.read();
  }} 
Atsisiųskite, sukompiliuokite ir išbandykite programą.

Srautas System.in tol skaito baitą po baito, kol sutinka baitą, kuriame saugomas skaičius 13 - tai įvedimo eilutės pabaigos simbolis. Iš perskaitytų kodų simbolių programa suformuoja eilutę, kuri interpretuojama kaip sveikasis skaičius. Aišku, kad taip naudotis srautu System.in nelabai patogu. Patogumus mums sukuria būtent šios komandos:
BufferedReader br = new BufferedReader (
new InputStreamReader (System.in)); 
Jomis iš pradinio srauto System.in suformuojamas toks įvedimo srautas, kad mes juo naudodamiesi galime programai perduoti duomenis ne baitas po baito, bet visą įvedamą simbolių visumą. Be to įvedimui paspartinti sukuriamas buferis - vidinės atminties sritis, kurioje formuojama įvedamų duomenų visuma.

   Į pradžią   

InputStream ir OutputStream

Programose duomenis tenka skaityti ir iš klaviatūros, ir iš failų ir iš, pavyzdžiui, masyvo. Ir rašyti duomenis taip pat prireikia ne vien tik į ekraną. Visais atvejais skaitymo (ir duomenų) rašymo procesai turi ir panašumų ir skirtumų. InputStream ir OutputStream yra abstrakčios klasės, kuriose aprašyti bendrieji visiems įvedimo ir išvedimo srautams metodai. Klasės yra abstrakčios - tai reiškia, kad negalima sukurti jų egzempliorių (kaip negalima, apskritai, išauginti abstraktaus obuolio - kiekvienu atveju tai bus konkrečios rūšies obuolys). Tačiau galima sukurti šių abstrakčių klasių modifikacijas, pritaikytas konkrečioms įvedimo ar išvedimo aplinkybėms. Labai svarbu išmokti įvesti duomenis iš failų ir išvesti juos į failus. Todėl panagrinėsime, kaip sukurti atitinkamus duomenų įvedimo ir išvedimo srautus bei kaip jais naudotis.

Panagrinėkime tokią programą. Ji perskaito duomenis iš failo ir išveda į ekraną. Programa vykdoma įvedus komandą
 java InOutFiles failo_vardas 
Iki šiol rašėme tik tokias programas, kurioms nereikdavo nurodyti parametrų. Tačiau visada pagrindinio metodo aprašymą pradėdavome taip
 public static  void main (String args[]) {...} 
Tai reiškia, kad į pagrindinį metodą gali būti kreipiamasi nurodant String tipo argumentus; jie programoje interpretuojami kaip argumentų masyvo args[] elementai. Mūsų atveju tėra tik vienas šio masyvo elementas - failo, iš kurio įvesime duomenis vardas.
 import java.io.*;
public class InOutFiles {
public static void main (String args [] )throws IOException
{if (args.length!=1) Tikrina, ar nurodytas tik vienas argumentas 
{System.out.println("Reikia nurodyti failo varda!");
return;} Jeigu argumentas ne vienas, išveda įspėjimą 
try{
InputStream ived = new FileInputStream(args[0]);Sukuria įvedimo iš failo srautą  
int sk = ived.available(); Suranda, kiek baitų yra faile  
byte mas[]= new byte[sk];  Kiek yra baitų, tiek bus masyvo elementų 
ived.read(mas); Srautas skaito baitą po baito ir priskiria masyvo elementams  
String eil = new  String (mas); Iš masyvo sukuriama viena eilutė  
System.out.println(eil); Eilutė išspausdinama  
ived.close();} Srautas uždaromas  
catch(IOException e) {}	
}}
 
Atsisiųskite, sukompiliuokite ir išbandykite programą.

Papildysime šią programą duomenų išvedimo į failą srautu. Dabar norėdami pradėti vykdyti programą, turime nurodyti du parametrus: failų vardus: iš kurio įvedame duomenis ir į kurį surašome.
import java.io.*;
public class InOutFiles {
	
public static void main (String args [] )throws IOException
{if (args.length!=2)
{System.out.println("Reikia nurodyti failo varda!");
return;}
try{
InputStream ived = new FileInputStream(args[0]);
int sk = ived.available();
byte mas[]= new byte[sk];
ived.read(mas);
String eil = new  String (mas);
System.out.println(eil);
ived.close();
OutputStream isved = new FileOutputStream(args[1]);
isved.write(mas);
isved.close();}
catch(IOException e) {}	
}}
Programoje naudojome du duomenų srautus: vienas jų InputStream modifikacija skaitymui iš failų, kitas - OutputStream modifikacija rašymui į failus. Abu srautai ,,dirba" su baitais, t.y. skaito ar rašo baitas po baito. Panaudojome kelis šių srautų metodus. Jų yra ir daugiau. Apžvelgsime svarbiausius metodus lentelėje.

InputStream metodai
int available( ) Nurodo, kiek galima perskaityti baitų.
void close( ) Uždaro srautą
void mark (int N) ,,Pažymi" baitą; prie šio baito galima bus sugrįžti,
jeigu po pažymėjimo bus perskaityta nedaugiau N baitų.
void reset( ) Grįžta prie pažymėto baito
int read( ) Perskaito sekantį baitą. Jeigo nėra ko skaityti,
grąžina reikšmę -1.
int read(byte [ ] b) Perskaito iki b.length baitų į masyvą b; grąžina perskaitytų
baitų skaičių; jeigu pasiekia failo pabaigą, grąžina -1.
int read (byte [ ] b, int n , int l) Į masyvą b užrašo iki l baitų,
n - pirmojo masyvo elemento, į kurį rašoma numeris.
long skip (long l) Praleidžia sraute (neskaito) l baitų.


OutputStream metodai
void close( ) Uždaro srautą
void flush( ) Išsiunčia iš buferio visus ten esančius baitus
void write( ) Užrašo sekantį baitą.
void write(byte [ ] b) Užrašo baitų masyvą.
void write (byte [ ] b, int n , int l) Užrašo masyvo b l baitų, pradedant n - uoju.

   Į pradžią   

Klasė File

Jeigu jau pradėjome nagrinėti skaitymą iš failų ir rašymą į juos bus pravartu geriau susipažinti, kaip Java darbuojasi su failais. Failas kūniškai egzistuoja (arba neegzistuoja) kompiuterio atmintyje. Kad programa galėtų užmegzti su juo ryšį, turime sukurti ,,programišką" to failo atitikmenį, t.y. klasės File objektą. Šios klasės konstruktoriai yra labai paprasti:
 
public File (String kelias)
public File (String kelias, String vardas)
Iš aprašymo aišku, kad konstruktoriaus parametras - kelias prie failo, arba tas kelias į failo katalogą atskirai, o failo vardas atskirai. Jeigu nurodysime tik failo vardą, tai bus laikoma, kad failo reikia ieškoti darbiniame kataloge, t.y. tame pačiame kataloge, kaip ir pagrindinė programos klasė. Jeigu programoje sukuriame File klasės objektą tai dar nereiškia, kad failas ,,kūniškai" egzistuoja. Beje File klasės objektas nebūtinai yra failas, gali būti ir katalogas. Informaciją apie File klasės objektą galime gauti kreipdamiesi į jo metodus. Kokie yra metodai (dalis jų) ir kaip į juos kreipiamasi galite sužinoti iš šios programos.
import java.io.*;
public class FilesMetodai {
 
public static void main (String args[]) 
throws IOException {
String fvardas,st;
fvardas="FilesMetodai.java";
File f = new File(fvardas);	

System.out.println("Ar failas "+fvardas+ " egzistuoja?");
System.out.println(f.exists());	

System.out.println("Gal tai  katalogas?");
System.out.println(f.isDirectory());

System.out.println("Koks failo vardas?");
System.out.println(f.getName());

System.out.println("Koks kelias is darbinio katalogo prie failo?");
System.out.println(f.getPath());

System.out.println("Koks absoliutus kelias prie failo?");
System.out.println(f.getAbsolutePath());

System.out.println("Ar galima ji skaityti?");
System.out.println(f.canRead());

System.out.println("Ar galima i ji rasyti?");
System.out.println(f.canWrite());

System.out.println("Kada jis modifikuotas?");
System.out.println("Po "+f.lastModified()+ " milisekundziu  nuo 1970 metu sausio 1 dienos");

System.out.println("Koks jo dydis baitais?");
System.out.println(f.length());
}}
 
Atsisiųskite, sukompiliuokite ir įvykdykite programą. Modifikuokite ją taip, kad nagrinėjamo failo vardą galėtumėte įvesti kaip programos paleidimo parametrą.

Jeigu File klasės objekto, kurį programoje sukūrėte ,,kūniškai" nėra, galite jį sukurti. Pavyzdžiui, taip darbiniame kataloge sukursime failą failas.text, kuris atitinka File klasės objektą f:
 File f = new File ("failas");
 try {f.createNewFile();}
 catch (IOException e) {}
 
Gali atsitikti, kad failo nepavyks sukurti, todėl reikia numatyti, kaip turi būti reaguojama į tokią išimtinę situaciją (net jeigu reagavimas reikštų, kad nebus jokio reagavimo).
Jeigu norite, kad būtų sukurtas ne failas, bet katalogas, turite naudoti metodą mkdir(). Pavyzdžiui,
File kat = new File ("katalogas");
try {kat.mkdir();}
catch (IOException e) {}

O dabar įvykdykite programą FilesMetodai su tokiu failo vardu:
 fvardas = "."; 

Gavote gana savotiškus duomenis. Apie ką jie? Apie jūsų darbinį katalogą. Kai kurie metodai nesuteikė jokios informacijos - gavote nulius. Tačiau kai File klasės objektas yra katalogas, galime kreiptis į keletą įdomių metodų. Pavyzdžiui, metodas
 String [] list()
 
patalpina į String tipo duomenų masyvą katalogo failų vardus. Štai pavyzdys:
 
import java.io.*;
public class Files {

public static void main (String args[]) 
throws IOException {	
File d = new File("."); 
String vard [] = d.list();	

System.out.println(vard.length);
for (int i=0; i<vard.length; i++)
System.out.println(vard[i]);
}}
Atsisiųskite, sukompiliuokite ir įvykdykite programą.

Tikriausiai gavote didelį sąrašą failų. Kaip padaryti, kad būtų sudarytas ,,filtruotas" failų sąrašas, t.y. į sąrašą būtų įtraukti tik mums reikalingi failai, pavyzdžiui, su plėtiniu .java? Kad gautume tokį filtruotą sąrašą, turime kreiptis į metodą, nurodydami ,,filtravimo objektą" FilenameFilter. Pasižiūrėkime į pavyzdį.
import java.io.*;
public class FilesFiltr {
File d = new File("."); 
String sarasas [];	

public static void main (String args[]) 
throws IOException {	
new FilesFiltr();}	

FilesFiltr () {
String sarasas [] = d.list(new Filtras ());
for (int i=0; i<sarasas.length; i++)
System.out.println(sarasas[i]);	
}	

class Filtras implements FilenameFilter{
public boolean accept (File f, String vard)
{return vard.endsWith("java");}
}}
Atsisiųskite, sukompiliuokite ir įvykdykite programą. Šioje programoje filtras tai klasės Filtras objektas. Šioje klasėje yra tik vienas metodas - accept. Jis grąžina reikšmę true, kai failas su vardu vard yra įtraukiamas į sąrašą. O jis įtraukiamas tada, kai eilutė vard baigiasi "java". Žinoma, galima metodą apibrėžti ir kitaip. Pavyzdžiui, galima tikrinti, ar failo vardas prasideda didžiąja (mažąja) raide ir taip toliau.

   Į pradžią   

Reader ir Writer

Nagrinėtieji srautai FileInputStream ir FileOutputStream skaito ir rašo duomenis baitas po baito. Tikriausiai visi verčiau norėtume galvoti apie iš įprastinių simbolių sudarytų duomenų skaitymą ir rašymą, o ne baitų srautų. Jeigu naudosime java.io paketo klases Reader ir Writer, tokią galimybę turėsime. Simbolių srautų skaitymui iš failų ir rašymui į juos skirtos klasės FileReader ir FileWriter.
Klasės FileReader objektą galime sukurti pasinaudoję konstruktoriais:
public FileReader (String fizinio_failo_vardas)
public FileReader (File java_File_objekto_vardas)
Šie konstruktoriai skiriasi tuo, kad pirmuoju atveju nurodome failo, kuris ,,kūniškai" egzistuoja kompiuterio atmintyje vardą, o antruoju - atitinkamo File klasės objekto vardas.
Panašiai galime sukurti ir FileWriter klasės objektus:
public FileWriter (String fizinio_failo_vardas)
public FileWriter (String fizinio_failo_vardas, boolean prirasyti)
public FileWriter (File java_File_objekto_vardas)
Jeigu antrajame konstruktoriuje loginio kintamojo prirasyti reikšmė yra true tai rašant į failą, duomenys pridedami prie jau užrašytų duomenų.

Sukūrus FileReader ir FileWriter klasių objektus geriausia juos iš karto ,,pertvarkyti" į buferizuotus srautus. Buferizuoti srautai pagreitins programos darbą. Tą buferizuotų srautų sukūrimą galime atlikti taip:
FileReader skait = new FileReader( fizinio failo ar java objekto vardas );  
BufferedReader bufskait = new BufferedReader(skait);

FileWriter ras = new FileWriter( fizinio failo ar java objekto vardas );  
BufferedWriter bufras = new BufferedWriter(ras);
arba dar trumpiau, sujungiant dvi eilutes į vieną:
BufferedReader bufskait = new BufferedReader
(new FileReader( fizinio failo ar java objekto vardas ) );

BufferedWriter bufras = new BufferedWriter
(new FileWriter( fizinio failo ar java objekto vardas ));
Naudojant buferizuotus srautus, galima duomenis iš failo skaityti eilutėmis. Metodas
readLine()
nuskaito eilinę eilutę iš failo. Taigi eilutę iš failo su buferizuotu srautu bufskait galime perskaityti taip:
 
bufskait.readLine();
Naudojant buferizuotą rašymo srautą į failą duomenis taip pat galime rašyti eilutėmis. Norėdami, kad kiti duomenys būtų rašomi naujoje eilutėje, turime įrašyti perėjimo į naują eilutę simbolį. Tai atlieka metodas
public void newLine();
Taigi užrašyti eilutę į failą ir pereiti į kitą eilutę su srautu bufras galime taip:
 bufras.write(eilutė);
 bufras.newLine()
 

   Į pradžią   

Foto peržiūra

O dabar naudodamiesi visais išnagrinėtais įrankiais sukursime įdomesnę ir sudėtingesnę programą. Tai programa skirta fotografijų, kurios saugomos darbinio katalogo pakatologyje ,,foto" peržiūrai. Ten saugomų fotografijų sąrašas yra faile fotog.txt. Paspaudus mygtuką "Pradėti" šis sąrašas atsidurs Swing sąraše. Pasirinkus fotografiją ji bus parodyta. Galima parašyti komentarą ir jį išsaugoti. Komentarai bus surašyti faile koment.txt. Programos langa atrodo taip:

Atsisiųskite, išarchyvuokite ir įvykdykite programą. Panagrinėkite jos tekstą.

   Į pradžią   

Užduotis

1. Knygų sąrašas
Parašyti programą, kuria naudojantis į failą būtų galima rašyti duomenis apie knygas:
Autorius...
Pavadinimas...
Leidimo metai ...
Suprogramuoti knygos paiešką pagal autorių: duomenys iš pradžių perskaitomi iš failo, atrinktieji parodomi sąraše ir surašomi į atskirą failą.
2. Duomenys apie failus
Parašyti programą, kuri į sąrašą išvestų darbinio katalogo failų su nurodytu plėtiniu vardus. Pasirinkus failus iš sąrašo, duomenys apie juos (vardas, dydis...) turi būti surašomi į atskirą failą.
3. Testas
Parašyti programą, kuri iš anksčiau sukurto failo į programos langą išvestų klausimus. Atsakymams rašyti reikia sukurti teksto lauką. Klausimai kartu su atsakymais turi būti surašomi į atskirą failą.
4. Failų peržiūra
Parašyti programą, kuri į sąrašą išvestų visus darbinio katalogo failų vardus. Pasirinkus failą iš sąrašo į atskirą failą turi būti užrašomas šio failo vardas, dydis ir pirmoji šio failo eilutė.
5. Išlaidų apskaita
Faile turi būti saugomi įrašai apie išlaidas:
Maistas: 12 12.3 10 ir t.t.
Kelionė: ....
Pramogos: ....
Kita: .....
.............
Parašyti programą, kuri nurodžius išlaidų rūšį (Maistas, Kelionė ir kt.) atitinkamas eilutes iš failo išvestų į programos langą (pav. į TextArea sritį), o taip pat suskaičiuotų bendrą sumą ir užrašytų rezultatą į atskirą failą.

   Į pradžią