Guide til BufferedReader

1. Oversikt

BufferedReader er en klasse som forenkler lesing av tekst fra en tegninngangsstrøm. Den buffrer tegnene for å muliggjøre effektiv lesing av tekstdata.

I denne opplæringen skal vi se på hvordan du bruker BufferedReader klasse.

2. Når skal du bruke BufferedReader

Generelt, BufferedReader kommer godt med hvis vi vil lese tekst fra en hvilken som helst inngangskilde, enten det er filer, stikkontakter eller noe annet.

For å si det enkelt, det gjør det mulig for oss å minimere antall I / O-operasjoner ved å lese biter av tegn og lagre dem i en intern buffer. Mens bufferen har data, vil leseren lese fra den i stedet for direkte fra den underliggende strømmen.

2.1. Buffer en annen leser

Som de fleste av Java I / O-klassene,BufferedReader redskaper Dekoratormønster, som betyr at det forventer en Leser i sin konstruktør. På denne måten gjør det oss i stand til å utvide en forekomst av en fleksibel Leser implementering med bufferfunksjonalitet:

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

Men hvis buffering ikke betyr noe for oss, kan vi bare bruke en FileReader direkte:

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

I tillegg til buffering, BufferedReader gir også noen fine hjelperfunksjoner for å lese filer linje for linje. Så selv om det kan virke enklere å bruke FileReader direkte, BufferedReader kan være til stor hjelp.

2.2. Buffering av en strøm

Generelt, vi kan konfigurere BufferedReader å ta noen form for inngangsstrømsom en underliggende kilde. Vi kan gjøre det ved hjelp av InputStreamReader og pakke den inn i konstruktøren:

BufferedReader-leser = ny BufferedReader (ny InputStreamReader (System.in));

I eksemplet ovenfor leser vi fra System.in som vanligvis tilsvarer inngangen fra tastaturet. På samme måte kan vi sende en inngangsstrøm for lesing fra en stikkontakt, fil eller hvilken som helst tenkelig type tekstinndata. Den eneste forutsetningen er at det er en passende InputStream implementering for det.

2.3. BufferedReader vs Scanner

Som et alternativ, kunne vi bruke Skanner klasse for å oppnå samme funksjonalitet som med BufferedReader.

Imidlertid er det betydelige forskjeller mellom disse to klassene som kan gjøre dem enten mer eller mindre praktiske for oss, avhengig av brukssaken:

  • BufferedReader er synkronisert (trådsikker) mens skanneren ikke er det
  • Skanner kan analysere primitive typer og strenger ved å bruke regulære uttrykk
  • BufferedReader gjør det mulig å endre størrelsen på bufferen mens skanneren har en fast bufferstørrelse
  • BufferedReader har en større standard bufferstørrelse
  • Skanner gjemmer seg IO Unntak, samtidig som BufferedReader tvinger oss til å håndtere det
  • BufferedReader er vanligvis raskere enn Skanner fordi den bare leser dataene uten å analysere dem

Med disse i tankene, hvis vi analyserer individuelle tokens i en fil, da Skanner vil føles litt mer naturlig enn BufferedReader. Men det er bare å lese en linje om gangen BufferedReader skinner.

Om nødvendig har vi også en guide på Skanner også.

3. Lese tekst med BufferedReader

La oss gå gjennom hele prosessen med å bygge, bruke og ødelegge en BufferReader riktig å lese fra en tekstfil.

3.1. Initialisere en BufferedReader

For det første, la oss lage en BufferedReader bruker sin BufferedReader (Reader) konstruktør:

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

Innpakning av FileReader som dette er en fin måte å legge til buffering som et aspekt til andre lesere.

Som standard vil dette bruke en buffer på 8 KB. Imidlertid, hvis vi ønsker å buffer mindre eller større blokker, kan vi bruke BufferedReader (Reader, int) konstruktør:

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

Dette vil sette bufferstørrelsen til 16384 byte (16 KB).

Den optimale bufferstørrelsen avhenger av faktorer som typen inngangsstrøm og maskinvaren som koden kjører på. Av denne grunn, for å oppnå den ideelle bufferstørrelsen, må vi finne den selv ved å eksperimentere.

Det er best å bruke krefter på 2 som bufferstørrelse, siden de fleste maskinvareenheter har en effekt på 2 som blokkstørrelse.

Endelig, det er en praktisk måte å lage en BufferedReader bruker Filer hjelperklasse fra java.nioAPI:

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

Å skape detsom dette er en fin måte å buffer på hvis vi vil lese en fil fordi vi ikke trenger å opprette en manuelt FileReader først og deretter pakke den inn.

3.2. Lese linje for linje

La oss deretter lese innholdet i filen ved hjelp av 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 ()); } returner content.toString (); }

Vi kan gjøre det samme som ovenfor ved å bruke linjer metoden introdusert i Java 8 litt enklere:

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

3.3. Lukke strømmen

Etter bruk av BufferedReader, vi må kalle det Lukk() metode for å frigjøre eventuelle systemressurser knyttet til den. Dette gjøres automatisk hvis vi bruker en prøv-med-ressurser blokkere:

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

4. Andre nyttige metoder

La oss nå fokusere på forskjellige nyttige metoder tilgjengelig i BufferedReader.

4.1. Lese et enkelt tegn

Vi kan bruke lese() metode for å lese et enkelt tegn. La oss lese hele innholdet karakter for karakter til slutten av strømmen:

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

Dette vil lese tegnene (returneres som ASCII-verdier), kaste dem til røye og legg dem til resultatet. Vi gjentar dette til slutten av strømmen, som er indikert av responsverdien -1 fra lese() metode.

4.2. Lese flere tegn

Hvis vi vil lese flere tegn på en gang, kan vi bruke metoden les (char [] cbuf, int off, int len):

public String readMultipleChars (BufferedReader reader) kaster IOException {int lengde; røye [] tegn = ny røye [lengde]; int charsRead = reader.read (tegn, 0, lengde); Strengeresultat; hvis (charsRead! = -1) {resultat = ny streng (tegn, 0, charsRead); } annet {resultat = ""; } returnere resultat; }

I ovennevnte kodeeksempel vil vi lese opptil 5 tegn i en char-array og konstruere en streng fra den. I tilfelle at ingen tegn ble lest i vårt leseforsøk (dvs. vi har nådd slutten av strømmen), returnerer vi ganske enkelt en tom streng.

4.3. Hopper over tegn

Vi kan også hoppe over et gitt antall tegn ved å ringe hopp over (lang n) metode:

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

I eksemplet ovenfor leser vi fra en inngangsstreng som inneholder tall atskilt med to understrekninger. For å lage en streng som bare inneholder tallene, hopper vi over understrekingene ved å ringe hopp over metode.

4.4. merke og nullstille

Vi kan bruke merke (int readAheadLimit) og nullstille() metoder for å markere en posisjon i strømmen og gå tilbake til den senere. La oss bruke det som et noe konstruert eksempel merke() og nullstille() å ignorere alle mellomrom i begynnelsen av en strøm:

@Test offentlig ugyldighet givenBufferedReader_whenSkipsWhitespacesAtBeginning_thenOk () kaster IOException {Strengresultat; prøv (BufferedReader reader = new BufferedReader (new StringReader ("Lorem ipsum dolor sit amet."))) {do {reader.mark (1); } mens (Character.isWhitespace (reader.read ())) reader.reset (); resultat = reader.readLine (); } assertEquals ("Lorem ipsum dolor sit amet.", resultat); }

I eksemplet ovenfor bruker vi merke() metode for å markere posisjonen vi nettopp har lest. Å gi verdien 1 betyr at bare koden husker merket for ett tegn fremover. Det er praktisk her, når vi først ser vårt første ikke-hvite mellomrom, kan vi gå tilbake og lese det karakteret uten å måtte behandle hele strømmen på nytt. Uten å ha et merke, ville vi miste L i vår siste streng.

Merk at fordi merke() kan kaste en Ikke-støttetOperationException, det er ganske vanlig å assosiere markSupported () med kode som påkaller merke(). Selv om vi faktisk ikke trenger det her. Det er fordi markSupported () returnerer alltid sant for BufferedReader.

Selvfølgelig kan vi kanskje gjøre det ovenfor litt mer elegant på andre måter, og faktisk merke og nullstille er ikke veldig typiske metoder. De kommer absolutt til nytte når det er behov for å se fremover.

5. Konklusjon

I denne raske opplæringen har vi lært å lese tegninngangsstrømmer på et praktisk eksempel ved hjelp av BufferedReader.

Til slutt er kildekoden for eksemplene tilgjengelig på Github.