코딩 노트

Java.io.file - single 본문

Java

Java.io.file - single

newbyeol 2023. 7. 14. 10:56

파일이란?

- 글자를 저장하는 저장소

- 파일이 크다는 것은 보관하는 글자가 많다는 소리이다.

- 글자 하나 당 1byte이다.

 

폴더란?

- 파일을 구분하기 위한 장치

- 디렉터리(directory)라고 부른다.

- 크기가 없다. (내부 파일의 합계가 크기일뿐 폴더 자체의 크기는 없다.)

파일(File) 제어

- 자바에서 파일과 폴더는 어떻게 처리할까?

 

1. java.io 패키지

- 블로킹 방식

- 한 번에 하나씩 순차적으로 처리한다. (앞에 것이 끝나지 않으면 뒤에 것이 실행되지 않는 것)

- 우선순위를 정해서 우선순위가 끝나야 다음 것을 처리할 수 있다. (한 번에 하나씩)

- 여러 개를 동시에 처리할 수 없다.

 

2. java.nio 패키지

- 논블로킹 방식

- 어떤 데이터가 먼저 끝나도 상관이 없을 때 이 방식을 사용한다.

- 순서가 필요하지 않은, 대용량 데이터를 처리할 때 사용한다.

 

File 클래스 객체 생성

- (파일을 생성한다는 말은 아니다.)

- a와 b가 같은 파일을 바라보게 된다.

File a = new File("D:/test.txt");

File b = new File("D:/", "test.txt");

 

· 파일 정보 분석

- 진짜 존재하는 파일인지 검사

System.out.println(a.exists());

 

· 파일인지 디렉터리인지 검사

System.out.println(a.isFile()); //파일이야?

System.out.println(a.isDirectory()); //디렉터리야?

 

· 파일일 때 정보 물어보는 코드

if(a.isFile()) {

System.out.println(a.getName()); //이름이 뭐야?

System.out.println(a.getPath()); //위치가 어디야?

System.out.println(a.length()); //파일의 크기(=들어있는 글자의 개수) 반환형이 long이다.

 

System.out.println(a.lastModified()); //최종 수정시각이 언제야?

}

 

 

디렉터리도 File 클래스로 제어한다.

- 요즘은 이스케이프 문자를 두 개씩 쓰는 것이 번거로워 windows, mac 상관없이 mac버전으로 통일한다.

File c = new File("D:/"); // mac 버전

File d = new File("D:\\"); //windows 버전

 

·  디렉터리일 때 정보 물어보는 코드

if(c.isDirectory()) {

System.out.println(c.getName());

System.out.println(c.getPath());

System.out.println(c.length()); //(주의) 디렉터리의 크기는 의미가 없다.

}

 

· 디렉터리는 내부에 존재하는 요소들을 추출할 수 있다.

String[] names = c.list(); //이름만 추출

File[] files = c.listFiles(); //파일 객체를 추출(이름+나머지 정보)

↑ 정보 전체 출력

for(File file: files) {

System.out.println(file);

}

 

 

Q. 사용자에게 파일 또는 폴더를 입력받아 파일 또는 폴더의 정보 출력(미니 탐색기)

String input = "D:/test.txt"; //파일

//분석을 하려면 파일 객체를 생성해야한다.

File a = new File(input);

 

if (a.isFile()){ //파일이면 이름+확장자+크기

System.out.println("<파일정보>");

System.out.println("이름: " + a.getName());

 

//확장자는 파일의 마지막 . 뒤에 있는 값을 말하며, 없을 수 있다.

//(ex) abc.def.ghi 이면 확장자는 .ghi이다.

int index = a.getName().lastIndexOf(".");

String extension;

if (index == -1)

extension = "없음";

} else {

extension = a.getName().substring(index);

}

System.out.println("확장자: " + extension);

System.out.println("크기: " + a.length() + " bytes");

 

} else if(a.isDirectory()) { //디렉터리면 목록[파일/폴더]

File[] list = a.listFiles();

for(File file : list) {

if(file.isFile()) {

System.out.print("[파일]");

} else if(file.isDirectory()) { //else해도 됨

System.out.print("[폴더]");

}

System.out.println(file.getName());

}

} else {

System.out.println("존재하지 않는 파일 또는 폴더");

}

 

 

파일일때 실행결과

<파일정보>

이름: test.txt

확장자: .txt

크기: 5 bytes

 

 

폴더일때 실행결과

[폴더]eclipse

[폴더]github

[폴더]workspace

 

파일, 디렉터리 생성과 삭제

File target = new File("D:/dummy");

File target = new File("D:/dummy/a/b/c"); //경로 입력도 가능

 

· 파일 생성

target.createNewFile();

 

·  디렉터리 생성

- 보통 mkdirs를 사용한다.

target.mkdir(); //중간경로가 없으면 생성을 하지 않는 명령

target.mkdirs(); //중간경로가 없으면 생성해주는 명령

 

· 파일, 디렉터리는 삭제 방법이 동일하다!

- 디렉터리는 비어있는 것만 삭제 가능하다.

target.delete();

 

(Q) 프로그램을 입력한 시간만큼 멈추게 하는 코드

Thread.sleep(5000L);

 

경로(path)

- PC마다 설치된 디스크 환경이 다를 수 있다.

    - "D:/"와 같은 절대적인 위치(절대경로:)는 다른 PC에서 인식하지 못할 수 있다.

- 다른 운영체제나 환경에서도 작동하는 경로를 설정할 수 있는 방법은 2가지가 있다.

 

1. 상대경로 (특정 대상을 기준으로 알려줌)

- . 은 현재 위치(current path)를 의미한다. (이클립스 안에서 확인 가능)

 

File a = new File("sample"); //프로젝트 범위에서 경로를 탐색

File a = new File("./sample"); //이클립스 안에서 확인 가능

a.mkdirs();

File b =new File("../dummy"); //프로젝트 위에 폴더 만들어짐 (이클립스 안에서 확인불가)

b.mkdirs();

- 첫 번째줄과 두 번째줄 코드는 둘 다 같은 코드지만 .을 사용하는 것이 더 정확하다.

 

- .. 은 상위 위치(parent path)를 의미한다. 프로젝트 위에 폴더가 만들어진다. (이클립스 안에서 확인 불가)

File b =new File("../dummy");

 

2. System 클래스 활용

- 외부에 설정되어 있는 환경정보를 읽어올 수 있는 클래스이다.

System.out.println(System.getProperty("user.home"));//사용자 홈 폴더

System.out.println(System.getProperty("user.dir"));//프로그램 실행위치

 

- user.home에 dummy 폴더를 추가하는 코드이다.

File c = new File(System.getProperty("user.home"),"dummy");

c.mkdir();

 

 

파일 입출력

- 싱글바이트는 그냥 들어가는 것이 가능하다. (ex... 조랭이떡)

- 멀티바이트는 분해라는 작업 필요하고, 불러와서 조립이 필요하다. (ex... 가래떡)

- 객체는 직렬화, 역직렬화를 하고 분해, 조립해서 들어간다. (ex... 떡반죽)

- 문자열은 싱글바이트처럼 처리가 된다.

 

싱글 바이트 입출력

- 입출력 규격인 1byte에 맞는 데이터이다. (자바로 치면 byte)

- 싱글바이트의 용도는 주로 파일 복사를 할 때 사용한다.

 

출력 준비물 (파일을 저장함)  

1. 출력할 파일 객체

- 프로젝트 내에 sample 폴더 안의 single.kh 파일

2. 출력을 위한 통로 객체

 

구조: [프로그램] → [출력통로(stream)] → [파일객체(target)] → [실제파일]

 ☞ 이 방향으로 배치 후 데이터를 하나씩 던지겠다!

 

//둘 다 가능

File target = new File("./sample/single.kh");

File target = new File("sample", "single.kh");

파일 생성은 출력시에만 하며 생략이 가능하고 밑의 코드는 원래 숨겨진다.

target.createNewFile(); //파일 생성

 

· 연결 대상을 알려주는 코드

FileOutputStream stream = new FileOutputStream(target);

 

byte의 유효범위는 -128부터 127 사이이다. (ASCII 코드)

 

· stream에게 데이터를 전달하는 코드

stream.write(9); //\t

stream.write('h'); //104

stream.write('e'); //101

stream.write('l'); //108

stream.write('l'); //108

stream.write('o'); //111

stream.write('\n'); //10

· 모든 작업을 마치고 연결 종료시키는 코드

- 파일을 닫아줘야 경고가 뜨지 않는다.

stream.close();

 

입력 준비물 (파일 내용을 불러옴)

1. 대상 파일 객체

2. 입력용 스트림

 

구조: [프로그램] ← [입력통로(stream)] ← [파일객체(target)] ← [실제파일]

 

File target = new File("sample", "single.kh");

FileInputStream stream = new FileInputStream(target);

 

int a = stream.read(); //한글자를 읽어옴

System.out.println("a = " + a);

파일의 끝 : End Of File = EOF

- 싱글 바이트에서 EOF는 -1이다.

 

이렇게 처리하면 파일에 있는 글자를 다 읽을 수 있다.

while(true) {

int a = stream.read(); 

if(a == -1) break;

System.out.println("a = " + a);

}

stream.close();

 

(Q) 싱글바이트를 이용한 파일 복사 프로그램

//파일 복사 프로그램

 

//준비물 : 입력용 파일 + 스트림, 출력용 파일 + 스트림

 

File readTarget = new File("D:/origin.txt"); //절대경로

FileInputStream readStream = new FileInputStream(readTarget);

 

File writeTarget = new File("./sample/copy.txt");

FileOutputStream writeStream = new FileOutputStream(writeTarget); //상대경로

 

//-1이 들어가면 그만두도록 처리

while(true) {

int a = readStream.read();

if(a == -1) break;

writeStream.write(a);

}

 

//정리

readStream.close();

writeStream.close();

 

조건을 말할 때

사람은 0부터 4까지 출력

컴퓨터는 0번부터 5개 출력

이라고 표현한다.

 

Q. 한글자씩 처리하면 느리다는 것을 알았기 때문에 여러 글자를 한 번에 처리해보는 프로그램

File target = new File("sample/single2.kh");

FileOutputStream stream = new FileOutputStream(target);

 

//출력(파일)

byte[] buffer = new byte[] {'h','e','l','l','o',' ','j','a','v','a'};

stream.write(buffer); // buffer에 들어있는 글자를 모두 출력

stream.write('\n');

stream.write(buffer);

stream.write('\n');

stream.write(buffer, 0, 5); //buffer에서 0번부터 5개를 출력

 

Q. byte를 뭉탱이로 읽어보는 프로그램

//읽을 수 있는 배열 준비

byte[] buffer = new byte[5];

 

//파일과 스트림을 준비

File target = new File("./sample/single2.kh");

FileInputStream stream = new FileInputStream(target);

 

//읽기

while(true) {

int size = stream.read(buffer);

if (size == -1) break;

System.out.print(size);

System.out.print("\t");

System.out.println(Arrays.toString(buffer));

}

 

· 대상을 준비할 때 운영체제가 거부하는 위치인 경우에 절대적인 경로 설정

String home = System.getProperty("user.home");

File readTarget = new File("home", "나머지 경로"); //sample/origin.zip

File writeTarget = new File("home", "나머지 경로"); //sample/copy.zip

 

(Q) 파일 복사 프로그램 (바이트 배열 - 버퍼 사용)

//배열 준비

byte[] buffer = new byte[8192]; //8192 = 자바 표준

 

//대상 준비

File readTarget = new File("경로");

File writeTarget = new File("경로");

 

//스트림 준비

FileInputStream readStream = new FileInputStream(readTarget);

FileOutputStream writeStream = new FileOutputStream(writeTarget);

 

//[읽을 파일] -> readTarget -> readStream -> [프로그램]

// -> writeStream -> writeTarget -> [내보낼파일]

while ( true ) {

int size = readStream.read(buffer);

if(size == -1) break;

writeStream.write(buffer, 0, size);;

}

 

//파일 통로 정리

writeStream.close();

readStream.close();

 

· 시간을 재는 코드

//시간을 재는 첫 번째 방법

long count = 0L; //옮긴 글자수

long total = readTarget.length(); //전체 옮길 글자수

DecimalFormat fmt = new DecimalFormat("#,##0.00");

while (true ) {

int size = readStream.read(buffer);

if(size == -1) break;

writeStream.write(buffer, 0, size);;

count += size;

double persent = count * 100d / total;

System.out.println("[" + fmt.format(persent) + "%] " + count + " / " + total);

//시간을 재는 두 번째 방법(첫 번째 방법은 시간이 더 걸린다)

long start = System.currentTimeMillis();

DecimalFormat fmt = new DecimalFormat("#,##0.00");

while (true ) {

int size = readStream.read(buffer);

if(size == -1) break;

writeStream.write(buffer, 0, size);;

 

}

long finish = System.currentTimeMillis();

long duration = finish - start;

System.out.println("소요시간: " + duration + "ms");

 

 

 

연습한 코드

byte[] buffer = new byte[8192]; // 8192가 가장 효율적

 

//대상 준비 (운영체제가 거부하는 위치인 경우) 절대적

String home = System.getProperty("user.home");

File readTarget = new File("home", "sample/origin.zip"); //복사할 파일(기존 읽을 파일)

File writeTarget = new File("home", "sample/copy.zip"); //복사될 파일(내용이 빈 파일)

 

//스트림 준비

FileInputStream readStream = new FileInputStream(readTarget); //스트림에 파일 연결

FileOutputStream writeStream = new FileOutputStream(writeTarget); //스트림에 파일 연결

 

// [읽을 파일] -> readTarget -> readStream -> [프로그램]

// -> writeStream -> writeTarget -> [내보낼 파일]

// 복사하고 싶은 파일을 readTarget으로 지정하고 readSteam을 연결한다.

// 그리고 writeStream에 복사될 새로운 파일을 지정한 writeTarget을 연결한다.

 

while (true) {

int size = readStream.read(buffer); //준비해놓은 버퍼에 읽어온 파일을 저장

if(size == -1) break;

writeStream.write(buffer, 0, size); //??

}

//파일 통로 정리

writeStream.close();

readStream.close();

 

 

· 파일과 디렉터리 복사 메소드

- 파일일 경우는 버퍼를 이용하여 대량 복사를 진행한다. (배운대로)

- 디렉터리일 경우는 다음과 같은 절차를 거친다.

    1. 대상 위치 안에 원본 디렉터리 이름과 같은 디렉터리를 생성

    2. 원본 디렉터리안에 있는 파일과 디렉터리를 1번에서 만든 위치로 복사

public static void copy(File origin, File target) throws IOException {

if(origin.isFile()) { //파일이라면

//읽기 위한 스트림 생성 및 조합

FileInputStream readStream = new FileInputStream(origin);

BufferedInputStream readBuffer = new BufferedInputStream(readStream);

 

//쓰기 위한 스트림 생성 및 조합

FileOutputStream writeStream = new FileOutputStream(target);

BufferedOutputStream writeBuffer = new BufferedOutputStream(writeStream);

 

//원래 반복문코드인데 버퍼라는 클래스를 사용했을 때 이렇게 쓸 수 있다.

byte[] data = readBuffer.readAllBytes();

writeBuffer.write(data);

 

//정리

writeBuffer.close();

readBuffer.close();

}

else if(origin.isDirectory()) {

//target이 없다면 생성

if(target.exists()) {

target.mkdirs(); //D:test

}

//같은 이름으로 target 아래에 생성

File dir = new File(target, origin.getName());

dir.mkdirs();

//여기까지의 코드는 폴더만 생성되는 것이다.

 

//내용물을 dir 내부에 복사

File[] list = origin.listFiles();

for (File file : list) {

//파일이면 대상 폴더에 같은 이름으로 복사

if(file.isFile()) {

copy(file, new File(dir, file.getName())); //재귀호출

}

//디렉터리면 대상폴더에 하위이름으로 생성

else if (file.isDirectory()) {

copy(file, dir);

}

}

}

}

위 코드를 불러서 파일 삭제

// 파일 삭제는 가능하지만 디렉터리는 비어있는 것만 삭제가 가능

// File target = new File("D:");

// target.delete();

 

File target = new File("D:/dummy");

// target.delete();

 

FileUtil.delete(target);

위 코드를 불러서 파일 복사

// 파일 복사

File a = new File("\"D:/origin.zip\"");

File b = new File("\"D:/copy/copy.zip\"");

 

// File a = new File("D:/test");

// File b = new File("D:/test2");

 

FileUtil.copy(a, b);

 

'Java' 카테고리의 다른 글

object 마지막 게임 캐릭터 정보 예제  (0) 2023.07.17
Java.io.file - multi + object  (0) 2023.07.17
Java.util.collection - Stack, Queue  (0) 2023.07.14
Java.util.collection - Map  (0) 2023.07.14
Java.util.collection - Set  (0) 2023.07.13