PushbackReader는 I/O 패키지 클래스로서 문자들을 입력으로 "unread" 시키거나
"push back"시킬 수 있다.
PushbackReader객체가 생성되었을때는 push back을 허용할 문자의 갯수를
프로그램상에 직접 기술하여 주어야 한다.
일반적으로 기본값을 1문자를 기준으로 한다.
아래에 간단한 예제를 보도록 하자.
import java.io.*;
public class PushDemo1 {
public static void main(String args[]) throws Exception {
char buf[] = {'t', 'e', 's', 't'};
// Character배열로부터 reader를 세팅시킨다.
CharArrayReader car = new CharArrayReader(buf);
PushbackReader pr = new PushbackReader(car, 1);
char c;
// 한 문자 읽어들이기.
c = (char)pr.read();
System.out.println(c);
// 또 다른 한문자 읽어들이기.
c = (char)pr.read();
System.out.println(c);
// 한문자 unread
pr.unread(c);
// 다시 읽어들인다.
c = (char)pr.read();
System.out.println(c);
}
}
위의 테스트 프로그램은 PushbackReader를 통하여 두개의 문자("t"와 "e")를
읽어들여 화면에 뿌려주고 있는데, 프로그램상에서 두번째문자인 "e"에서 다시
push back하고 있으며, read()메소드로 다시 읽어들이고 있다.
결국 화면상에 출력되는 내용은 아래와 같다.
output:
t
e
e
아래에서 다시 PushbackReader객체를 사용하여 더 많은 문자를 이용한 읽기를 시도해보자.
아래의 프로그램을 실행시켜 보면 Buffer overflow라는 IOException을 만나게 된다.
import java.io.*;
public class PushDemo2 {
public static void main(String args[]) throws Exception {
char buf[] = {'t', 'e', 's', 't'};
CharArrayReader car = new CharArrayReader(buf);
PushbackReader pr = new PushbackReader(car, 1);
char c1, c2;
// 두개의 문자를 읽어들인다.
c1 = (char)pr.read();
System.out.println(c1);
c2 = (char)pr.read();
System.out.println(c2);
// 양쪽 모두 unread
pr.unread(c2);
pr.unread(c1);
c1 = (char)pr.read();
System.out.println(c1);
}
}
위의 프로그램에서 Exception이 발생하는 이유는 두개의 문자를 pushback시키려하기
때문이다. PushbackReader객체는 한문자만을 지원하도록 만들어져 있다.
그러면 이러한 PushbackReader를 실제 프로그램상에서 어떻게 활용해야 할지 막막할수도
있을것이다. 한가지를 예로 들자면 몇몇의 입력에 대하여 token화시켜야할 경우도 있을것이다.
즉, 단어들과 숫자들을 분리시킬 경우이다.
아래와 같은 문장이 있을때
testing123abc
붙어있는 문자들을 아래와 같이 자르고 싶을 경우도 있을 것이다.
testing
123
abc
어떻게 이일을 처리할수 있을까 한번씩 고민들 해보기 바란다.
실제 StringTokenizer나 StreamTokenizer같은 경우는 해당 split char를 사용함으로서
표현할수 있겠지만 문장이 하나로 완전히 붙어있는 경우라면 그런 코드를 적용하기도 힘들 것이다.
PushbackReader를 사용한다면 위의 문장을 하나씩 잘라낼수 있다.
먼저 들어오는 입력을 이용하여 하나씩 read()하면서 문자를 읽어내어
해당 문자가 digit인지 letter인지를 분리시킬수가 있을 것이고, 만약 단어(letter의 조합)가 끝나고
숫자가 시작이 된다면 해당 digit을 만나는 부분에서 unread()로 pushback을 시킨 후
다시 read를 하면 되기 때문이다.
그에 대한 샘플코드를 아래에서 보도록 하자.
import java.io.*;
class ParseInput {
private PushbackReader pr;
// types of tokens
private static final int NONE = 0;
private static final int LETTER = 1;
private static final int DIGIT = 2;
// 파일을 위한 PushbackReader 를 세팅한다.
public ParseInput(String fn) throws IOException {
FileReader fr = new FileReader(fn);
BufferedReader br = new BufferedReader(fr);
pr = new PushbackReader(br, 1);
}
// 파일로부터 다음 토큰을 얻어낸다.
public String getToken() throws IOException {
int c;
char ch;
int type = NONE;
// 첫번째 문자를 찾아낸다.
do {
c = pr.read();
// 파일의 마지막인지 체크한다.
if (c == -1) {
return null;
}
ch = (char)c;
// 숫자인지 문자인지 분리해낸다.
if (Character.isLetter(ch)) {
type = LETTER;
}
else if (Character.isDigit(ch)) {
type = DIGIT;
}
} while (type == NONE);
// StringBuffer에 최소 한문자를 세팅할수 있다.
StringBuffer sb = new StringBuffer();
sb.append(ch);
for (;;) {
c = pr.read();
if (c == -1) {
break;
}
ch = (char)c;
// 먼저 들어온 문자와 같은 타입의 char라면 버퍼에 추가한다.
// 아닐 경우 unread()메소드를 통하여 pushback시킨다.
if ((type == LETTER && Character.isLetter(ch)) ||
(type == DIGIT && Character.isDigit(ch))) {
sb.append(ch);
}
else {
pr.unread(c);
break;
}
}
return sb.toString();
}
public void close() throws IOException {
pr.close();
}
}
public class PushDemo3 {
public static void main(String args[]) throws IOException {
if (args.length != 1) {
System.err.println("Missing input file");
System.exit(1);
}
ParseInput pi = new ParseInput(args[0]);
String token;
// 입력파일로부터 토큰을 얻어내도록 한다.
while ((token = pi.getToken()) != null) {
System.out.println(token);
}
pi.close();
}
}
위의 프로그램은 읽어들인 파일로부터 데이터를 입력받아 문자와 숫자를 분리하여
처리해내는 코드이다.
주석을 모두 달아놓았으므로 충분히 이해가 가리라 본다.
만약 파일에서 아래와 같은 내용이 입력되었다고 가정하자.
testing123this4is56a78test9
출력은 아래와 같은 형태로 나오게 된다.
testing
123
this
4
is
56
a
78
test
9
자바API에서는 StreamTokenizer클래스가 있기는 하지만 이 클래스는 바이트기반의
스트림이기 때문에 unicode문자에 대하여 토큰을 구성하기는 약간의 문제가 있다.
이럴경우 PushbackReader를 사용함으로써 그러한 문제점들을 해결해낼수 있다.
|