1. Mga stream ng data

Bihirang magkaroon ng isang programa bilang isang isla sa sarili nito. Karaniwang nakikipag-ugnayan ang mga programa sa "labas na mundo". Ito ay maaaring mangyari sa pamamagitan ng pagbabasa ng data mula sa keyboard, pagpapadala ng mga mensahe, pag-download ng mga pahina mula sa Internet, o, kabaligtaran, pag-upload ng mga file sa isang malayong server.

Maaari tayong sumangguni sa lahat ng mga pag-uugaling ito sa isang salita: pagpapalitan ng data sa pagitan ng programa at sa labas ng mundo. Teka, hindi lang iyon isang salita.

Siyempre, ang palitan ng data mismo ay maaaring nahahati sa dalawang bahagi: pagtanggap ng data at pagpapadala ng data. Halimbawa, nagbasa ka ng data mula sa keyboard gamit ang isang Scannerbagay — ito ay tumatanggap ng data. At nagpapakita ka ng data sa screen gamit ang isang System.out.println()command — ito ay nagpapadala ng data.

Sa programming, ang terminong "stream" ay ginagamit upang ilarawan ang pagpapalitan ng data. Saan nagmula ang katagang iyon?

Sa totoong buhay, maaari kang magkaroon ng isang stream ng tubig o isang stream ng kamalayan. Sa programming, mayroon kaming mga stream ng data .

Ang mga stream ay isang maraming nalalaman na tool. Pinapayagan nila ang programa na makatanggap ng data mula sa kahit saan (mga stream ng input) at magpadala ng data kahit saan (mga stream ng output). Kaya, mayroong dalawang uri:

  • Ang input stream ay para sa pagtanggap ng data
  • Ang isang output stream ay para sa pagpapadala ng data

Upang gawing 'nasasalat' ang mga stream, sumulat ang mga tagalikha ng Java ng dalawang klase: InputStreamat OutputStream.

Ang InputStreamklase ay may read()paraan na hinahayaan kang magbasa ng data mula rito. At ang OutputStreamklase ay may isang write()paraan na hinahayaan kang magsulat ng data dito. Mayroon din silang iba pang mga pamamaraan, ngunit higit pa sa na mamaya.

Mga byte stream

Anong uri ng data ang pinag-uusapan natin? Anong format ang kailangan nito? Sa madaling salita, anong mga uri ng data ang sinusuportahan ng mga klaseng ito?

Ito ay mga generic na klase, kaya sinusuportahan nila ang pinakakaraniwang uri ng data — ang byte. Ang isang OutputStreammaaaring magsulat ng mga byte (at mga byte array), at ang isang InputStreambagay ay maaaring magbasa ng mga byte (o mga byte array). Iyon lang — hindi nila sinusuportahan ang anumang iba pang uri ng data.

Bilang resulta, ang mga stream na ito ay tinatawag ding mga byte stream .

Ang isang tampok ng mga stream ay ang kanilang data ay maaari lamang basahin (o isulat) nang sunud-sunod. Hindi mo mababasa ang data mula sa gitna ng isang stream nang hindi binabasa ang lahat ng data na nauuna dito.

Ganito gumagana ang pagbabasa ng data mula sa keyboard sa buong Scannerklase: magbasa ka ng data mula sa keyboard nang sunud-sunod, linya sa linya. Binabasa namin ang isang linya, pagkatapos ay ang susunod na linya, pagkatapos ay ang susunod na linya, at iba pa. Angkop, ang paraan para sa pagbabasa ng mga linya ay tinatawag na nextLine().

Ang pagsulat ng data sa isang OutputStreamay nangyayari rin nang sunud-sunod. Ang isang magandang halimbawa nito ay ang console output. Mag-output ka ng isang linya, na sinusundan ng isa at isa pa. Ito ay sequential output. Hindi mo maaaring i-output ang unang linya, pagkatapos ay ang ikasampu, at pagkatapos ay ang pangalawa. Ang lahat ng data ay isinulat sa isang output stream nang sunud-sunod lamang.

Mga stream ng character

Nalaman mo kamakailan na ang mga string ay ang pangalawa sa pinakasikat na uri ng data, at totoo nga. Maraming impormasyon ang ipinapasa sa anyo ng mga character at buong string. Ang isang computer ay mahusay sa pagpapadala at pagtanggap ng lahat bilang mga byte, ngunit ang mga tao ay hindi gaanong perpekto.

Accounting para sa katotohanang ito, ang Java programmer ay sumulat ng dalawa pang klase: Readerat Writer. Ang Readerklase ay kahalintulad ng InputStreamklase, ngunit read()ang pamamaraan nito ay hindi nagbabasa ng mga byte, ngunit mga character ( char). Ang Writerklase ay tumutugma sa OutputStreamklase. At tulad ng Readerklase, gumagana ito sa mga character ( char), hindi bytes.

Kung ihahambing natin ang apat na klaseng ito, makukuha natin ang sumusunod na larawan:

Byte (byte) Mga tauhan (char)
Pagbasa ng datos
InputStream
Reader
Pagsusulat ng datos
OutputStream
Writer

Praktikal na aplikasyon

Ang InputStream, OutputStream, Readerat Writermga klase mismo ay hindi direktang ginagamit ng sinuman, dahil hindi sila nauugnay sa anumang mga konkretong bagay kung saan mababasa ang data (o kung saan maaaring isulat ang data). Ngunit ang apat na klase na ito ay may maraming descendant classes na maraming magagawa.


2. InputStreamklase

Ang InputStreamklase ay kawili-wili dahil ito ang parent class para sa daan-daang mga descendant na klase. Wala itong sariling data, ngunit mayroon itong mga pamamaraan na minana ng lahat ng mga nagmula nitong klase.

Sa pangkalahatan, bihira para sa mga stream object na mag-imbak ng data sa loob. Ang stream ay isang tool para sa pagbabasa/pagsusulat ng data, ngunit hindi sa storage. Sabi nga, may mga exception.

Mga pamamaraan ng InputStreamklase at lahat ng mga descendant na klase nito:

Paraan Paglalarawan
int read()
Nagbabasa ng isang byte mula sa stream
int read(byte[] buffer)
Nagbabasa ng hanay ng mga byte mula sa stream
byte[] readAllBytes()
Binabasa ang lahat ng mga byte mula sa stream
long skip(long n)
Nilalaktawan ang nmga byte sa stream (binabasa at itinatapon ang mga ito)
int available()
Sinusuri kung ilang byte ang natitira sa stream
void close()
Isinasara ang stream

Sa madaling sabi, dumaan tayo sa mga pamamaraang ito:

read()paraan

Ang read()pamamaraan ay nagbabasa ng isang byte mula sa stream at ibinabalik ito. Maaaring nalilito ka sa inturi ng pagbabalik. Ang uri na ito ay pinili dahil intito ang karaniwang uri ng integer. Ang unang tatlong byte ng intay magiging zero.

read(byte[] buffer)paraan

Ito ang pangalawang variant ng read()pamamaraan. Hinahayaan ka nitong basahin ang isang byte array mula sa isang InputStreamsabay-sabay. Ang array na mag-iimbak ng mga byte ay dapat na maipasa bilang argumento. Ang pamamaraan ay nagbabalik ng isang numero — ang bilang ng mga byte na aktwal na nabasa.

Sabihin nating mayroon kang 10 kilobyte buffer at nagbabasa ka ng data mula sa isang file gamit ang FileInputStreamklase. Kung ang file ay naglalaman lamang ng 2 kilobytes, ang lahat ng data ay ilo-load sa buffer array, at ibabalik ng pamamaraan ang numerong 2048 (2 kilobytes).

readAllBytes()paraan

Isang napakahusay na pamamaraan. Binabasa lang nito ang lahat ng data mula sa InputStreamhanggang sa maubos ito at ibabalik ito bilang isang solong byte array. Ito ay napaka-madaling gamitin para sa pagbabasa ng maliliit na file. Ang mga malalaking file ay maaaring hindi pisikal na magkasya sa memorya, at ang pamamaraan ay magtapon ng isang pagbubukod.

skip(long n)paraan

Ang pamamaraang ito ay nagpapahintulot sa iyo na laktawan ang unang n byte mula sa InputStreambagay. Dahil ang data ay mahigpit na binabasa nang sunud-sunod, binabasa lang ng pamamaraang ito ang unang n byte mula sa stream at itinatapon ang mga ito.

Ibinabalik ang bilang ng mga byte na aktwal na nilaktawan (kung sakaling natapos ang stream bago nnalaktawan ang mga byte).

int available()paraan

Ibinabalik ng pamamaraan ang bilang ng mga byte na natitira pa sa stream

void close()paraan

Isinasara ng close()pamamaraan ang stream ng data at inilalabas ang mga panlabas na mapagkukunang nauugnay dito. Kapag naisara na ang isang stream, wala nang mababasang data mula rito.

Sumulat tayo ng isang halimbawang programa na kumukopya ng napakalaking file. Hindi namin magagamit ang readAllBytes()paraan upang basahin ang buong file sa memorya. Halimbawa:

Code Tandaan
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

try(FileInputStream input = new FileInputStream(src);
FileOutputStream output = new FileOutputStream(dest))
{
   byte[] buffer = new byte[65536]; // 64Kb
   while (input.available() > 0)
   {
      int real = input.read(buffer);
      output.write(buffer, 0, real);
   }
}



InputStreampara sa pagbabasa mula sa file
OutputStreampara sa write to file

Buffer kung saan babasahin natin ang data
Hangga't mayroong data sa stream

Basahin ang data sa buffer
Isulat ang data mula sa buffer hanggang sa pangalawang stream

Sa halimbawang ito, gumamit kami ng dalawang klase: FileInputStreamay isang descendant ng InputStreampara sa pagbabasa ng data mula sa isang file, at FileOutputStreamisang descendant ng OutputStreampara sa pagsulat ng data sa isang file. Pag-uusapan natin ang pangalawang klase mamaya.

Ang isa pang kawili-wiling punto dito ay ang realvariable. Kapag ang huling bloke ng data ay nabasa mula sa isang file, madali itong magkaroon ng mas mababa sa 64KB ng data. Alinsunod dito, kailangan nating i-output hindi ang buong buffer, ngunit bahagi lamang nito - ang mga unang realbyte. Ito mismo ang nangyayari sa write()pamamaraan.



3. Readerklase

Ang Readerklase ay isang kumpletong analogue ng InputStreamklase. Ang tanging pagkakaiba ay gumagana ito sa mga character ( char), hindi sa mga byte. Katulad ng InputStreamklase, ang Readerklase ay hindi ginagamit saanman sa sarili nitong: ito ang parent class para sa daan-daang mga descendant na klase at tumutukoy sa mga karaniwang pamamaraan para sa lahat ng ito.

Mga pamamaraan ng Readerklase (at lahat ng mga descendant na klase nito):

Paraan Paglalarawan
int read()
Nagbabasa ng isa charmula sa stream
int read(char[] buffer)
Nagbabasa ng chararray mula sa stream
long skip(long n)
Lumalaktaw n charssa stream (binabasa at itinatapon ang mga ito)
boolean ready()
Tinitingnan kung may natitira pa sa stream
void close()
Isinasara ang stream

Ang mga pamamaraan ay halos kapareho ng sa InputStreamklase, bagama't may kaunting pagkakaiba.

int read()paraan

Binabasa ng paraang ito ang isa charmula sa stream at ibinabalik ito. Lumalawak ang charuri sa isang int, ngunit ang unang dalawang byte ng resulta ay palaging zero.

int read(char[] buffer)paraan

Ito ang pangalawang variant ng read()pamamaraan. Hinahayaan ka nitong basahin ang isang char array mula sa isang Readersabay-sabay. Ang array na mag-iimbak ng mga character ay dapat maipasa bilang argumento. Ang pamamaraan ay nagbabalik ng isang numero — ang bilang ng mga character na aktwal na nabasa.

skip(long n)paraan

Ang pamamaraang ito ay nagpapahintulot sa iyo na laktawan ang unang n mga character mula sa Readerbagay. Gumagana ito nang eksakto kapareho ng katulad na pamamaraan ng InputStreamklase. Ibinabalik ang bilang ng mga character na aktwal na nilaktawan.

boolean ready()paraan

Ibinabalik truekung may mga hindi pa nababasang byte sa stream.

void close()paraan

Isinasara ng close()pamamaraan ang stream ng data at inilalabas ang mga panlabas na mapagkukunang nauugnay dito. Kapag naisara na ang isang stream, wala nang mababasang data mula rito.

Para sa paghahambing, sumulat tayo ng isang programa na kumukopya ng isang text file:

Code Tandaan
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

try(FileReader reader = new FileReader(src);
FileWriter writer = new FileWriter(dest))
{
   char[] buffer = new char[65536]; // 128Kb
   while (reader.ready())
   {
      int real = reader.read(buffer);
      writer.write(buffer, 0, real);
   }
}



Readerpara sa pagbabasa mula sa isang file
Writerpara sa pagsulat sa isang file

Buffer kung saan babasahin natin ang data
Hangga't mayroong data sa stream

Basahin ang data sa isang buffer
Isulat ang data mula sa buffer hanggang sa pangalawang stream