표준입출력
: 표준입출력은 콘솔을 통한 데이터 입력과 콘솔로의 데이터 출력을 의미한다.
자바에서는 표준입출력을 위해 3가지 입출력 스트림을 제공하는데, 이들은 자바 어플리케이션 실행과 동시에 자동생성되기때문에
개발자가 별도로 코드를 작성하지 않고도 사용이 가능하다.
스트림 명 | 설명 |
System.in | 콘솔로부터 데이터를 입력받을때 사용한다. |
System.out | 콘솔로 데이터를 출력할때 사용한다. |
System.err | 콘솔로 데이터를 출력할때 사용한다. |
✔️ in, out, err는 System클래스에 선언된 클래스 변수 이다.
✔️ in, out, err의 선언부분의 타입은 InputStream과 PrintStream이지만 실제로는 버퍼를 이용하는
BufferedInputStream과 BufferedOutputStream의 인스턴스를 사용한다.
💡콘솔입력 예시
import java.io.IOException;
public class StandardIOEx1 {
public static void main(String[] args) {
int input = 0;
try {
while ((input = System.in.read()) != -1) {
System.out.println("input : " + input
+ ", (char)input :" + (char) input);
}
}catch(IOException e){
e.printStackTrace();
}
}
}
💡실행결과
hello /* hello 입력하고 엔터치기 */
input : 104, (char)input :h
input : 101, (char)input :e
input : 108, (char)input :l
input : 108, (char)input :l
input : 111, (char)input :o
input : 10, (char)input : /* 개행문자가 출력되어 줄바꿈되었다.*/
^Z /* crtl + z 종료 */
✔️ 콘솔입력은 버퍼를 가지고 있기 때문에 Backspace키를 이용해서 편집이 가능하며 한번에 버퍼의 크기만큼 입력이 가능하다.
✔️ Enter키나 입력을 끝을 알리는 '^z'를 누르기 전까지는 아직 데이터가 입력중인것으로 간주되어 커서가 입력을 계속 기다리는 상태인 블락킹상태에 머무르게 된다.
✔️ while((input = System.in.read())!= -1) : 사용자가 '^z'를 입력하면 read()는 입력이 종료되었음을 인식하고 -1을 반환하여 while문을 벗어나 프로그램이 종료된다.
✔️윈도우에서는 '^z', 유닉스와 매킨토시에서는 '^d'를 누르는 것이 스트림의 끝을 의미한다.
윈도우의 콘솔은 한 번에 최대 255자까지만 입력이 가능하다.
✔️예시를 보면 Enter키도 사용자입력으로 간주되는데 이러한 불편함을 제거 하려면 System.in에 BufferedReader를 이용해서 readLine()을 통해 라인단위로 데이터를 입력받으면 된다.
표준입출력의 대상변경
: System.in, System.out, System.err의 입출력대상이 콘솔화면이지만,
아래 메소드를 사용하면 입출력을 콘솔 이외에 다른 입출력 대상으로 변경하는것이 가능하다.
메서드 | 설명 |
static void setOut(PrintStream out) | System.out의 출력을 지정된 PrintStream으로 변경 |
static void setErr(PrintStream err) | System.err의 출력을 지정된 PrintStream으로 변경 |
static void setIn(PrintStream in) | System.in의 출력을 지정된 InputStream으로 변경 |
💡입출력 위치 변경 예시
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
public class StandardIOEx3 {
public static void main(String[] args) {
PrintStream ps = null;
FileOutputStream fos = null;
try{
fos = new FileOutputStream("test.txt");
ps = new PrintStream(fos);
System.setOut(ps); // System.out의 출력대상을 test.txt파일로 변경
}catch (FileNotFoundException e){
System.err.println("File not found");
}
System.out.println("Hello by System.out");
System.err.println("Hello by System.err");
}
}
💡실행결과 : System.out을 이용한 출력은 모두 test.txt파일에 저장된다.
user@user src % java StandardIOEx3.java
Hello by System.err
user@user src % cat test.txt /* test 파일 보기*/
Hello by System.out
user@user src %
💡커맨드라인에서 표준입출력의 대상을 변경하는 예시
public class StandardIOEx2 {
public static void main(String[] args) {
System.out.println("out : hello world!");
System.err.println("err : hello world!");
}
}
user@user src % java StandardIOEx2.java
out : hello world!
err : hello world!
user@user src % java StandardIOEx2.java > output.txt
err : hello world!
user@user src % cat output.txt
out : hello world!
user@user src % java StandardIOEx2.java >> output.txt
err : hello world!
user@user src % cat output.txt
out : hello world!
out : hello world!
user@user src % java StandardIOEx2.java < output.txt
out : hello world!
out : hello world!
> | System.out의 출력을 콘솔이 아닌 해당 위치로 변경한다. |
>> | System.out의 출력내용을 해당 위치에 추가한다. |
< | 표준입력을 콘솔이 아닌 해당 위치로 변경한다. 콘솔이 아닌 해당 파일로 부터 데이터를 입력받는다. |
RandomAccessFile
: 자바에서는 입력과 출력이 각각 분리되어 별도로 작업을 하도록 설계되어있는데,
RandomAccessFile는 하나의 클래스로 파일에 대한 입력과 출력을 모두 할 수 있도록 되어있다.
DataInput인터페이스와 DataOutput인터페이스를 모두 구현했기 때문에 읽기, 쓰기가 모두 가능하다.
💡RandomAccessFile의 장점
1️⃣ DataInput인터페이스와 DataOutput인터페이스를 모두 구현했기때문에 기본자료형 단위로 데이터를 읽고 쓸 수 있다.
2️⃣ 다른 입출력 클래스들은 순차적으로 읽기쓰기가 실행되지만, RandomAccessFile클래스는 파일의 어느위치에서나 읽기/ 쓰기가 가능하다.
💡RandomAccessFile클래스의 포인터
RandomAccessFile클래스가 읽고쓰는 위치에 제한이 없는 이유는
내부적으로 파일 포인터를 사용하는데, 입출력 시에 작업이 수행되는 곳이 파로 파일 포인터가 위치한 곳이기 때문이다.
파일 포인터는 기본적으로 파일의 제일 첫 부분에서 시작하며, 읽기 또는 쓰기를 수행할 때마다 작업이 수행한 다음 위치로 이동하게 된다.
만약 파일의 임의의 위치에 있는 내용에 대해서 작업하고자 한다면, 먼저 파일 포인터를 원하는 위치로 옮긴 다음 작업을 해야한다.
- getFilePointer() : 포인터의 위치 조회
- seek(long pos), skipBytes(int n) : 포인터의 위치 이동
* 사실 모든 입출력 클래스들은 포인터를 내부에 갖고있다. 하지만 내부적으로만 사용되기 때문에 사용자가 위치를 마음대로 변경할 수 없다는 것이 RandomAccessFile과 다른 점이다.
💡RandomAccessFile클래스의 예시
import java.io.EOFException;
import java.io.IOException;
import java.io.RandomAccessFile;
public class RandomAccessFileEx2 {
public static void main(String[] args) {
// 번호 국어 영어 수학
int [] score = { 1, 100, 90, 90,
2, 70, 90, 100,
3, 100, 100, 100,
4, 70, 60, 80,
5, 70, 90, 100
};
try {
RandomAccessFile raf = new RandomAccessFile("score2.dat","rw");
for(int i=0; i<score.length; i++){
raf.writeInt(score[i]);
}
while (true){
System.out.println(raf.readInt());
}
}catch (EOFException eof){
// readInt()를 호출했을 때 더 이상 읽을 내용이 없으면 EOFException이 발생한다.
}catch (IOException e){
e.printStackTrace();
}
}
}
-실행결과
파일은 생성되지만 화면에는 아무것도 출력이 되지 않을텐데
그 이유는 writeInt를 수행하면서 파일 포인터의 위치가 파일의 마지막으로 이동된 다음 readInt를 호출했기 때문이다.
마지막부터 읽기 시작하기 때문에 아무것도 읽지 못하고 EOFException이 발생해서 무한반복문을 벗어나게 된다.
while문 전에 raf.seek(0)을 추가해 포인터의 위치를 맨 앞으로 이동시긴다면 전체내용이 출력될 수 있다.
RandomAccessFile을 'rw(읽기쓰기)모드'로 생성해서 작업할때에는 이러한 점을 염두해야한다.
728x90
반응형
'개발 공부 > Java & Spring' 카테고리의 다른 글
DTO vs Map 장점과 단점은? (1) | 2024.08.14 |
---|---|
27. File (0) | 2023.04.17 |
25. 문자기반의 보조스트림 (0) | 2023.04.09 |
24. Collector 구현하기 (0) | 2023.03.29 |
23. 스트림 - collect() (0) | 2023.03.26 |