파일 시스탬
기본적 파일 시스탬
위도우 -> C:\ -> 루트 디렉토리를 가져올 수 있다.
절대경로, 상대경로
절대경로는 루트 디렉토리부터 시작하는 파일의 위치 정보
상대경로는 현재 디렉토리를 기준으로 파일의 위치를 표현
프로그램이 실행되면 프로그램의 작업 디렉토리가 하나 정해진다. 작업 디렉토리를 가리켜 현재 디렉토리라고 한다. 실행 중인 프로그램에서 경로 정보 없이 파일을 생성하면 파일이 생성되는 디렉토리가 있는데 그것을 가리켜 현재 디렉토리라고 한다.
절대경로는 파일 또는 디렉토리 위치를 루트 디렉토리를 기준으로 표현한 경로, 하지만 상대경로는 현재 디렉토리가 어디냐에 따라서, 가리키는 파일이 달라진다.
Path / Paths 클래스
File 클래스를 대체하기 위한 인터페이스. 이는 경로 표현하기 위한 인터페이스 인데, Paths.get 메소드가 반환하는 경로를 담은 인스턴스를 참조 -> 참조변수 선언에 사용
path가 참조하는 인스턴스에 절대 경로 정조 C: XXxxx 값이 담긴다. 해당 파일의 존재 유무 상관 없이 이 문장을 실행한다고 해서 해당 경로에 파일이 생성되는 것도 아니다
import java.nio.file.Path;
import java.nio.file.Paths;
public class Main1 {
public static void main(String[] args) {
Path pt1 = Paths.get("C:\FightingJava");
Path pt2 = pt1.getRoot();
Path pt3 = pt1.getParent();
Path pt4 = pt1.getFileName();
System.out.println(pt1);
System.out.println(pt2);
System.out.println(pt3);
System.out.println(pt4);
}
}
Path pt2 = pt1.getRoot(); //루트
Path pt3 = pt1.getParent(); //부모 디렉
Path pt4 = pt1.getFileName();//파일 이름
상대 경로를 갖고도 메소드 호출이 가능
import java.nio.file.Path;
import java.nio.file.Paths;
public class Main2 {
public static void main(String[] args) {
Path cur = Paths.get("");
String cdir;
if (cur.isAbsolute())
cdir = cur.toString();
else
cdir = cur.toAbsolutePath().toString();
System.out.println(cdir);
}
}
Path cur = Paths.get("");
현재 디렉토리의 정보를 출력
현재 디렉토리 정보를 상대경로의 형태로 담은 인스턴스 반환. 따라서 담겨있는 정보가 절대경로? 물어보면 false
cdir = cur.toAbsolutePath().toString();
절대 경로로 수정된 인스턴스 생성 및 반환.
파일 디렉토리 생성 및 소멸
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class Main3 {
public static void main(String[] args) throws IOException {
Path pth = Paths.get("C:\JavaStudy");
pth = Files.createFile(pth);
Path dp1 = Paths.get("C:\\JavaStudy\\Empty");
dp1 = Files.createFile(dp1);
Path dp2 = Paths.get("C:\\JavaStudy\\Empty");
dp2 = Files.createDirectories(dp2);
System.out.println(pth);
System.out.println(dp1);
System.out.println(dp2);
}
pth = Files.createFile(pth); // 파일 생성
dp1 = Files.createDirectory(dp1); // 디렉토리 생성
dp2 = Files.createDirectories(dp2); // 경로의 모든 디렉토리 생성
만약 파일이 없다면 예외로 빠진다.
파일을 대상으로 하는 간단한 입력 및 출력
IOstream 기반 방법 말고 간단한 경우에 사용.
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class Main4 {
public static void main(String[] args) throws IOException {
Path pt = Paths.get("C:\JavaStudy");
pt = Files.createFile(pt);
byte[] buf1 = {0x13, 0x14, 0x15};
for (byte b:
buf1) {
System.out.println(b);
}
Files.write(pt, buf1, StandardOpenOption.APPEND);
byte[] buf2 = Files.readAllBytes(pt);
for (byte b1:
buf2) {
System.out.println(b1);
}
}
}
Files.write(pt, buf1, StandardOpenOption.APPEND);
pt가 지시하는 파일에 배열 buf1의 데이터가 전부 저장. 위 메소드를 호출하면 파일이 자동으로 열리고 닫힌다.
APPEND (파일의 끝에 데이터를 추가), CREATE (파일이 존재하지 않으면 생성), CREATE_NEW (새 파일을 생성, 이미 파일이 존재하면 예외 발생), TRUNCATE_EXISTING (쓰기 위해 파일을 여는데 파일이 존재하면 파일의 내용을 덮어 쓴다.)
write 메소드를 호출하면서, 옵션을 하나도 전달하지 않으면 다음 두 옵션은 기본적으로 저장된다. (CREATE, TRUNCATE_EXISTING)
옵션을 전달하지 않으면 새 파일이 생성된다. 그리고 같은 이름의 파일이 있다면 그 파일 내용을 덮어버린다.
APPEND 이미 실행된 부분을 다시 호출하면 뒤에 붙여서 출력된다.
데이터를 읽어드릴 때 배열을 마련할 필요도 없다. 배열을 생성해서 파일의 모든 내용을 저장하고 배열의 참조 값을 반환.
Write 옵션
StandardOpenOption의 옵션 중에는 write가 있어서 데이터를 쓰기 위해 파일을 열어야 함을 지시 가능.
readAllLines(입력), Path write(출력)
문자열을 저장한 String 인스턴스를 저장한 컬랙션 인스턴스가 두번째 매개변수의 인자로 올수 있다는 의미
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
public class Main5 {
public static void main(String[] args) throws IOException {
Path pf = Paths.get("C:\Java");
String st1 = "One Simple";
String st2 = "Two Simple";
List<Object> list = Arrays.asList(st1, st2);
Files.write(pf, list); //파일에 문자열 저장
List<String> lst2 = Files.readAllLines(pf); // 파일로부터 문자열 읽기
System.out.println(list);
}
}
write 메소드 호출 시 옵션을 전달하지 않았기 때문에 다음 두 옵션이 기본
CREATE, TRUNCATE_EXISTING (파일이 없으면 생성하고, 그 내용을 모두 지우고 문자열 데이터 저장)
파일 및 디렉토리의 복사 이동
public static Path copy / move (복사 / 이동)
copy를 진행하는 메소드 -> REPLACE_EXISTING (이미 파일이 있다면 대체)
& COPY_ATTRIBUTES (파일의 속성까지 복사)
REPLACE_EXISTING은 move 메소드에 옵션으로 전달 가능.
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 Main6 {
public static void main(String[] args) throws IOException {
Path src = Paths.get("C:\Java");
Path dst = Paths.get("C:\Java1");
Files.move(src, dst, StandardCopyOption.REPLACE_EXISTING);
Files.copy(src, dst, StandardCopyOption.REPLACE_EXISTING);
}
}
NIO.2 기반의 IO 스트림 생성
new를 통한 인스턴스의 생성으로 스트림을 생성.
바이트 스트림의 생성
NIO2는 IO 스트림과 같은 방법으로 생성을 제안. 출력 또한 마찬가지이다
public class Main7 {
public static void main(String[] args) throws IOException {
Path pf = Paths.get("data.dat");
try(DataOutputStream out = new DataOutputStream(
Files.newOutputStream(pf)
)){
out.writeInt(970);
out.writeDouble(9.70);
}
catch (IOException e){
e.printStackTrace();
}
try(DataInputStream in =
new DataInputStream(Files.newInputStream(pf))){
int num1 = in.readInt();
double num2 = in.readDouble();
System.out.println(num1);
System.out.println(num2);
}
catch (IOException e){
e.printStackTrace();
}
}
}
문자 스트림의 생성
문자 스트림 생성 방법은 NIO.2 바이트 스트림 생성방법과 유사, 버퍼 스트림이 연결된 문자 스트림의 생성 방법이 같았다.
BufferedWriter br = new BufferedWriter(new FileWriter(“String.txt”))
Files 클래스는 문장을 통째로 대신하는 new BufferedWriter 메소드를 제공. 버퍼링 기능이 있는 문자 스트림을 바로 생성할 수 있다.
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class Main8 {
public static void main(String[] args) throws IOException {
String ks = "need Money";
String sk = "Life is Long";
Path pt = Paths.get("String.txt");
try(BufferedWriter bw = Files.newBufferedWriter(pt)){
bw.write(ks, 0, ks.length());
bw.write(sk, 0 , sk.length());
}
catch (IOException e){
e.printStackTrace();
}
try(BufferedReader br = Files.newBufferedReader(pt)){
String str;
while (true){
str = br.readLine();
if (str == null)
break;
System.out.println(str);
}
}
catch (Exception e){
e.printStackTrace();
}
}
NIO 기반 입출력
NIO Channel과 버퍼
“스트림 채널도 데이터 입력 및 출력을 위한 통로”
채널에는 “스트림은 한 방형으로만 데이터가 이동하지만, 채널은 양방향으로 이동이 가능”
단 채널을 사용하기 위해서는 버퍼에 연결해서 사용해야한다.
스트림 생성 후 성능 향상을 위해 필터 스트림인 버퍼 스트림을 연결. 하지만 NIO에서는 채널에 직접 데이터를 읽고 쓰는 것을 허용하지 않는다. 따라서 버퍼가 필요
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Scanner;
public class Main9 {
public static void main(String[] args) throws IOException {
Scanner sc = new Scanner(System.in);
System.out.println("File");
Path src = Paths.get(sc.nextLine());
System.out.println("사본");
Path dst = Paths.get(sc.nextLine());
ByteBuffer br = ByteBuffer.allocate(1024);
try(FileChannel ifc = FileChannel.open(src, StandardOpenOption.READ);
FileChannel ofc = FileChannel.open(dst, StandardOpenOption.WRITE, StandardOpenOption.CREATE)){
int num;
while (true){
num = ifc.read(br);
if (num == -1)
break;
br.flip();
ofc.write(br);
br.clear();
}
}
catch (IOException e){
e.printStackTrace();
}
}
}
ByteBuffer br = ByteBuffer.allocate(1024);
= 하나의 버퍼 형성
try(FileChannel ifc = FileChannel.open(src, StandardOpenOption.READ);
FileChannel ofc = FileChannel.open(dst, StandardOpenOption.WRITE, StandardOpenOption.CREATE)){
= 두 채널 생성
int num;
while (true){
num = ifc.read(br); // 채널 ifc에서 버퍼로 읽어 동일
if (num == -1) //읽어드린 데어터가 없다면
break;
br.flip(); // 모드 변환
ofc.write(br); // 버퍼에서 채널 ofc로 데이터 전송
br.clear(); // 버퍼 비움
}
}
ByteBuffer br = ByteBuffer.allocate(1024);
1024 바이트 버퍼 생성
두 개의 채널을 선택 -> 전달된 옵션 READ, WRITE, CREATE 이름 그대로 이해
버퍼와 채널을 생성 & NIO에서는 버퍼와 채널의 연결 과정을 거치지 않으며, 이 둘은 독립적으로 존재.
num = ifc.read(br);
채널 ifc로부터 데이터를 버퍼 br로 읽어 들임
br.flip();
버퍼에 저장된 데이터를 읽을 수 있는 상태로 변경
ofc.write(br);
버퍼에 저장된 데이터를 채널 ofc로 전송
모든 데이터를 복사할 때 까지 이 과정을 반복하게 된다. 버퍼에 저장된 데이터를 읽어 드렸다고 해서 버퍼가 비워지는 것은 아니다. 지금 설명한 한차례 복사가 이뤄지고 나면, 두 문장 중 하나를 실행해서 버퍼를 비우는 과정을 거쳐야 한다.
br.clear();
버퍼 비움
성능향상 포인트는 어디?
기존 IO 모델을 기반으로 파일 복사 프로그램을 작성하려면, 입력 스트림과 출력 스트림에 각각 버퍼스트림을 연결. 두개의 버퍼가 필요. 또한 버퍼 사이에 데이터 이동 과정을 거쳐야 한다. 이란 과정을 NIO는 생략 가능.
또한 Non – direct 버퍼를 생성하지 않고, Direct 버퍼를 생성하여 성능 향상을 기대 할 수 있다.
ByteBuffer br = ByteBuffer.allocate(1024); //non -direct
ByteBuffer br1 = ByteBuffer.allocateDirect(1024); // direct
Non direct -> 파일, 운영체제, 가상머신 버퍼, 실행 중인 자바
Direct -> 파일, 운영체제, 실행중인 자바
중간 과정이 생략되었기 때문에, 성능향상 기대 가능 하지만 Direct 버퍼 할당과 해제에 드는 시간적 비용이 Non direct 버퍼보다 다소 높기 때문에 파일이 크지 않을 경우만 사용하는 것을 추천
파일 랜덤 접근
파일에 데이터를 쓰거나 읽을 때 원하는 위치에 쓰거나 읽는 것을 의미. 원하는 순서로 읽을 수 있다. NIO의 파일 랜덤 접근은 버퍼를 기반.
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class Main10 {
public static void main(String[] args) throws IOException {
Path pt = Paths.get("data.dat");
ByteBuffer bf = ByteBuffer.allocate(1024);
bf.putInt(120);
bf.putInt(240);
bf.putDouble(0.94);
bf.putDouble(1.12);
try(FileChannel fc = FileChannel.open(pt,
StandardOpenOption.CREATE,
StandardOpenOption.READ,
StandardOpenOption.WRITE)){
bf.flip();
fc.write(bf);
ByteBuffer byte1 = ByteBuffer.allocate(1024);
fc.position(0);
fc.read(byte1);
byte1.flip();
System.out.println(byte1.getInt());
byte1.position(Integer.BYTES * 2);
System.out.println(byte1.getDouble());
System.out.println(byte1.getDouble());
byte1.position(Integer.BYTES);
System.out.println(byte1.getInt());
} catch (Exception e){
e.printStackTrace();
}
}
즉 데이터를 담은 후 그 내용을 파일에 전달하기 위한 것, (데이터1 – 버퍼 – 채널 – 파일)
다른 하나는 다음 목적을 갖는다 (파일 – 채널 – 버퍼2 – 출력)
목적은 다르지만 둘 다 데이터를 담았다가 내보내는 과정을 거치기 때문에 중간에 각 버퍼를 대상으로 flip 메소드를 호출
try(FileChannel fc = FileChannel.open(pt,
StandardOpenOption.CREATE,
StandardOpenOption.READ,
StandardOpenOption.WRITE)){
bf.flip();
fc.write(bf);
READ, WRITE 옵션이 모두 전달 따라서 이 채널은 데이터의 입출력 모두 가능
포지션
어느 위치까지 데이터를 썼는지 , 어느 위치까지 데이터를 읽었는지 표시
ByteBuffer byte1 = ByteBuffer.allocate(1024);
포지션은 0이다
bf.putInt(240);
bf.putInt(120);
4바이트 포지션 저장시 4씩 ++
flip 메소드를 다시 활용하면 -> 0으로 포지션 초기화
import java.nio.ByteBuffer;
public class Main11 {
public static void main(String[] args) {
ByteBuffer bb = ByteBuffer.allocate(1024);
System.out.println(bb.position());
bb.putInt(120);
System.out.println(bb.position());
bb.putDouble(4.4);
System.out.println(bb.position());
bb.flip();
System.out.println(bb.position());
int n = bb.getInt();
System.out.println(bb.position());
double d = bb.getDouble();
System.out.println(bb.position());
}
}
position 메소드는 현재 버퍼의 포지션 정보를 반호나.