본문 바로가기
아카이브/자바의 정석

자바의 정석 15장 입출력

by nineteen 2021. 12. 23.
반응형

배운것

자바 입출력에 대한 개략적인 이해

 

 

 

 

내용 정리

 

스트림 (InputStream / OutputStream)

    - 데이터를 운반하는데 사용되는 연결 통로

    - 자바에서 입출력을 수행하기 위해 필요

    - 단방향 통신

    - 입력/출력을 동시에 수행하려면 입력스트림, 출력스트림, 2개의 스트림 필요

    - 바이트단위로 데이터 전송

 

 

입력스트림 출력스트림 입출력 대상의 종류
FileInputStream FileOutputStream 파일
ByteArrayInputStream ByteArrayOutputStream 메모리(byte배열)
PipedInputStream PipedOutputStream 프로세스(프로세스간 통신)
AudioInputStream AudioOutputStream 오디오장치

 

자바에서는 입출력을 처리할 수 있는 표준화된 방법을 제공

-> 입출력의 대상이 달라져도 동일한 방법으로 입출력이 가능! ( 프로그래밍 하기 편리하다는 장점 )

 

 

보조스트림

    - 실제 데이터를 주고받는 스트림이 아니라 데이터를 직접 입출력 할 수 없음

    - 스트림의 기능을 향상시키거나 새로운 기능을 추가하는 용도

    - 기반스트림 생성 -> 기반스트림을 이용해서 보조스트림 생성

 

 

스트림을 이용해 작업을 한 후에는 close()를 호출해 반드시 닫아주자

 

 

 

문자기반 스트림 (Reader / Writer)

    - 문자데이터를 입출력할 때 사용

    - 바이트기반 스트림과 이름만 조금 다를 뿐 활용방법은 거의 같다

 

 

 

 

자주 쓰이는 스트림들

 

FileInputStream/FileOutputStream

    - 파일에 입출력을 하기 위한 스트림

public class FileCopy {
	public static void main(String[] args) throws IOException {
		// FileInputStream, FileOutputStream 생성
		// args[0]파일을 읽어서 args[1]에 저장
		FileInputStream fis = new FileInputStream(args[0]);
		FileOutputStream fos = new FileOutputStream(args[1]);
		
		int data = 0;
		// fis의 내용을 읽어서 fos로 저장
		while((data=fis.read())!=-1) {
			fos.write(data);
		}
		fis.close();
		fos.close();
	}
}

 

 

 

BufferedInputStream/BufferedOutputStream

    - 스트림의 입출력 효율을 높이는 용도, 대부분의 입출력 작업에 사용

    - 데이터를 버퍼크기만큼 가져와 저장, 입출력

public class Ex15_6 {
	public static void main(String[] args) {
		// 버퍼를 이용해 123456789가 담긴 123.txt를 생성
		try {
			// 기반스트림(보조스트림)
			FileOutputStream fos = new FileOutputStream("100.txt");
			BufferedOutputStream bos = new BufferedOutputStream(fos,5);
			
			for(int i='1'; i<'9'; i++) {
				bos.write(i);
			}
          		// 버퍼에 남아있는 모든 데이터를 출력소스에 출력
            		// 보조스트림의 close()만 호출하면 됨
			bos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

 기반스트림(FileOutputStream)을 이용해 보조스트림(BufferedOutputStream)을 생성

 

 

 

SequenceInputStream

    - 여러 개의 입력스트림을 연속적으로 연결해서 처리

public class Ex15_7 {
	public static void main(String[] args) {
		byte[] arr1 = {0,1,2};
		byte[] arr2 = {3,4,5};
		byte[] arr3 = {6,7,8};
		byte[] outSrc = null;
		
		Vector v = new Vector();
		v.add(new ByteArrayInputStream(arr1));
		v.add(new ByteArrayInputStream(arr2));
		v.add(new ByteArrayInputStream(arr3));
		
		// 여러 inputStream을 하나로 연결
		SequenceInputStream input = new SequenceInputStream(v.elements());
		ByteArrayOutputStream output = new ByteArrayOutputStream();
		
		int data=0;
		
		try {
			while((data=input.read())!=-1) {
				output.write(data);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		outSrc = output.toByteArray();
		
		System.out.println(Arrays.toString(outSrc));
	}
}

 

 

 

 

FileReader/FileWriter

    - 파일로부터 텍스트 데이터를 읽고, 파일에 쓰는데 사용

    - 사용 방법은 FileInputStream/FileOutputStream과 같음

public class Ex15_9 {
	public static void main(String[] args) {
		try {
			FileReader fr = new FileReader(args[0]);
			FileWriter fw = new FileWriter(args[1]);
			
			int data = 0;
			while((data=fr.read())!=-1) {
				if(data!='\t' && data!='\n' && data!=' ' && data!='\r')
					fw.write(data);
			}
			
			fr.close();
			fw.close();
			
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
}

 

 

 

BufferedReader/BufferedWriter

    - 버퍼를 이용해서 입출력의 효율을 높일 수 있도록 해주는 역할

    - 문자 기반

public class Ex15_11 {
	public static void main(String[] args) {
		// 버퍼를 이용해 파일을 읽어 들임
		try {
			FileReader fr = new FileReader("Ex15_11.java");
			BufferedReader br = new BufferedReader(fr);
			
			String line = "";
			for(int i=1; (line = br.readLine())!=null; i++) {
				if(line.indexOf(';')!=-1)
					System.out.println(i+":"+line);
			}
			
			br.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

위의 스트림 사용예제와는 다르게, readLilne()을 이용해서 라인단위로 읽어들이임

때문에 위에서 사용했던 방법인 int data를 선언 후 한문자씩 읽어 들이는 것이 아니라, String line을 선언 후, 문장단위로 읽어 들임

 

 

 

 

InputStreamReader/OutputStreamWriter

    - 바이트기반 스트림을 문자기반 스트림으로 연결시켜주는 역할

    - 바이트기반 스트림의 데이터를 지정된 인코딩의 문자데이터로 변환하는 작업을 수행

public class Ex15_12 {
	public static void main(String[] args) {
		String line = "";
		
		try {
			InputStreamReader isr = new InputStreamReader(System.in);
			BufferedReader br = new BufferedReader(isr);
			
			System.out.println("사용중인 os 인코딩:"+isr.getEncoding());
			
			do {
				System.out.print("문장을 입력하세요 (q:종료)");
				line = br.readLine();
				System.out.println("입력한 문장:"+line);
			} while(!line.equalsIgnoreCase("q"));
			
			// System.in과 같은 표준입출력은 닫지 않아도 됨
//			br.close();
			System.out.println("프로그램 종료");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

 

 

 

File

    - 가장 많이 사용되는 입출력 대상

    - File클래스를 통해서 파일과 디렉토리를 다룰 수 있음

    - File인스턴스는 파일 일 수도 있고, 디렉토리일 수도 있다

public class Ex15_15 {
	public static void main(String[] args) throws IOException {
		File f = new File("C:\\BIBLE JAVA2\\CH15\\src\\Ex15_15.java");
		String fileName = f.getName();
		int pos = fileName.lastIndexOf('.');
		
        // 파일 이름
		System.out.println(f.getName());
        // 확장자 뺀 파일이름
		System.out.println(fileName.substring(0,pos));	
        // 확장자
		System.out.println(fileName.substring(pos+1));	
		// 경로
		System.out.println(f.getPath());	
        // 절대경로
		System.out.println(f.getAbsolutePath());
        // 정규경로
		System.out.println(f.getCanonicalPath());	
        // 파일이 속해있는 디렉토리
		System.out.println(f.getParent());			
        // 경로구분자
		System.out.println(File.pathSeparator);		
        // 경로구분자
		System.out.println(File.pathSeparatorChar);		
        // 이름구분자
		System.out.println(File.separator);		
        // 이름구분자`
		System.out.println(File.separatorChar);			
		System.out.println(System.getProperty("user.dir"));
	}
}

File인스턴스를 생성하고 메소드를 이용해서 파일의 경로와 구분자 등의 정보를 얻을 수 있음

user.dir는 현재 프로그램이 실행 중인 디렉토리를 의미함

 

 

 

public class Ex15_16 {
	public static void main(String[] args) {
		if(args.length!=1) {
			System.out.println("USAGE : java Ex15_16 DIRECTORY");
			System.exit(0);
		}
		
		File f = new File(args[0]);
		
		if(!f.exists() || !f.isDirectory()) {
			System.out.println("유효하지 않은 디렉토리입니다.");
			System.exit(0);
		}
		
		File[] files = f.listFiles();
		
		for(int i=0; i<files.length; i++) {
			String fileName = files[i].getName(); 
			System.out.println(files[i].isDirectory() ? "["+fileName+"]" : fileName);
		}
	}
}

File인스턴스를 이용해 파일과 디렉토리를 구별하는 예제

 

 

 

 

표준 입출력

    - 콘솔창을 통한 데이터 입력과 데이터 출력을 의미

    - 3가지 입출력 스트림 (System.in, System.out, System.err)

    - 별도로 스트림 생성하지 않고 사용 가능

public class Ex15_13 {
	public static void main(String[] args) {
		System.out.println("out : Hello World!");
		System.err.println("err : Hello World!");
	}
}

 

 

 

 

직렬화

    - 객체를 데이터 스트림으로 만드는 것

    - 객체에 저장된 데이터를 스트림에 쓰기(write)위해 연속적인 데이터로 변환하는 것

    - ObjectOutputStream 사용

    - 보조 스트림

 

 

직렬화가 가능한 클래스를 만들기 위해서는,

직렬화하고자 하는 클래스가 java.io.Serrializable인터페이스를 구현하면 됨

 

tarnsnt를 붙여서 직렬화 대상에서 제외하도록 할 수 있음

public class UserInfo implements Serializable {
	String name;
	transient String password;  // 직렬화 대상에서 제외
	int age;

 

 

 

역직렬화

    - 스트림으로부터 데이터를 읽어서 객체를 만드는 것

    - ObjectInputStream 사용

    - 보조 스트림

 

 

 

 

직렬화/역직렬화 예시

 

public class Ex15_20 {
	public static void main(String[] args) {
		try {
			// 저장할 파일 이름
			String fileName = "UserInfo.ser";
			
			// 파일에 저장
			// 기반스트림 FileOutputStream, 보조스트림 BufferedOutputStream, ObjectOutputStream
			FileOutputStream fos = new FileOutputStream(fileName);
			BufferedOutputStream bos = new BufferedOutputStream(fos);
			
			// 직렬화하기위한 스트림 생성
			ObjectOutputStream oos = new ObjectOutputStream(bos);
			
			// 직렬화할 객체 생성
			UserInfo u1 = new UserInfo("롭 홀딩", "kkkdg", 26);
			UserInfo u2 = new UserInfo("외데고르", "rhw125", 24);
			
			// 직렬화할 객체를 담을 리스트 생성
			ArrayList<UserInfo> list = new ArrayList<>();
			list.add(u1);
			list.add(u2);
			
			// 객체 직렬화
			oos.writeObject(u1);
			oos.writeObject(u2);
			oos.writeObject(list);
			
			oos.close();
			System.out.println("직렬화 종료");	
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

직렬화

 

 

public class Ex15_21 {
	public static void main(String[] args) {
		try {
			// 읽어올 파일의 이름
			String fileName = "UserInfo.ser";
			
			// 파일을 읽어옮
			// 기반 스트림 FileInputStream, 보조스트림 BufferedInputStream, ObjectInputStream
			FileInputStream fis = new FileInputStream(fileName);
			BufferedInputStream bis = new BufferedInputStream(fis);
			
			// 역직렬화하기 위한 ObjectInputStream
			ObjectInputStream ois = new ObjectInputStream(bis);
			
			// 역직렬화, 객체를 읽어옮
			// 객체를 읽어올 때, 출력한 순서와 일치해야함
			UserInfo u1 = (UserInfo) ois.readObject();
			UserInfo u2 = (UserInfo) ois.readObject();
			ArrayList<UserInfo> list = (ArrayList<UserInfo>) ois.readObject();
			
			// 역직렬화한 객체들을 출력
			System.out.println(u1);
			System.out.println(u2);
			System.out.println(list.toString());
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

역직렬화

 

 

 

 

느낀점 / 보완할 점

자주 사용해보지 않은 부분이라 그런지 낯선 부분들이 있었다.

개념들만 이해하려 하지 말고 어떻게 사용하는지등 손으로 직접 쳐보며 익혀야겠다.

 

 

 

혹시 잘못된 정보가 있다면 지적 부탁드려요.

'아카이브 > 자바의 정석' 카테고리의 다른 글

자바의 정석 2장 변수  (0) 2021.12.27
자바의 정석 16장 네트워킹  (0) 2021.12.24
13장 쓰레드 20200106  (0) 2021.01.06
13장 쓰레드 20210104  (0) 2021.01.04
13장 Thread 20201120  (0) 2020.11.20