본문 바로가기
  • 1+1=3
개발삽질/SSAFY하는 동안 기록

[Java] File IO, 노드스트림

by 여스 2022. 2. 6.
반응형

IO란 Input과 Output을 말함.

데이터는 한쪽~다른쪽 으로 이동함.

한쪽은 입력이고 한쪽은 출력을 할텐데, 이 끝단을 노드라고 한다.

이 두 노드를 연결하고 데이터를 전송할 수 있는 개념이 스트림(Stream)이다.

스트림은 단방향으로만 움직임! just like 워러~

 

노드의 종류는 키보드, 모니터, 메노, 파일, 데이터베이스 등등 다양하다.

노드는 다음처럼 분류가 됨.

- 데이터 타입에 따라

byte를 이동시키면 XXStream으로 끝나고, 

char을 이동시키면 XXer로 끝남

 

- 입력이냐 출력이냐에 따라

InputStream/ OutputStream

Reader/Writer

 

- 노드 타입에 따라

(byte를 이동시키는 애들)

키보드 - InputStream

모니터 - OutputStream

File - FileInputSttream, FileOutputSttream

ByteArray- ByteArrayInputStream, ByteArrayOutputStream

 

(char를 이동시키는 애들)

키보드 - Reader

모니터 - Writer

File - FileReader, FileWriter

CharArray - CharArrayReader, CharArrayWriter

String - StringReader, STringWriter

 

InputStream

자 그럼 바이트를 읽어들이는 XXStream으로 끝나는 애들 먼저 보자.

메서드를 활용해보자.

package practice_IO;


import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.management.loading.PrivateClassLoader;

public class NodeStream {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		read();
		readBuffer();
		
	}
	
	
	
	private static void read() {
		String data1 = "hi java world!";
		try(InputStream input = new ByteArrayInputStream(data1.getBytes())){
			int read = -1;
			while((read = input.read()) != -1) {
				System.out.printf("읽은 값: %d, 문자로: %c\n", read, read);
			}
		}catch (IOException e) {
			// TODO: handle exception
			e.printStackTrace();
		}	
	}
	
	private static void readBuffer() {
		String data2 =  "자바는 객체지향 언어입니다";
		byte[] buffer = new byte[10];
		try(InputStream input = new ByteArrayInputStream(data2.getBytes())) {
			int read = -1;
			while((read = input.read(buffer))>0) {
				
				System.out.printf("읽은 개수: %d, 문자열:%s%n", read, new String(buffer));
			}
		}catch (Exception e) {
			// TODO: handle exception
		}
	}
}

read()의 출력은 다음과 같다.

보면 문자의 유니코드의 바이트코드 숫자값이 나오는 것을 볼 수 있다. a가 97인거 기억하징?

=>

읽은 값: 104, 문자로: h
읽은 값: 105, 문자로: i
읽은 값: 32, 문자로:  
읽은 값: 106, 문자로: j
읽은 값: 97, 문자로: a
읽은 값: 118, 문자로: v
읽은 값: 97, 문자로: a
읽은 값: 32, 문자로:  
읽은 값: 119, 문자로: w
읽은 값: 111, 문자로: o
읽은 값: 114, 문자로: r
읽은 값: 108, 문자로: l
읽은 값: 100, 문자로: d
읽은 값: 33, 문자로: !

 

근데 byte하나씩 뽑아오는 것은 비효율적이기에 한방에 몇개씩 가져올 수 있는데 오버로딩된 read()에 byte배열을 넣으면 된다.

위 코드에서 readBuffer()의 결과를 보면 다음처럼 나오는데, 유니코드에서 한글은 하나에 3바이트인데, 내가 10바이트씩 가져오다가 3바이트를 1바이트 2바이트 이렇게 잘라서 가져오게 되면 다음처럼 되는 것이다.

=>

읽은 개수: 10, 문자열:자바는 
읽은 개수: 10, 문자열:객체지�
읽은 개수: 10, 문자열:�� 언어�
읽은 개수: 8, 문자열:��니다��

 

따라서 이제 문자를 가져올 땐 바이트를 이동시키는 것보다 char를 이동시키는 방법을 써야 함을 알았다.

 

 

Reader

Reader의 read()메서드 보면 InputSream이랑 거의 다 똑같은데, 들어오는 인자의 타입이 char인것만 다름!

 

예제를 해보잣.

지금 근데 보면 위에 Stream쓸때랑 new String()에 들어가는 인자가 다른데, 사실 원래 위에것도 지금이것처럼 하는제 맞음.(어차피 깨지는 예시라 걍 막 한거임).

암튼 여기서 눈여겨볼 포인트

1. 출력결과가 깨지지 않는다! char하나씩 받아온거니까!

2. data.toCharArray()는 char[]을 반환하니까, 얘를 input으로 받는 노드스트림은 CharArrayReader()다.

3. (이건 Stream에서도 공통) read는 배열의 길이를 반환하고, 없으면 -1을 리턴함. char들이 쌓이는 공간은 buffer이다.

4. new String()에 두번째 인자는 char[]의 시작인덱스이다. 세번째 인자는 총 몇개 인덱스를 가져올지임.

public class NodeStream {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		readChar();
//		readBuffer();
	}
	private static String data = "자바는 객체지향 언어입니다";
	
	private static void readChar() {
		char[] buffer = new char[10];
		try(Reader input = new CharArrayReader(data.toCharArray())) {
			int read = -1;
			while((read = input.read(buffer))>0 ) {
				System.out.printf("읽은 갯수: %d, 문자열: %s\n",read, new String(buffer,0,read));
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		};	
	}
 }

출력결과:

=>

읽은 갯수: 10, 문자열: 자바는 객체지향 언
읽은 갯수: 4, 문자열: 어입니다

 

아근데 번외로 궁금한게 있어. new String()에 두번째랑 세번째 인자 안들어가면 어케나올까?

뒤에 쓸데없는 언어가 더 붙는다. 사실 이건 buffer가 계속 재사용되기 때문에 10번인덱스까지 꽉 채우지 않았을 때 뒤에 남아있는 애들이다. 그래서 0번째 인덱스부터 read(Reader에서 읽어들인 수)만큼만 String으로 표현하라고 해줘야 한당

System.out.printf("읽은 갯수: %d, 문자열: %s\n",read, new String(buffer,0,read));
=>아래처럼 변경해보면
System.out.printf("읽은 갯수: %d, 문자열: %s\n",read, new String(buffer));

출력결과:

=>

읽은 갯수: 10, 문자열: 자바는 객체지향 언
읽은 갯수: 4, 문자열: 어입니다객체지향 언

 

File

이제 파일을 보쟛. File은 ㄹㅇ파일만 지칭하는 게 아님. 파일과 디렉토리를 다루는 클래스임.

  • public File(String pathname)
    pathname에 해당하는 파일을 생성. 경로없이 파일을 생성하면 앱을 시작한 경로가 됨.
  • public File(String parent, String child)
    parent경로 아래 child 생성
  • public File(File parent, String child)
    parent경로 아래 child 생성
  • public File(URI uri)
    file로 시작하는 URI 객체를 이용해 파일을 생성한다
  • public boolean mkdir()
    새 디렉토리 생성
  • public String getName()
    파일의 이름을 리턴
  • public String getPath()
    파일의 경로를 리턴
  • public String getAbsolutePath()
    절대경로 리턴
  • public String getCanonicalPath() throws IOException
    파일의 정식경로 리턴.정식 경로란 절대경로이며 경로 내에 . 또는 ..의 상대경로가 없는 경로임
  • public boolean isDirectory()
    파일이 디렉토리인지 리턴
  • public boolean isFile()
    파일이 파일인지
  • public File[] listFiles()
    파일이 디렉토리인 경우 자식파일들을 리턴

에휴 많아라. 손가락 아파. 이런게 있다는 걸 인지하고 나중에 제대로 문서보면서 쓰자

FileInputStream

기본적인 사용법은 InputStream, OutputStream이랑 같다. 상속받은거니깐!

달라진 점은 노드만 달라짐. ByteArrayInputStream이 아니라 여기선 FileInputStream임.

FileReader

얘도 Reader랑 거의 비슷. 문자받을땐 Reader나 Writer였지

아래 flow를 연습해보자.

키보드 -> System.in -> Scanner -> FileWriter -> diary.txt -> FileReader -> System.out -> 모니터

 

해보자. 아근데 이거 저장은 잘 되는데, 저장하자마자 바로 읽어들이는게 시간차가 나서 그런지 읽어들이지는 못하네;;왜이럴까;;  일단 저장잘되고 읽어들이기만 하면 직전에 저장된건 잘 불러와진다.


public class NodeStream {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
//		readChar();
//		readBuffer();
		readFile();
	}
	private static String data = "자바는 객체지향 언어입니다";
	
	
	private static void readFile() {
		File target = new File("C:"+File.separator+"SSAFY"+File.separator + "diary.txt");
		try(Scanner scanner = new Scanner(System.in);
			FileWriter writer = new FileWriter(target, true);
			FileReader reader = new FileReader(target);
			){
			
			System.out.println("일기를 작성합니다. 그만두려면 x를 입력하세요");
			String str = null;
			while(true) {
				str = scanner.nextLine();
				if(str.equals("x")) {
					break;
				}else {
					writer.write(str + "\n");
//					System.out.println("writer에 보냄");
				}
			}

			char[] buffer = new char[10];
			int read = -1;
			while((read = reader.read(buffer)) > 0) {
				System.out.println(String.valueOf(buffer,0,read));
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

 

이상으로 노드스트림 기본 사용법 끝! 다음 포스팅은 보조스트림 사용법이 될것이다 자고일어나서 써야징

반응형

댓글