BufferedReader 가이드

1. 개요

BufferedReader 는 문자 입력 스트림에서 텍스트 읽기를 단순화하는 클래스입니다. 텍스트 데이터를 효율적으로 읽을 수 있도록 문자를 버퍼링합니다.

이 튜토리얼에서는 BufferedReader 클래스 를 사용하는 방법을 살펴볼 것 입니다.

2. BufferedReader 를 사용하는 경우

일반적으로 BufferedReader 는 파일, 소켓 또는 다른 어떤 종류의 입력 소스에서 텍스트를 읽으려는 경우 유용합니다.

간단히 말해, 문자 청크를 읽고 내부 버퍼에 저장하여 I / O 작업 수를 최소화 할 수 있습니다. 버퍼에 데이터가있는 동안 판독기는 기본 스트림에서 직접 읽지 않고 읽습니다.

2.1. 다른 리더 버퍼링

대부분의 Java I / O 클래스와 마찬가지로 BufferedReaderDecorator 패턴을 구현합니다 ., 생성자에 Reader있어야합니다 . 이러한 방식 으로 버퍼링 기능 을 사용하여 Reader 구현 의 인스턴스를 유연하게 확장 할 수 있습니다.

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

그러나 버퍼링이 중요하지 않다면 FileReader를 직접 사용할 수 있습니다.

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

버퍼링 외에도 BufferedReader 는 파일을 한 줄씩 읽을 수있는 몇 가지 멋진 도우미 함수를 제공합니다 . 따라서 FileReader를 직접 사용하는 것이 더 간단 해 보일 수 있지만 BufferedReader 가 큰 도움이 될 수 있습니다.

2.2. 스트림 버퍼링

일반적으로 모든 종류의 입력 스트림을 사용 하도록 BufferedReader 를 구성 할 수 있습니다.기본 소스로 . InputStreamReader를 사용 하여 생성자에서 래핑 할 수 있습니다 .

BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

위의 예에서 우리는 일반적으로 키보드의 입력에 해당하는 System.in 에서 읽습니다 . 마찬가지로 소켓, 파일 또는 상상할 수있는 모든 유형의 텍스트 입력에서 읽기위한 입력 스트림을 전달할 수 있습니다. 유일한 전제 조건은 적절한 InputStream 구현이 있다는 것입니다.

2.3. BufferedReader 대 스캐너

대안으로 Scanner 클래스를 사용하여 BufferedReader 와 동일한 기능을 수행 할 수 있습니다 .

그러나이 두 클래스 사이에는 사용 사례에 따라 다소 편리하게 만들 수있는 중요한 차이점이 있습니다.

  • BufferedReader 는 동기화되고 (스레드 안전) Scanner는 그렇지 않습니다.
  • 스캐너 는 정규식을 사용하여 기본 유형 및 문자열을 구문 분석 할 수 있습니다.
  • BufferedReader 는 스캐너가 고정 된 버퍼 크기를 갖는 동안 버퍼의 크기를 변경할 수 있습니다.
  • BufferedReader의 기본 버퍼 크기가 더 큽니다.
  • 스캐너 가죽은 IOException이는 동안, BufferedReader로는 우리를 강제로 그것을 처리하기 위해
  • BufferedReader 는 데이터를 구문 분석하지 않고 읽기만하기 때문에 일반적으로 Scanner 보다 빠릅니다.

이를 염두에두고 파일에서 개별 토큰을 구문 분석하는 경우 ScannerBufferedReader 보다 조금 더 자연스럽게 느껴질 것 입니다. 그러나 한 번에 한 줄을 읽는 것만으로도 BufferedReader가 빛납니다.

필요한 경우 스캐너에 대한 가이드 도 있습니다.

3. BufferedReader로 텍스트 읽기

텍스트 파일에서 읽을 수 있도록 BufferReader를 적절하게 빌드, 사용 및 삭제하는 전체 프로세스를 살펴 보겠습니다 .

3.1. BufferedReader 초기화

먼저 BufferedReader (Reader) 생성자를 사용하여 BufferedReader생성 해 보겠습니다 .

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

이와 같이 FileReader를 래핑하는 것은 버퍼링을 다른 독자에게 측면으로 추가하는 좋은 방법입니다.

기본적으로 이것은 8KB의 버퍼를 사용합니다. 그러나 더 작거나 더 큰 블록을 버퍼링 하려면 BufferedReader (Reader, int) 생성자를 사용할 수 있습니다 .

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

그러면 버퍼 크기가 16384 바이트 (16KB)로 설정됩니다.

최적의 버퍼 크기는 입력 스트림의 유형 및 코드가 실행되는 하드웨어와 같은 요소에 따라 다릅니다. 이러한 이유로 이상적인 버퍼 크기를 얻으려면 실험을 통해 직접 찾아야합니다.

대부분의 하드웨어 장치는 2의 거듭 제곱을 블록 크기로 사용하므로 버퍼 크기로 2의 거듭 제곱을 사용하는 것이 가장 좋습니다.

마지막으로 java.nio API 파일 도우미 클래스사용하여 BufferedReader 를 만드는 또 다른 편리한 방법이 있습니다 .

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

그것을 만들기이렇게하면 FileReader 를 수동으로 생성 한 다음 래핑 할 필요가 없기 때문에 파일을 읽으려는 경우 버퍼링하는 좋은 방법 입니다.

3.2. 한 줄씩 읽기

다음으로 readLine 메서드를 사용하여 파일의 내용을 읽어 보겠습니다 .

public String readAllLines(BufferedReader reader) throws IOException { StringBuilder content = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { content.append(line); content.append(System.lineSeparator()); } return content.toString(); }

Java 8에 도입 된 lines 메소드를 사용하여 위와 동일한 작업을 좀 더 간단하게 수행 할 수 있습니다.

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

3.3. 스트림 닫기

BufferedReader를 사용한 후에 는 close () 메서드를 호출하여 관련된 시스템 리소스를 해제해야합니다. try-with-resources 블록을 사용하면 자동으로 수행됩니다 .

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

4. 기타 유용한 방법

이제 BufferedReader 에서 사용할 수있는 다양한 유용한 메서드에 초점을 맞 춥니 다 .

4.1. 단일 문자 읽기

read () 메서드를 사용하여 단일 문자를 읽을 수 있습니다 . 스트림이 끝날 때까지 전체 콘텐츠를 문자별로 읽어 보겠습니다.

public String readAllCharsOneByOne(BufferedReader reader) throws IOException { StringBuilder content = new StringBuilder(); int value; while ((value = reader.read()) != -1) { content.append((char) value); } return content.toString(); }

This will read the characters (returned as ASCII values), cast them to char and append them to the result. We repeat this until the end of the stream, which is indicated by the response value -1 from the read() method.

4.2. Reading Multiple Characters

If we want to read multiple characters at once, we can use the method read(char[] cbuf, int off, int len):

public String readMultipleChars(BufferedReader reader) throws IOException { int length; char[] chars = new char[length]; int charsRead = reader.read(chars, 0, length); String result; if (charsRead != -1) { result = new String(chars, 0, charsRead); } else { result = ""; } return result; }

In the above code example, we'll read up to 5 characters into a char array and construct a string from it. In the case that no characters were read in our read attempt (i.e. we've reached the end of the stream), we'll simply return an empty string.

4.3. Skipping Characters

We can also skip a given number of characters by calling the skip(long n) method:

@Test public void givenBufferedReader_whensSkipChars_thenOk() throws IOException { StringBuilder result = new StringBuilder(); try (BufferedReader reader = new BufferedReader(new StringReader("1__2__3__4__5"))) { int value; while ((value = reader.read()) != -1) { result.append((char) value); reader.skip(2L); } } assertEquals("12345", result); }

In the above example, we read from an input string which contains numbers separated by two underscores. In order to construct a string containing only the numbers, we are skipping the underscores by calling the skip method.

4.4. mark and reset

We can use the mark(int readAheadLimit) and reset() methods to mark some position in the stream and return to it later. As a somewhat contrived example, let's use mark() and reset() to ignore all whitespaces at the beginning of a stream:

@Test public void givenBufferedReader_whenSkipsWhitespacesAtBeginning_thenOk() throws IOException { String result; try (BufferedReader reader = new BufferedReader(new StringReader(" Lorem ipsum dolor sit amet."))) { do { reader.mark(1); } while(Character.isWhitespace(reader.read())) reader.reset(); result = reader.readLine(); } assertEquals("Lorem ipsum dolor sit amet.", result); }

In the above example, we use the mark() method to mark the position we just read. Giving it a value of 1 means only the code will remember the mark for one character forward. It's handy here because, once we see our first non-whitespace character, we can go back and re-read that character without needing to reprocess the whole stream. Without having a mark, we'd lose the L in our final string.

Note that because mark() can throw an UnsupportedOperationException, it's pretty common to associate markSupported() with code that invokes mark(). Though, we don't actually need it here. That's because markSupported() always returns true for BufferedReader.

Of course, we might be able to do the above a bit more elegantly in other ways, and indeed mark and reset aren't very typical methods. They certainly come in handy, though, when there is a need to look ahead.

5. Conclusion

이 빠른 자습서에서는 BufferedReader 를 사용하여 실제 예제에서 문자 입력 스트림을 읽는 방법을 배웠습니다 .

마지막으로 예제의 소스 코드는 Github에서 사용할 수 있습니다.