Guide til BufferedReader

1. Oversigt

BufferedReader er en klasse, der forenkler læsning af tekst fra en tegninputstrøm. Det buffrer tegnene for at muliggøre effektiv læsning af tekstdata.

I denne vejledning skal vi se på, hvordan du bruger BufferedReader klasse.

2. Hvornår skal du bruge det? BufferedReader

Generelt, BufferedReader kommer godt med, hvis vi vil læse tekst fra enhver form for inputkilde, hvad enten det er filer, sockets eller noget andet.

Kort fortalt, det gør det muligt for os at minimere antallet af I / O-operationer ved at læse klumper af tegn og gemme dem i en intern buffer. Mens bufferen har data, læser læseren fra det i stedet for direkte fra den underliggende strøm.

2.1. Buffering af en anden læser

Som de fleste af Java I / O-klasser,BufferedReader redskaber Dekoratør mønster, hvilket betyder, at det forventer en Læser i sin konstruktør. På denne måde gør det os i stand til fleksibelt at udvide en forekomst af en Læser implementering med bufferingsfunktionalitet:

BufferedReader-læser = ny BufferedReader (ny FileReader ("src / main / resources / input.txt"));

Men hvis buffering ikke betyder noget for os, kan vi bare bruge en FileReader direkte:

FileReader-læser = ny FileReader ("src / main / resources / input.txt");

Ud over buffering, BufferedReader giver også nogle gode hjælperfunktioner til læsning af filer linje for linje. Så selvom det kan virke nemmere at bruge FileReader direkte, BufferedReader kan være en stor hjælp.

2.2. Buffering af en strøm

Generelt, vi kan konfigurere BufferedReader at tage enhver form for inputstrømsom en underliggende kilde. Vi kan gøre det ved hjælp af InputStreamReader og indpakke det i konstruktøren:

BufferedReader-læser = ny BufferedReader (ny InputStreamReader (System.in));

I ovenstående eksempel læser vi fra System.in hvilket typisk svarer til input fra tastaturet. På samme måde kunne vi sende en inputstrøm til læsning fra en sokkel, fil eller enhver tænkelig type tekstindgang. Den eneste forudsætning er, at der er en passende InputStream implementering for det.

2.3. BufferedReader vs Scanner

Som et alternativ kunne vi bruge Scanner klasse for at opnå den samme funktionalitet som med BufferedReader.

Der er dog betydelige forskelle mellem disse to klasser, som kan gøre dem enten mere eller mindre bekvemme for os, afhængigt af vores brugssag:

  • BufferedReader er synkroniseret (trådsikker), mens scanneren ikke er det
  • Scanner kan analysere primitive typer og strenge ved hjælp af regulære udtryk
  • BufferedReader giver mulighed for at ændre størrelsen på bufferen, mens Scanneren har en fast bufferstørrelse
  • BufferedReader har en større standardbufferstørrelse
  • Scanner skjuler IOUndtagelse, mens BufferedReader tvinger os til at håndtere det
  • BufferedReader er normalt hurtigere end Scanner fordi den kun læser dataene uden at parsere dem

Med disse i tankerne, hvis vi analyserer individuelle tokens i en fil, så Scanner vil føle sig lidt mere naturlig end BufferedReader. Men bare at læse en linje ad gangen er hvor BufferedReader skinner.

Hvis det er nødvendigt, har vi også en guide til Scanner såvel.

3. Læsning af tekst med BufferedReader

Lad os gennemgå hele processen med at opbygge, bruge og ødelægge en BufferReader korrekt at læse fra en tekstfil.

3.1. Initialisering af en BufferedReader

For det første, lad os oprette en BufferedReader ved hjælp af dens BufferedReader (læser) konstruktør:

BufferedReader-læser = ny BufferedReader (ny FileReader ("src / main / resources / input.txt"));

Indpakning af FileReader som dette er en god måde at tilføje buffering som et aspekt til andre læsere.

Som standard bruger dette en buffer på 8 KB. Men hvis vi vil buffer mindre eller større blokke, kan vi bruge BufferedReader (Reader, int) konstruktør:

BufferedReader reader = ny BufferedReader (ny FileReader ("src / main / resources / input.txt"), 16384);

Dette indstiller bufferstørrelsen til 16384 bytes (16 KB).

Den optimale bufferstørrelse afhænger af faktorer som typen af ​​inputstrøm og den hardware, som koden kører på. Af denne grund er vi nødt til at finde den selv ved at eksperimentere for at opnå den ideelle bufferstørrelse.

Det er bedst at bruge kræfter på 2 som bufferstørrelse, da de fleste hardwareenheder har en effekt på 2 som blokstørrelse.

Langt om længe, der er en mere praktisk måde at oprette en BufferedReader bruger Filer hjælperklasse fra java.nioAPI:

BufferedReader reader = Files.newBufferedReader (Paths.get ("src / main / resources / input.txt"))

Opretter detsom dette er en god måde at buffer, hvis vi vil læse en fil, fordi vi ikke behøver at oprette en manuelt FileReader først, og pakk det derefter ind.

3.2. Læser linje for linje

Lad os derefter læse indholdet af filen ved hjælp af readLine metode:

public String readAllLines (BufferedReader reader) kaster IOException {StringBuilder content = new StringBuilder (); Streng linje; mens ((line = reader.readLine ())! = null) {content.append (line); content.append (System.lineSeparator ()); } returnere content.toString (); }

Vi kan gøre det samme som ovenfor ved hjælp af linjer metode introduceret i Java 8 lidt mere simpelt:

public String readAllLinesWithStream (BufferedReader reader) {return reader.lines () .collect (Collectors.joining (System.lineSeparator ())); }

3.3. Lukning af strømmen

Efter brug af BufferedReader, vi er nødt til at kalde det tæt() metode til at frigøre eventuelle systemressourcer, der er knyttet til det Dette gøres automatisk, hvis vi bruger en prøv med ressourcer blok:

prøv (BufferedReader reader = new BufferedReader (new FileReader ("src / main / resources / input.txt"))) {return readAllLines (reader); }

4. Andre nyttige metoder

Lad os nu fokusere på forskellige nyttige metoder, der er tilgængelige i BufferedReader.

4.1. Læsning af et enkelt tegn

Vi kan bruge Læs() metode til at læse et enkelt tegn. Lad os læse hele indholdet tegn for tegn indtil slutningen af ​​strømmen:

public String readAllCharsOneByOne (BufferedReader reader) kaster IOException {StringBuilder content = new StringBuilder (); int-værdi; mens ((værdi = reader.read ())! = -1) {content.append ((char) værdi); } returnere content.toString (); }

Dette vil læse tegnene (returneres som ASCII-værdier), kaste dem til char og tilføj dem til resultatet. Vi gentager dette indtil slutningen af ​​strømmen, hvilket er angivet med svarværdien -1 fra Læs() metode.

4.2. Læsning af flere tegn

Hvis vi vil læse flere tegn på én gang, kan vi bruge metoden læse (char [] cbuf, int off, int len):

public String readMultipleChars (BufferedReader reader) kaster IOException {int længde; char [] tegn = ny char [længde]; int charsRead = reader.read (tegn, 0, længde); Strengresultat; hvis (charsRead! = -1) {resultat = ny streng (tegn, 0, charsRead); } andet {resultat = ""; } returnere resultat }

I ovenstående kodeeksempel læser vi op til 5 tegn i en char-array og konstruerer en streng ud fra den. I tilfælde af at ingen tegn blev læst i vores læseforsøg (dvs. vi har nået slutningen af ​​strømmen) returnerer vi simpelthen en tom streng.

4.3. Springe over tegn

Vi kan også springe over et givet antal tegn ved at ringe til spring (lang n) metode:

@Test offentlig ugyldighed givenBufferedReader_whensSkipChars_thenOk () kaster IOException {StringBuilder resultat = ny StringBuilder (); prøv (BufferedReader-læser = ny BufferedReader (ny StringReader ("1__2__3__4__5"))) {int-værdi; mens ((værdi = reader.read ())! = -1) {result.append ((char) værdi); reader.skip (2L); }} assertEquals ("12345", resultat); }

I ovenstående eksempel læser vi fra en inputstreng, der indeholder tal adskilt af to understregninger. For at konstruere en streng, der kun indeholder tallene, springer vi over understregningerne ved at kalde springe metode.

4.4. mærke og Nulstil

Vi kan bruge mark (int readAheadLimit) og Nulstil() metoder til at markere en position i strømmen og vende tilbage til den senere. Lad os bruge det som et noget konstrueret eksempel mærke() og Nulstil() at ignorere alle hvide rum i begyndelsen af ​​en strøm:

@Test offentlig ugyldighed givenBufferedReader_whenSkipsWhitespacesAtBeginning_thenOk () kaster IOException {String result; prøv (BufferedReader-læser = ny BufferedReader (ny StringReader ("Lorem ipsum dolor sit amet."))) {gør {reader.mark (1); } mens (Character.isWhitespace (reader.read ())) reader.reset (); resultat = reader.readLine (); } assertEquals ("Lorem ipsum dolor sit amet.", resultat); }

I ovenstående eksempel bruger vi mærke() metode til at markere den position, vi lige har læst. At give den en værdi på 1 betyder, at kun koden husker mærket for et tegn fremad. Det er praktisk her, fordi når vi først ser vores første ikke-hvide mellemrum, kan vi gå tilbage og genlæse det tegn uden at skulle behandle hele strømmen igen. Uden at have et mærke, ville vi miste L i vores sidste streng.

Bemærk, at fordi mærke() kan kaste en Ikke-understøttetOperationException, det er ret almindeligt at associere markSupported () med kode, der påberåber sig mærke(). Selvom vi faktisk ikke har brug for det her. Det er fordi markSupported () returnerer altid sandt for BufferedReader.

Selvfølgelig kan vi muligvis gøre ovenstående lidt mere elegant på andre måder og faktisk mærke og Nulstil er ikke meget typiske metoder. De er bestemt nyttige, selvom der er behov for at se fremad.

5. Konklusion

I denne hurtige vejledning har vi lært, hvordan man læser tegninputstrømme på et praktisk eksempel ved hjælp af BufferedReader.

Endelig er kildekoden til eksemplerne tilgængelig på Github.