코드지우개

입력 스트림과 출력 스트림 ① 본문

java

입력 스트림과 출력 스트림 ①

코드지우개 2023. 9. 5. 11:15
반응형

 

입력 스트림과 출력 스트림 개념

프로그램이 있다면 프로그램을 기준으로 데이터가 들어오게 되면 입력 스트림을 이용해야 하고 데이터가 나가게 되면 출력 스트림을 이용해야 한다.

즉 데이터가 들어오는 흐름이 입력 스트림, 데이터가 나가는 흐름이 출력 스트림이다.

 

위에 그림에서 B 프로그램에서 출력 스트림으로 데이터를 보내게 되면 A 프로그램은 입력 스트림으로 받아야 하고 반대로 A에서 출력 스트림에서 데이터를 보내게 되면 B 프로그램은 입력 스트림으로 받아야 한다.


바이트 기반 스트림과 문자 기반 스트림

[바이트 기반 스트림]

  • 그림, 멀티미디어, 문자 등 모든 종류의 데이터를 받고 보낼 수 있다.

[문자 기반 스트림]

  • 문자만 받고 보낼 수 있도록 특화되어 있다.
구분
바이트 기반 스트림
문자 기반 스트림
입력 스트림
출력 스트림
입력 스트림
출력 스트림
최상위 클래스
InputStream
OutputStream
Reader
Writer
하위 클래스
(예)
XXXInputStream
(FileInputStream)
XXXOutPutStream
(FileOutPutStream)
XXXReader
(FileReader)
XXXWriter
(FileWriter)

InputStream은 바이트 기반 입력 스트림의 최상위 클래스이고 OutputStream은 바이트 기반 출력 스트림의 최상위 클래스 이 클래스들은 각각 상속받는 하위 클래스는 접미사로 InputStream 또는 OutPutStream이 붙는다.
Reader는 문자 기반 입력 스트림의 최상위 클래스이고, Writer는 문자 기반 출력 스트림의 최상위 클래스이다. 이 클래스들을 각각 상속받는 하위 클래스는 접미사로 Reader 또는 Writer가 붙는다.


InputStream

InputStream은 바이트 기반 입력 스트림의 최상위 클래스로 추상 클래스이다.
다시 말해 new() 연산자를 사용해서 객체를 생성할 수 없다. 대신에 InputStream을 상속을 받는
FileInputStream, BufferedInputStream, DataInputStream을 가지고 객체를 생성할 수 있다.
또한 InputStream은 다른 api 에디터 타입으로 제공이 되기 때문에 직접적으로 InputStream을 객체로 생성할 일은 없다.

 

 
리턴 타입
메소드
설명
int
read()
입력 스트림으로부터 1 바이트를 읽고 읽은 바이트를 리턴한다.
int
read(byte[] b)
입력 스트림으로부터 읽은 바이트들을 매 개값으로 주어진 바이트 배열 b에 저장하고 실제로 읽은 바이트 수를 리턴한다.
int
read(byte[] b, int off, int len)
입력 스트림으로부터 len 개의 바이트만큼 읽고 매 개값으로 주어진 바이트 배열 b[off]부터 len 개까지 저장한다. 그리고 실제로 읽은 바이트 수인 len 개를 리턴한다. 만약 len 개를 모두 읽지 못하면 실제로 읽은 바이트 수를 리턴한다.
void
close()
사용한 시스템 자원을 반남하고 입력 스트림을 닫는다.

read() 메소드

read() 메소드는 입력 스트림으로부터 1바이트를 읽고 4바이트 int 타입으로 리턴한다. 따라서 리턴된 4바이트 중 끝의 1바이트에만 데이터가 들어가 있다. 예를 들어 입력 스트림에서 5개의 바이트가 들어온다면 다음과 같이 read() 메서드로 1바이트씩 5번 읽을 수 있다.

InputStream is = new FileInputStream("C:/test.txt");
--방법1
int readByte;
while( (readByte = is.read()) !=  -1 ){
    System.out.println(readByte);
}


--방법2
int readByte;
while(true){
    readByte = is.read();
    if(readByte == -1) break;
    System.out.println(readByte); 
    //아스키 코드 값이 나온다 문자로 출력하고 싶으면 (char)readByte로 강제 타입변환 해서 출력하면 된다.
}
 

C 드라이브에 test.jpg라는 파일이 있고 그 파일에 크기가 5바이트라고 가정을 했을 때 5개의 바이트가 InputStream에 들어오게 된다. 그런다음 FileInputStream을 객체 생성을 하고 InputStream 타입으로 대입을 했다.
그리고 int 타입에 readByte라는 변수를 선언했는데 이 readByte에는 무엇이 저장되냐면 InputStream으로부터 읽은 데이터가 저장된다.
그래서 처음 read()가 실행되면 파일을 읽고 readByte에 저장이 된다.
읽은 값은 -1이 될 수 없기 때문에 실행문에 코드를 실행하고 다시 read()가 실행된다. 그렇게 해서 마지막 5번째에 바이트를 읽고 나서 다음 6번째에 바이트를 읽게 되면 더 이상 읽을 바이트가 없기 때문에 read()는 -1을 리턴하게 된다.

 

read(byte[] b) 메소드
 

매 개값으로 바이트 배열을 받는 read() 메서드를 알아보자

 

 
출처) 이것이 자바다

C 드라이브에 test.jpg라는 파일이 있고 그 파일에 크기가 5바이트라고 가정을 했을 때 5개의 바이트가 InputStream에 들어오게 된다.
read(byte[] b) 메서드는 데이터를 읽을 때 바이트 배열을 주게 되어 있는데 만약 바이트 배열에 길이가 3이라고 한다면 read() 메서드는 읽을 때 3바이트를 읽고 바이트 배열 0번째 인덱스부터 차례대로 저장한다.
그리고 read()는 3개의 바이트를 읽었기 때문에 3을 리턴한다.(읽은 바이트 수를 리턴) 실제 데이터는 바이트 배열에 저장이 된다.

 

아직 2바이트가 남아있는 상태인데 두 번째 읽을 경우 read()는 다시 바이트 배열에 2바이트를 읽고 바이트 배열 0번째 인덱스부터 저장을 한다.

이때 2번째 인덱스 때 읽을 데이터가 없으니깐 2번째 인덱스에는 데이터를 저장하지 않는다.

그럼 2번째 인덱스에는 머가 저장이 되어 있을까? 바로 첫 번째에 저장했던 3값이 들어있다.

이번에는 read()는 2개의 바이트를 읽었기 때문에 2를 리턴한다.

더 이상 읽을 바이트가 없으면 read()는 -1을 리턴하게 된다.

InputStream is = new FileInputStream("C:/test.txt");

--방법1
int readByteNo;
byte[] readBytes = new byte[3];
while( (readByteNo = is.read(readBytes)) != -1 ){
    System.out.println(readByteNo);
}

--방법2
int readByteNo;
byte[] readBytes = new byte[3];

//읽은 데이터를 저장할 변수
String data = "";
while(true){
    readByteNo = is.read(readBytes);
    if(readByteNo == -1) break;
    System.out.println(readByteNo); //읽은 바이트 수를 리턴한다.

    //읽은 데이터를 문자열로 복원
    data += new String(readBytes, 0, readByteNo); //readBytes 배열에서 0번째 인덱스에서 readByteNo개를 변환
}
System.out.println(data); //while문 끝날때 출력

 
 

read(byte[] b, int off, int len) 메소드

매개 값으로 바이트 배열, 시작 인덱스와 길이를 갖는 read()메소드에 대해서 알아보자

 
 
출처) 이것이 자바다

InputStream으로부터 5개의 바이트가 들어온다고 가정해 보자
read() 메소드를 호출할 때 길이가 8인 바이트 배열과 2 하고 3을 줬을 때 read() 메소드는 마지막 매 개값에 주어진 값(3) 만큼 데이터를 읽는다.
그리고 저장할 위치는 두 번째 매 개값으로 주어진 값(2)에 인덱스 위치부터 저장을 한다.
즉, 길이가 8인 바이트 배열에서 2인덱스에서부터 3개에 바이트를 읽고 저장하겠다는 뜻이 된다.
그 후 read() 메소드는 3개의 바이트를 읽었기 때문에 3을 리턴한다. (읽은 바이트 수를 리턴)

InputStream is = new FileInputStream("C:/test.txt");
int readByte;
byte[] readBytes = new byte[100];
int readByteNo = is.read(readBytes);

// 위에 코드와 아래 코드는 동일한 동작을 한다.

InputStream is = new FileInputStream("C:/test.txt");
int readByte;
byte[] readBytes = new byte[100];
int readByteNo = is.read(readBytes, 0, 100);
 

위에 코드에서의 read() 메소드는 매 개값으로 주어진 바이트 배열에 길이만큼 데이터를 읽고 실제로 읽은 바이트 수만큼 리턴을 한다.
결국 길이가 100바이트이기 때문에 0번째 인덱스부터 100개를 읽는 것과 동일하다.
그래서 위에 코드와 아래 코드는 똑같은 실행 결과를 얻을 수 있다.

 

close() 메소드

close() 메소드는 InputStream을 더 이상 사용하지 않을 경우 호출해 준다.
그 이유는 InputStream에서 사용했던 시스템 자원을 풀어주기 때문이다.
만약에 파일에서 데이터를 읽었다면 InputStream.close()를 해야만이 파일이 닫힌다.
그래서 파일을 다른 프로그램에서 이용할 수가 있게 된다.

InputStream is = new FileInputStream("C:/test.txt");
--방법1
int readByte;
while( (readByte = is.read()) !=  -1 ){
    System.out.println(readByte);
}
is.close(); //InputStream을 닫아준다

--방법2
int readByte;
while(true){
    readByte = is.read();
    if(readByte == -1) break;
    System.out.println(readByte); 
    //아스키 코드 값이 나온다 문자로 출력하고 싶으면 (char)readByte로 강제 타입변환 해서 출력하면 된다.
}
is.close(); //InputStream을 닫아준다
 
반응형

'java' 카테고리의 다른 글

File을 MultipartFile 타입으로 변환  (0) 2023.09.05
RestTemplate.execute()를 사용하여 File타입으로 리턴 받기  (0) 2023.09.05
람다식 ⑤  (0) 2023.09.05
람다식 ④  (0) 2023.06.05
람다식 ③  (0) 2023.06.05
Comments