Java (Input / Output)

yihyun·2024년 8월 5일

Java

목록 보기
11/12
post-thumbnail

Input / Output

이전 포스팅에서 다룬 내용에서는 Java 내부에서만 Data(변수, 배열, 클래스)를 다뤘다면 이제는 System 외부로부터의 Data도 다룰 수 있다.

우리는 system 안으로 또는 밖으로 이동하는 Data의 흐름을 Stream 이라고 한다.

Stream : 물줄기 처럼 졸졸졸 흘러간다

Stream은 빨대라고 생각하고 이해하면 된다.
예를들어 우리가 버블티를 사서 먹을 때에는 빨대를 꽂아 펄이 들어있는 버블티를 빨아들이면 펄이 하나씩 들어오게 되는데 프로그래밍 입장에서 이 펄이 데이터 라고 생각하면 된다.

이러한 빨대는 사용하는 곳에 따라 다르게 사용해줘야 한다.
(뜨거운 커피를 마실 때에는 얇은 빨대, 버블티를 마실 때에는 두꺼운 빨대, 재사용성을 높이기 위해선 스테인레스 빨대를 사용하듯)

하지만 우리가 빨대를 모두 만드는 것은 너무 어렵기 이미 만들어진 빨대를 상황에 맞춰 사용해주면 된다.

Java.io.package 는 크게 InputStreamOutputStream 으로 나뉘고, 바이트 기반과 문자 기반으로 나뉜다.
Byte Base (바이트 기반)

  • InputStream (최상위 클래스)
  • OutputStream (최상위 클래스)

Character Base (문자 기반)

  • Reader (최상위 클래스)
  • Writer (최상위 클래스)

위에 나온 InputStream 은 바이트 기반 최 상위 class 이기 때문에 보통 하위 Stream 을 사용한다.

InputStream / OutputStream

InputStream 과 OutputStream 은 byte 기반 최 상위 class 이기 때문에 각 특화된 분야가 있는 하위 클래스들을 사용하며, InputStream 을 직접 사용하는 경우는 드물다.

InputStream 의 하위 클래스는 다음과 같다.

  • File InputStream
  • Buffered InputStream
  • Data InputStream

OutputStream 의 하위 클래스는 다음과 같다.

  • File OutputStream
  • Buffered OutputStream
  • Data OutputStream

Scanner

Scanner클래스를 이용하면 사용자로부터 직접 입력을 받거나 java에서 사용되는 여러 데이터를 쉽게 읽을 수 있다. (자주 사용되지는 않는다.)

Scanner sn = new Scanner(System.in);

.nextInt() : int값을 얻는다.
.nextLine() : String 값을 얻는다.
(.nextXXX() : Boolean, Long, Byte, Float, Short, Double 작성 가능)

import java.util.Scanner;

public class Main {
	public static void main(String[] args) {

		Scanner scan = new Scanner(System.in);
		
		System.out.print("이름>");
		String inputStr =  scan.nextLine();
		System.out.println("입력 받은 값 : " + inputStr); 
        // 실행 후 텍스트를 입력하면 "입력 받은 값 : 텍스트" 출력
	}
}

FileInputStream / FileOutputStream

File 은 Java에서 file / directory 등을 다룰 수 있게 해주는 객체이며, I/O에 해당하는 클래스는 아니다.

File에 date를 읽고 쓰는 기능은 Stream을 통해 이루어지며, File 객체를 꼭 생성해주어야 한다.

  • 폴더 만들기 (디렉토리 생성)
import java.io.File;
public class Main {
	public static void main(String[] args) {
		File dir = new File("C:/img/temp");
		
		if(!dir.exists()){
			dir.mkdirs();
		}
    }
}

mkdir() : 하나만 생성할 경우 사용
mkdirs() : 여러개를 생성할 경우 사용
delete() : 파일 또는 디렉토리 삭제
createNewFile() : 새 파일 생성
exists() : 존재할 경우 true 리턴 (없을경우 만들어야 하므로 if문으로 체크 ✔)

  • 파일 만들기
import java.io.File;
import java.io.IOException;

public class Main {

	public static void main(String[] args) throws IOException {
		File file = new File("C:/img/temp/test.txt"); // 파일 경로 + 이름
		
		if (!file.exists()) {    // 없으면 생성해라
			file.createNewFile();
		}
	}
}

file을 만들 때에는 경로와 이름을 모두 작성해 주어야 하며, IOException이 (일반예외) 발생하므로, try-catch를 해주거나 throws로 예외처리를 해주어야 한다.

.list() : 특정 폴더 내에 있는 파일명 추출 (파일인지 directory인지 알려주지 않는다)
→ 악성코드를 찾을 때 기존 파일 목록과 새로운 파일 목록을 대조해 없으면 삭제 하는 등으로 활용할 수 있다.

File 객체는 파일에 대한 모든 정보가 담겨있다.
그렇기 때문에 다양한 메소드를 활용해서 file에 대한 정보를 확인할 수 있다.

.isDirectory() : 디렉토리인지 확인
.getName() : 파일의 이름
.length() : 파일의 사이즈 (byte 단위)

File I/O Stream (FileReader) 을 활용해 텍스트 파일을 읽어와 콘솔로 출력하는 방법은 다음과 같다.

🔽 File 읽어오는 순서
1. 읽어올 파일 위치를 정한다.
2. 파일을 객체화 한다. (File)
3. 읽어올 때 필요한 Stream 을 준비한다. (InputStream)
4. 한 글자 씩 읽어 (read)와 출력(print)한다.
5. 사용한 자원을 닫는다. (close)
try-catch-resource 를 활용해주면 .close() 없이도 자동으로 닫히게 된다.

import java.io.File;
import java.io.FileReader;
public class Main {
	public static void main(String[] args) {

		File file = new File("C:/img/temp/test.txt");
		
		try (FileReader reader = new FileReader(file);) {			
			int data;
			
//			-1 : EOF(End Of File : 더 이상 읽을 게 없다.)
			while((data = reader.read()) != -1)
				System.out.print((char)data); // read는 int타입(아스키코드)이기 때문에 문자열로 바꿔줌
			}	
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

.read() : 해당 메소드는 int값 즉 아스키코드를 반환해주기 때문에 문자열로 변환해줘야 한다. (char)변수

.read() 를 해주면 읽어온 후 던져버리기 때문에 int 타입의 변수를 선언해 해당 변수에 읽어온 값을 담아준다.

🔽 파일 내보내는 순서
1. 저장할 파일 위치를 설정한다.
2. 파일을 객체화 한다 . (File)
3. 기록할 때 필요한 Stream 을 준비한다. (Writer)
4. 한 글자 씩 기록(write) 한다.
5. 모두 내보낸(flush)뒤 사용한 자원을 닫는다. (close)
try-catch-resource 를 활용해주면 .close() 없이도 자동으로 닫히게 된다.

import java.io.File;
import java.io.FileWriter;

public class OutputMain {

	public static void main(String[] args) {

		File file = new File("C:/img/temp/user001.txt");
		
		try(FileWriter writer = new FileWriter(file, true);) {

			writer.write("Hello, JAVA I.O.");

			writer.flush();
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

FileWriter Stream을 생성하며 경로/객체 뒤에 true를 작성해주면 덮어쓰지 않고, 이어서 작성한다는 의미가 된다.

.append() : 이어서 작성하기

flush : 안에 있는 걸 깔끔하게 비워버린다는 뜻이다.

왜 Input에서는 사용하지 않고 Output에서만 flush를 사용할까?
Input은 다 들어오면 -1을 반환하며 알려주지만 내보낼 때에는 다 나갔는지 알려주지 않기 때문에 혹시나 남아있는 찌꺼기 등으로 인해 파일이 깨지는 등의 상황을 방지하기 위해 사용해준다.
(실행마다 다른 결과가 나오는 무서운 일이 발생한다...😫)

이렇게 파일을 읽어오고 내보내기를 해주게 되면 우리가 자주 사용하는 복사-붙여넣기를 할 수 있다.

🔽 복사 붙여넣기 하는 순서
1. 손님을 맞이한다. IntputStream
2. 방문 목적에 맞는 일을 한다. read
3. 함께 외출한다. Output Stream
4. 외출 전 점검(문단속, 사람이 다 나왔는지) flush
5. 문을 닫고 잠근다. Close

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class Main {
	public static void main(String[] args) {

		File src = new File("C:/img/img.gif"); // 복사할 위치
		File dst = new File("C:/img/temp/img_copy.gif"); // 붙여넣기 할 위치
		
		try(
			FileInputStream fis = new FileInputStream(src); 
			FileOutputStream fos = new FileOutputStream(dst);
			) {
			
			int data;
			int i = 0;
			while((data = fis.read()) != -1) { 
				fos.write(data);
			}
			fos.flush();
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

만약 우리가 위 방법으로 바이너리파일 (1과 0으로 이루어져 있는 파일)을 복사, 붙여넣기를 해보면 파일 크기에 따라 다르지만 10KB만 되어도 매우 느린 속도를 경험할 수 있을 것이다.
(12KB 파일 전송에 약 2분 정도가 소요됨...😓)

이런 이유는 1byte 씩 옮기기 때문이다. (큰 물병을 채우기 위해 티스푼으로 물을 옮기는 중...🧊)

그렇다면 더 빠른 방법은 어떤게 있을까? ▶ 바로 배열(Array)을 사용해주는 것이다.
배열을 통해 하나씩 옮겨주던 것을 배열의 크기 만큼 byte를 담아 옮겨주면 더욱 빠르게 옮기는 것이 가능할 것이다.
(다른 방법 : 컴퓨터의 성능(cpu)를 향상시킨다.)
(💡 가장 좋은 것은 보조스트림을 사용해 주는 것이다. 이 내용은 아래에 작성할 예정!)

byte[] cup = new byte[1024]; // KB 단위
int i = 0;
			
while(fis.read(cup) != -1) { // read(byte[]) 
	fos.write(cup); 
}

read(byte[]) 은 내가 얼마나 읽었는지만 알려주기 때문에 변수에 따로 담을 필요가 없다!

그렇다면 위 내용을 조합해 Scanner로 입력받은 내용을 txt 파일로 저장하는 코드를 작성해보자. (입력 내용은 계속 추가되어야 한다.)

import java.io.File;
import java.io.FileWriter;
import java.util.Scanner;

public class Main {
	public static void main(String[] args) {

		File file = new File("C:/img/temp/sample.txt");
		
		try(FileWriter fw = new FileWriter(file, true)) {
			Scanner scan = new Scanner(System.in);
			
//          [방법1]
//			new line 문자 (개행문자 입력)
			fw.write(scan.nextLine() + "\r\n"); // 한 줄씩 계속 입력
			
//          [방법2]
			while(scan.hasNextLine()) { // 입력 종료 작성 전까지 계속 입력
				String inputStr = scan.nextLine();
				if(inputStr.equals("입력종료")) {
					scan.close();
					break;
				} else {
					fw.append(inputStr);
				}
			}
			fw.flush();
            scan.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

❕ FileWriter 은 file 객체를 내장하고 있기 때문에 객체와 경로 모두 작성할 수 있다.

보조 스트림

보조스트림은 주스트림에 연결되어 추가적인 기능을 제공해주는 스트림이다.

예를들어 사람은 자동차처럼 빠르게 달릴 수 없지만 자동차에 탑승하면 빠르게 달릴 수 있듯이
주스트림이 보조스트림에 탑승하게 되면 부가적인 기능을 사용할 수 있다.
(빠져나오면 보조스트림의 기능을 사용할 수 없다.)

보조스트림은 1개가 아닌 2개 이상을 사용할 수 있으며, 마지막에 탑승한 보조스트림의 객체를 사용해주고 닫아주면 된다. (마지막 객체만 닫으면 자동으로 다 닫힌다.)


BufferedInputStream / BufferedOutputStream

보조스트림에서는 성능 향성을 위한 Buffer 를 제공한다.

우리 일상에서 자주 사용하는 단어에도 Buffer가 있는데 바로 Buffering이다.
Buffering는 매우 유용한 기능임에도 부정적인 인식이 많이 있는데, 이 설명을 통해 긍정적으로 바라볼 수 있으면 좋겠다!

버퍼링이 걸린다는 것은 network의 문제로 데이터를 준비하지 못한 것으로 만약 버퍼링이 없다면 우리는 정지된 화면만 바라보고 있어야 한다.
그렇기 때문에 버퍼링은 지금 버퍼에 데이터를 채우고 있다고 친절하게 알려주는 역할을 하는 것이다.

위에서 알 수 있듯이 버퍼는 내용을 모아서 한 번에 전송을 시켜주는 임시 저장소의 역할을 하고, Buffered 보조스트림 생성 시 8192byte의 저장소가 생기게 되기 때문에 속도를 향상시킬 때 사용한다. (위에서 배열에 데이터를 담아 전달했던 것과 같다.)

사용 방법은 주스트림을 생성한 후 보조스트림에 주스트링을 탑승시켜주면 된다.

FileInputStream fis = new FileInputStream("경로");

BufferedInputStream bis = new BufferedInputStream(fis);

.read() 로 데이터를 가져오고, .write() 로 데이터를 내보낼 수 있다.


DataInputStream / DataOutputStream

바이트 스트림은 기본타입(int, boolean, long ···) 데이터를 전송할 수 없다.
즉 java의 고유 언어를 사용할 수 없게된다.

하지만 우리는 문자열, 숫자, 참/거짓 등을 파일로 저장할 경우가 생기는데 이럴 때는 Data 보조스트림을 사용해주면 된다.

❕ 하지만 class 타입을 저장해줄 수 없다는 단점이 있다. (컬렉션 프레임워크 등은 아예 사용할 수 없다.)

나머지는 데이터타입의 클래스 형태로 적어주고 String만 UTF라고 적어준다.
.readInt(), .readDouble(), '.readUTF() ···

주의 사항은 쓰는 순서와 읽는 순서가 동일해야 한다.

🔽 DataOutputStream 사용순서
1. 내보낼 파일 위치 설정
2. OutputStream 준비
3. 보조 스트림 추가 (Data, Buffer)
4. 데이터 쓰기
5. 비우기
6. 사용한 자원 닫기

import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;

public class DataOutput {
	public static void main(String[] args) throws Exception {

		FileOutputStream fos = new FileOutputStream("C:/img/temp/data.dat", true); // 확장자는 dat로 해야한다.
		
		BufferedOutputStream bos = new BufferedOutputStream(fos);
		DataOutputStream dos = new DataOutputStream(bos); 
		
		dos.writeUTF("홍길동");
		dos.writeInt(500);
		dos.writeBoolean(true);
		
		dos.flush();
		dos.close();
	}
}

🔽 DataInputStream 사용순서
1. 가져올 파일 위치 설정
2. InputStream 순비
3. 보조 스트림 추가 (Data, Buffer)
4. 데이터 읽고 출력하기
5. 사용한 자원 닫기

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;

public class DataInput {
	public static void main(String[] args) throws Exception {

		FileInputStream fis = new FileInputStream("C:/img/temp/data.dat");
		
		BufferedInputStream bis = new BufferedInputStream(fis);
		DataInputStream dis = new DataInputStream(bis);
		
//		문자열 -> 정수 -> boolean 순으로 읽어야 함 (적은 순서 그대로)
		String name =  dis.readUTF();
		int salary = dis.readInt();
		boolean promotion = dis.readBoolean();
		
		System.out.println(name + " / " + salary + " / " + promotion);
		
		dis.close();
	}
}

ObjectInputStream / ObjectOutputStream

위에서 살펴본 Data 보조스트림은 class 타입은 다룰 수 없기 때문에 java의 최상위 객체인 Object를 다룰 수 있는 Object보조스트림을 사용해 객체, 배열, 컬렉션 등 다양한 데이터 형태를 넣고, 내보낼 수 있다.

이때 생겨난 개념이 직렬화(serialize) 이다.
직렬화

직렬화란 데이터를 바이트 형태로 변환하는 것이고, 역직렬화란 변환된 데이터를 다시 객체로 변환하는 것을 말한다.
(class 객체의 경우 직렬화를 하지 않으면 전달되지 않는다.)

즉, 일렬로 나열해 객체를 전달하기 위해 객체를 잘게 쪼개서 전달하고 이렇게 전달한 데이터를 다시 합쳐서 사용하는 것이다.

그렇다면 우리가 파쇄기에 넣은 종이는 다시 사용할 수 없고, 퍼즐은 맞출 수 있는 것처럼 쪼개진 데이터를 어떻게 맞춰야 한다는 가이드 즉 규격이 있어야 한다.

때문에 우리가 직접 직렬화를 할 때에는 Serializable interface를 구현해주어야 한다.
(기존에 있는 것들은 이미 다 들어가 있다.)

import java.io.Serializable;

public class Sample implements Serializable {
//	네트워크에서 돌아다니는 객체들을 구분하기 위한 아이디를 지정하도록 권장한다.
	private static final long serialVersionUID = 1L;

	int num = 11;
	String team = "Edu";
	String job = "manager";
	
	public String method() {
		return "메서드 호출 완료!!";
	}
}

Object 보조스트림 을 사용하는 방법은 다음과 같다.
🔽 데이터 내보내기

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Map;

public class ObjectOutput {
	public static void main(String[] args) throws IOException {

		FileOutputStream fos = new FileOutputStream("C:/img/temp/obj.dat");
		BufferedOutputStream bos = new BufferedOutputStream(fos);
		ObjectOutputStream oos = new ObjectOutputStream(bos);
		
//		Map
		Map<String, String> map = new HashMap<String, String>();
		map.put("name", "홍길동");
		map.put("phone", "01012341234");
		
		oos.writeObject(map);
		
//		Array
		oos.writeObject(new int[] {80, 80, 90, 100});
		
//		UTF (String)
		oos.writeUTF("기본적인 데이터도 넣을 수 있다.");
		
//		Sample 클래스
		oos.writeObject(new Sample());
		
		oos.flush();
		oos.close();
	}
}

🔽 데이터 읽어오기
✔ Object는 최상위 클래스이기 때문에 명시적 형변환(Casting)이 필요하다.

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.Arrays;
import java.util.Map;

public class ObjectInput {

	public static void main(String[] args) throws Exception {

		
//		1. 경로 지정 + 주스트림 생성
		FileInputStream fis = new FileInputStream("C:/img/temp/obj.dat");
		
//		2. 보조 스트림 생성
		BufferedInputStream bis = new BufferedInputStream(fis);
		ObjectInputStream ois = new ObjectInputStream(bis);
		
//		Map 
		Map<String, String> map = (Map<String, String>) ois.readObject();
		System.out.println(map);
		
//		Array
		int[] scores = (int[]) ois.readObject();
		System.out.println(Arrays.toString(scores)); // 배열은 그냥은 안찍히니까.
		
//		String
		String msg = ois.readUTF();
		System.out.println(msg);
		
//		Sample 클래스
		Sample sample = (Sample) ois.readObject();
		System.out.println("team " + sample.team);
		System.out.println(sample.method());
		
		ois.close();
	}
}

Properties

Properities 는 본래 Map Interface를 상속받은 자료구조이다.
해당 자료구조를 Java.io 에서 다루는 이유는 파일을 활용하기 때문이다.

Properties 파일은 주로 java project 안에서 read / write 하기 때문에 환경설정에 활용하게 된다.

Map 인터페이스를 상속받았기 때문에 HashMap 이 아닌 Properties를 사용하는 이유는 Properities를 사용하면 외부에서 읽고, 수정할 수 있기 때문이다.

🔽 Output 순서 및 방법
1. properities 객체 준비
2. 객체에 전달할 값 추가
3. 보낼 파일 위치 선정
4. OutputStream 준비
5. OutputStream 을 이용해 Properities 저장
6. 사용한 자원 닫기

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;

public class OutputProps {
	public static void main(String[] args) throws IOException {

		FileOutputStream fos = new FileOutputStream("src/chap10/ex09/setting.properties");
		
		BufferedOutputStream bos = new BufferedOutputStream(fos);
		
		Properties prop = new Properties();

		// Map 인터페이스를 구현했기 때문에 .put() 사용 가능하다.
		prop.put("id", "admin");
		prop.put("pass", "1234");
		prop.put("name", "kim");
		prop.put("phone", "01012341234");
		prop.put("email", "admin@email.com");
		
		prop.store(bos, "simple comment");
		System.out.println("저장완료");
		
		bos.close(); // store에서 내용을 다 가져갔기 때문에 flush를 안해준다.
	}
}

🔽 Input 순서 및 방법
1. Properities 객체 준비
2. 가져올 파일 위치 선정
3. InputStream 준비
4. InputStream 을 이용해 Properities 불러옴
5. 속성을 하나씩 가져옴
6. 사용한 자원 닫기

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

public class InputProps {
	public static void main(String[] args) throws IOException {

		FileInputStream fis = new FileInputStream("src/chap10/ex09/setting.properties");
		BufferedInputStream bis = new BufferedInputStream(fis);
		
		Properties prop = new Properties();
		prop.load(bis);
		
        // Map 인터페이스를 구현했기 때문에 .get() 사용 가능
		String id = (String) prop.get("id"); // get을 쓰면 형변환을 해야한다.
		String pass = prop.getProperty("pass");
		System.out.println(id + " / " + pass);
		
		for(Object key : prop.keySet()) {
			System.out.println((String)key + " : " + prop.get(key));
		}
		
		bis.close();
	}
}

Java NIO

NIO 는 IO에 New가 붙은 것이다. (java 1.8 이후)

그렇다면 무엇이 달라져서 New가 붙은 것일까?
기존에 사용하던 IO는 Stream을 사용해서 필요한 만큼의 Stream을 만들어 사용했지만 NIO는 Channel를 사용하기 때문에 Stream을 사용하지 않아도 된다.

그리고 IO는 Buffer 를 별도로 준비해주어야 했다면 NIO는 Buffer가 이미 존재하기 때문에 별도의 준비가 필요 없다. (속도를 보장해준다.)

이렇게 불편함을 줄여준 것이 NIO인데 조금 더 전문화를 시켰기 때문에 기존에 File 객체만 만들어 사용할 수 있었던 IO와 달리
경로는 Path
파일 정보 FileSystem
파일 다루기 Files 를 사용해줘야 한다.

NIO를 사용하는 곳도 있기는 하지만 아직은 일반적으로 IO를 사용하고 있다.

1. 경로 특화 클래스 Path

import java.nio.file.Path;
import java.nio.file.Paths;

public class PathExam {
	public static void main(String[] args) {

		Path path  = Paths.get("C:/img/img.gif"); 
		
		System.out.println("특정 경로의 파일 이름 : " + path.getFileName());
		System.out.println("부모 폴더 : " + path.getParent().getFileName());
		System.out.println("루트 : " + path.getRoot());
	}
}

2. 파일을 다루는 Files 클래스

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class FilesExam {
	public static void main(String[] args) throws IOException {

		Path path = Paths.get("C:/img/temp/test.txt");
		
		System.out.println("디렉토리 여부 : " + Files.isDirectory(path));
		System.out.println("마지막 수정 시간 : " + Files.getLastModifiedTime(path));
		System.out.println("파일 크기 : " + Files.size(path));
		System.out.println("소유자 : " + Files.getOwner(path));
		System.out.println("숨김파일여부 : " + Files.isHidden(path));
	}
}

3. 파일 시스템 특화 클래스

import java.io.IOException;
import java.nio.file.FileStore;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;

public class FileSystemExam {
	public static void main(String[] args) throws IOException {

		FileSystem fs = FileSystems.getDefault();
		Iterable<FileStore> infoList = fs.getFileStores();
		
		for (FileStore info : infoList) {
			System.out.println("드라이브 이름 : " + info.name());
			System.out.println("NTFS/FAT32 : " + info.type());
			System.out.println("전체공간 : " + (info.getTotalSpace()/1024/1024/1024) + "GB");
			System.out.println("사용 가능 공간 : " + (info.getUsableSpace()/1024/1024/1024) + "GB");
			System.out.println("===========================================");
		}
	}
}

❕ 위에서 나오는 NTFS 와 FAT32는 저장하는 방식으로,
NTFS는 작은 공간으로 나눠져 있어 메모리 효율은 좋으나 속도는 조금 느려 보통 하드디스크에 사용되고,
FAT32는 큰 공간으로 나눠져 있어 메모리 효율은 낮으나 속도가 빠르기 때문에 USB에 주로 사용된다.

위 내용을 활용해서 File I/O에서 했던 것처럼 파일을 생성하고 입력하는 방법은 다음과 같다.
디렉토리 및 파일 생성

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class MakeDir {
	public static void main(String[] args) throws IOException {
		Path path = Paths.get("C:/img/temp2");
		
		if (Files.notExists(path)) {
			System.out.println("폴더 없음");
			Files.createDirectory(path);
		}
		
		Path file = Paths.get("C:/img/temp2/textFile.txt");
		
		if (Files.notExists(file)) {
			System.out.println("파일 없음");
			Files.createFile(file);
		}
	}
}

파일 읽어오기

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

public class FileRead {
	public static void main(String[] args) throws IOException {
		
		Path path = Paths.get("C:/img/temp/test.txt");
		
		List<String> lines =  Files.readAllLines(path); // 한번에 담겨서 온다.

		for (String line : lines) {
			System.out.println(line);
		}
	}
}

NIO를 사용하면 자원을 반납하지 않아도 알아서 반납이 된다.
.readAllLines() 는 한번에 담아서 가져올 수 있는 메소드이다.

파일 내보내기

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Scanner;

public class FileWrite {
	public static void main(String[] args) throws IOException {

		Path path = Paths.get("C:/img/temp/write.txt");
		
		if(Files.notExists(path)) {
			System.out.println("파일 생성");
			Files.createFile(path);
		}
		
		Scanner scan = new Scanner(System.in);
		System.out.print("입력 > ");
		String msg = scan.nextLine() + "\r\n";
		
		Files.writeString(path, msg, StandardOpenOption.APPEND);

		System.out.println("파일 저장이 완료 되었습니다.");
		
		scan.close();
	}
}

StandardOpenOption
APPEND : 이어쓰기
CREATE : 쓰기모드
READ : 읽기모드
CREATE_NEW : 이미 파일이 존재하는 경우 새 파일을 생성

파일 복사 붙여넣기

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;

public class FileCopy {
	public static void main(String[] args) throws IOException {

		Path src = Paths.get("C:/img/temp/img.gif");
		Path dst = Paths.get("C:/img/temp2/img.gif");
		
		Files.copy(src, dst, StandardCopyOption.REPLACE_EXISTING);
	}
}

StandardCopyOption
REPLACE_EXISTING : 이미 파일이 있으면 덮어쓴다. (만약 이 옵션이 없으면 이미 파일이 있을 경우 Exception이 발생한다.)
COPY_ATTRIBUTES : 파일의 속성까지도 복사한다. (파일속성 : 권한, 읽기전용 등)

NIO를 사용하면 IO를 사용한 코드보다 간결하게 사용해 줄 수 있다.

profile
개발자가 되어보자

0개의 댓글