Back-End

[JAVA] 기초 공부 12 File I/O

Minch13r 2025. 1. 21. 11:51
package day015.class01;

import java.io.FileWriter;
import java.io.IOException;

// [ 파일 입출력 ]
// 1. 문서(.txt)로 실습
// 2. 이미지(.png, .jpg)로 실습
public class Test01 {
    public static void main(String[] args) {
        // 입력(input) : 컴퓨터에 저장되어있던 파일의 내용을 코드로 불러오기 -> R 읽기 모드
        // 출력(output) : 코드의 내용을 컴퓨터로 내보내기(컴퓨터에 저장시키기) -> W 쓰기 모드

        String path = "D:\\new\\minch13r\\resource\\";
        String fileName = "result.txt"; // 내보내기, 이름 지정
        String msg = "hello";

        //초기화 하지 않으면 닫기가 안 될 수 있기 때문에 null로 초기화
        FileWriter fw = null;
        //fw.write(msg)만 하면 오류날 수 있어서 try~catch문을 사용해야 함
        // 따로 하는것도 좋지만 depth 때문에 한꺼번에 묶어서 진행 가능.
        try {
            fw = new FileWriter(path + fileName);
            fw.write(msg);
        } catch (IOException e) { // 메모리가 부족할 수 있기 때문에
            e.printStackTrace(); // 예외처리를 하는거라고 생각하면 됨
        } finally {
            try {
                fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println("로그 : 파일 쓰기(출력)가 완료되었습니다.");
        }
    }
}
package day015.class01;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class Test02 {
    public static void main(String[] args) {
        String path = "D:\\new\\minch13r\\resource\\";
        String fileName = "result.txt";

        // 버퍼라는 공간에 저장해뒀다가 다시 넘어가는데
        // BufferedReader는 버퍼 공간을 열어주는 열할
        FileReader fr;
        try {
            fr = new FileReader(path + fileName);
            // scope issue로 같이 넣음
            BufferedReader br = new BufferedReader(fr);

            while(true){
                String msg=br.readLine();
                if(msg == null){
                    break;
                    // EOF는 End of File이고 이게 파일 마지막에 숨겨져 있는데
                    // msg == null을 통해서 EOF를 찾고 그만 읽어도 된다고 말해줌
                }
            }
        } catch (FileNotFoundException e){
            e.printStackTrace();
        } catch (IOException e) {
            //throw new RuntimeException(e); //예외처리 미루기
            // main이기 때문에 예외처리 해줄 친구가 없어서
            e.printStackTrace(); // 이게 더 나음, 바로 해결하는 형식
        }
    }
}
package day015.class02;

import java.io.*;

public class Test01 {
    public static void main(String[] args) {
        // 1. 원본 이미지를 코드로 불러오기
        // 2. 복사 이미지를 특정 경로에 작성하기
        String img = "D:\\new\\minch13r\\resource\\test.png";
        // destination 의 줄임말, 복사된 이미지가 도착할 곳
        String dest = "D:\\new\\minch13r\\resource\\test - 복사본.png";

        FileOutputStream fw = null;
        try {
            FileReader fr = new FileReader(img);
            fw = new FileOutputStream(dest);

            // 코드의 길이가 얼마나 길지 모름
            while (true) {
                int character = fr.read(); // 한글자씩 읽음
                if (character == -1) { // EOF는 null 을 넣는다고 했는데
                    // character를 정수형으로 선언해서 -1로 선언
                    // 이유는 EOF가 정수형으로 보면 0이 아니라 -1임
                    break;
                }
                fw.write(character); // 한글짜씩 읽은거 쓰기
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println("로그 : 이미지 복붙 완료");
        }
    }
}

package day015.class03;

// [ 스레드 ]
// 프로세스, 스레드 <<< 자원(리소스, CPU, 메모리(공간))을 할당받으면!
// 프로세스, 프로그램, 어플리케이션, 스레드, S/W, 앱, 어플, ...

// 스레드를 구현하는 방법 01
// Thread 클래스를 "상속" 받아오기
class TestThread extends Thread {
    @Override
    public void run() {
        for(int i=1; i<=10; i++){
            System.out.println(i + ". 스레드 클래스를 상속받아 만들었습니다.");
        }
    }
}

// 스레드를 구현하는 방법 02
// 인터페이스를 구현하는 방법
class MyThread implements Runnable {
    @Override
    public void run() {
        for(int i=1; i<=10; i++){
            System.out.println(i + ". 인터페이스를 구현하여 만들었습니다.");
        }
    }
}

public class Test01 {
    public static void main(String[] args) {
        TestThread tt = new TestThread();
        tt.start();
        // tt.run이 아니라 start를 했더니 run 메서드가 나왔다.
        // cpu 안에 있는 스레드를 실행시켰더니 나온 것들
        // 스레드 안에 run을 만들고 start는 cpu를 실행
        // cpu 실행 -> 스레드 실행 -> run 메서드 호출

        // 인터페이스를 구현했다는 이유만으로는 thread가 될 수 없다.
        // 따라서, 진짜 스레드를 생성하고 내가 만든 유사 스레드를 넣어야 한다.
        MyThread mt = new MyThread();
        Thread t = new Thread(mt);
        t.start();
        // 결과가 이상하게 나오는데, 자원의 issue 때문에 그럼
        // 하드웨어가 자원을 어떻게 할당하는지 관해서 이렇게 나옴
        // 프로세스한테 명령하는거기 때문에 프로세스가 순서를 관리함
        // 그래서 실행할 때마다 순서가 계속 바뀜
    }
}

결과가 실행마다 다름

실행 결과가 매번 다른 이유:

  1. 스케줄링
    • 운영체제의 스레드 스케줄러가 실행 순서를 결정
    • CPU 사용량, 우선순위 등 여러 요소를 고려하여 동적으로 결정
  2. 시스템 상태
    • 다른 프로그램들의 실행 상태
    • 시스템 자원의 사용 상황
    • 메모리 상태 등이 영향을 준다.
  3. 컨텍스트 스위칭
    • 스레드 간 전환이 발생할 때마다 컨텍스트 스위칭이 일어난다..
    • 이 타이밍도 매번 달라질 수 있다.

package day015.class04;
/*
[1]
test.txt 파일에 1~100 사이의 정수를 하나 입력한 후 저장합니다.

사용자와 업다운게임을 진행합니다.
test.txt 파일에 저장된 정수가 80이라면,
1~100 사이의 정수입력 >> 50
업!
51~100 사이의 정수입력 >> 90
다운!
51~89 사이의 정수입력 >> 80
정답입니다!
총 3번만에 정답을 맞추셨습니다.
라고 출력해주세요.
그리고 "총 3번만에 정답을 맞추셨습니다."라는 내용의 result.txt 파일을 생성해주세요.

+) 오류사항에 대하여

1~90 사이의 정수입력 >> 91
잘못된 입력입니다! 범위에 맞게 입력해주세요!
1~19 사이의 정수입력 >> 사과
잘못된 입력입니다! 정수로 입력해주세요!

이렇게 오류 문구를 출력해주시고, 이는 정답맞추기 시도횟수 카운팅 대상이 아닙니다.

++) 1~5번만에 정답을 맞추면 금메달 이미지를,
6~번만에 정답을 맞추면 기본 이미지를 resource 폴더에 저장해주세요.
*/


import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Scanner;

/*===========================================================================*/
// 한글코딩
/*
test.txt 파일에 59 입력한채로 저장

"업다운게임을 진행합니다!"
FileReader로 읽으면 될 것 같음
try - catch 문으로 오류 잡아내고 숫자 판단하는거 진행
txt 파일 내에 있는거는 문자열, 정수형으로 형변환 해야 할 것 같음
사용자가 스캐너를 통해서 숫자를 입력하고 이를 num이라고 가정

1~100 사이의 정수 입력! num을 통해서 50입력함
업!이 나옴
업일 때 num+1 ~ 100 사이의 숫자를 입력하라고 출력
만약 90을 입력하면 다운!이 나옴
다운일 때 지난 num+1과 이번에 입력한 num-1을 통해 범위 출력
예를 들자면 51~89 사이의 정수 입력! 이런식으로
이런식으로 진행하면서 cnt 변수를 생성해서 게임을 한 번 진행할 때마다
카운트를 증가시킬 예정. 입력한 num 값이 FileReader를 통해 읽은 값과 동일하다면
그때 정답입니다! 총 cnt번만에 정답을 맞추셨습니다. 이런식으로 해서 몇번만에
답을 맞췄다고 출력. 그리고 "정답입니다! 총 cnt번만에 정답을 맞추셨습니다." 라는 내용의
result.txt 파일 생성

근데 입력할 때마다 try ~ catch 문을 써서 예외처리를 할거임
예를 들자면 1~90 사이의 정수를 입력하라고 했는데 0인 91을 넣으면
잘못된 입력입니다! 범위에 맞게 입력해주세요! 라고 하고
사과 이런식으로 문자열을 입력하면
잘못된 입력입니다! 정수로 입력해주세요! 라고 출력하게끔 진행

근데 이런 오류는 정답맞추기 시도횟수 카운팅 대상이 아니게끔 처리
카운트 개수가 1~5 사이면 금메달 이미지를, 6번 이상만에 정답을 맞추면
아무 이미지를 resource 폴더에 저장 */
public class Test01 {
    public static void main(String[] args) {
        // 경로
        String path = "C:\\Users\\3333c\\Desktop\\school\\ACADEMY\\resource\\";
//        String path = "D:\\new\\minch13r\\resource\\"; // 본인 test.txt 파일 경로 입력
        // 파일 이름
        String fileName = "test.txt"; // test.txt 파일 불러오기

        // 파일 읽는 작업
        FileReader fr;
        // 파일 쓰는 작업
        FileWriter fw;

        // scope 이슈
        // msg는 파일 내에 있는 문자열을 읽을 변수임
        String msgStr;

        int msg=0; // txt 파일에 있는 내용을 정수화 할 변수

        // 파일 읽기
        try {
            // FileReader는 파일 읽는 것
            fr = new FileReader(path + fileName);
            // scope issue로 같이 넣음
            BufferedReader br = new BufferedReader(fr);

            while (true) {
                msgStr = br.readLine();
                if (msgStr == null) {
                    break;
                    // EOF는 End of File이고 이게 파일 마지막에 숨겨져 있는데
                    // msgStr == null을 통해서 EOF를 찾고 그만 읽어도 된다고 말해줌
                }
//                System.out.println(msgStr); // 문자열 출력
                // 형변환 진행
                msg = Integer.parseInt(msgStr);
            }
        } catch (FileNotFoundException e) { // 파일이 없을 때 예외처리
            System.out.println("파일을 찾을 수 없습니다: " + path + fileName);
            e.printStackTrace();
        } catch (IOException e) { // 파일 읽기 중 오류가 발생했을 때 예외처리
            //throw new RuntimeException(e); //예외처리 미루기
            // main이기 때문에 예외처리 해줄 친구가 없어서
            System.out.println("파일 읽기 중 오류가 발생했습니다.");
            e.printStackTrace(); // 이게 더 나음, 바로 해결하는 형식
        }

        // 스캐너 객체 생성
        Scanner sc = new Scanner(System.in);

        System.out.println("업다운 게임을 진행합니다!");

        // 최대값 / 최소값 / 카운트 선언
        int min = 1;
        int max = 100;
        int cnt = 0;

        // 제대로 된 숫자를 얼만큼 입력할지 몰라서 while로 무한루프
        while(true) {
            System.out.print("숫자 " + min + "~" + max + "사이의 숫자를 입력해주세요!\n>> ");
            int num = sc.nextInt();

            // 범위 체크
            // 최소값이 num 보다 크거나 최대값이 num 보다 작으면
            // OR을 써서 둘 중 하나라도 True면 오류나게 진행
            if(num < min || num > max) {
                System.out.println("잘못된 입력입니다! 범위에 맞게 입력해주세요!");
                continue;
            }

            // 올바른 범위의 입력일 때만 카운트 증가
            // 올바른 범위의 입력이 아니면 카운트 증가하지 않음
            cnt++;

            // 정답일 때
            if (num == msg) {
                System.out.println("정답입니다! " + cnt + "번 만에 맞추셨습니다!");
                // 게임이 끝난 후, 시도 횟수에 따라 이미지 복사
                try {
                    // 원본 이미지
                    String sourceImage;
                    // 복사본 이미지
                    String targetImage;
                    if (cnt <= 5) { // 5번 이하로 정답을 맞추면
                        sourceImage = "C:\\Users\\3333c\\Desktop\\school\\ACADEMY\\java\\goldmedal.png";
                        targetImage = path + "goldmedal.png";
                        System.out.println("축하합니다! 금메달 획득!");
                    } else { // 6번 이상으로 정답을 맞추면
                        sourceImage = "C:\\Users\\3333c\\Desktop\\school\\ACADEMY\\java\\fire.png";
                        targetImage = path + "fire.png";
                        System.out.println("아쉽네요! 다음에 더 잘해봐요!");
                    }

                    // 파일 읽기와 쓰기 객체 생성
                    // stream 형식으로 해야 더 복사가 잘 됨. 오류 나서 해결
                    FileInputStream fis = new FileInputStream(sourceImage);
                    FileOutputStream fos = new FileOutputStream(targetImage);

                    // 한 글자씩 읽어서 복사
                    while (true) {
                        int data = fis.read();
                        if (data == -1) { // 파일의 끝에 도달하면 종료
                            break;
                        }
                        fos.write(data);
                    }
                    // 파일 닫기
                    fis.close();
                    fos.close();
                    System.out.println("이미지가 성공적으로 저장되었습니다!");
                } catch (Exception e) {
                    System.out.println("파일 복사 중 오류 발생");
                }
                break;
            } else if (num > msg) { // txt 파일 안 있는 숫자가 입력한 값보다 작으면
                System.out.println("Down!");
                max = num - 1; // 최대값은 num - 1이여야 범위가 줄음
            } else { // txt 파일 안 있는 숫자가 입력한 값보다 크면
                System.out.println("Up!");
                min = num + 1; // 최소값은 num + 1이여야 범위가 줄음
            }
        }
    }
}

package day015.class04;
/*
[2]
가족 공동 계좌가 있습니다.
만원 저금이 되어있습니다.

엄마 / 아빠 / 동생 / 나
1000  2000  3000  5000

동기화 처리를 하셔서,
돈이 정상적으로 출금될수있도록 코딩해주세요.

+) 누가 얼마를 출금했습니다.
남은 금액은 ㅇㅇㅇ원입니다.
누가 얼마를 출금실패했습니다.
남은 금액은 ㅇㅇㅇ원입니다.
* */

/*
 * 클래스 두개 생성. 출금 클래스와 가족 클래스
 * 출금 클래스는 synchronized를 쓸 예정
 * 초기 금액이 있는데 가족 클래스를 통해 만들어진
 * 가족 중 누가 쓰는 금액에 따라 값을 달리 해야 하기 때문에
 * 변수 처리. 그러면 기존 금액 가족에 할당되는 금액을 빼고
 * 남은 금액을 출력해줘야 한다. 남은 금액이 0원보다 많으면
 * 출금이 가능하고 0원보다 적으면 출금이 불가능하다.
 *
 * 가족 클래스는 가족이름과 가족이 가지고 있는 금액을
 * 변수로 가지고 있어야 한다. 가족이 가지고 있는 금액은
 * 가족이름을 통해 출금 클래스에 접근할 수 있도록
 * 메소드를 만들어야 한다. 그리고 출금 클래스를 상속해
 * 출금 매개변수를 사용 가능하게끔 한다. 이후 스레드를 사용해
 * run과 start 그 형식으로 진행하면 될 것 같다.
 * */

class WithDraw {
    private int money = 10000; // 기본 금액

    // synchronized를 통해서 동기화 처리
    public synchronized void withDraw(int money, String name) {
        if (this.money >= money) { // 출금 가능
            this.money -= money;
            System.out.println(name + "님이 " + money + "원을 출금했습니다.");
            System.out.println("남은 금액은 " + this.money + "원입니다.");
        } else { // 출금 실패
            System.out.println(name + "님이 " + money + "원을 출금실패했습니다.");
            System.out.println("남은 금액은 " + this.money + "원입니다.");
        }
    }
}

class Family extends Thread {
    private String name; // 가족 이름
    private int money; // 가족이 가지고 있는 금액
    private WithDraw withDraw; // 출금 클래스를 통해 접근 가능

    // 생성자, withDraw를 통해 출금 클래스에 접근 가능
    public Family(String name, int money, WithDraw withDraw) {
        this.name = name;
        this.money = money;
        this.withDraw = withDraw;
    }

    // run 오버라이딩
    @Override
    public void run() {
        withDraw.withDraw(money, name);
    }

}

public class Test02 {
    public static void main(String[] args) {
        WithDraw wd = new WithDraw();

        // 가족 구성원 생성
        Family mom = new Family("엄마", 1000, wd);
        Family dad = new Family("아빠", 2000, wd);
        Family sister = new Family("동생", 3000, wd);
        Family me = new Family("나", 5000, wd);

        // 스레드 시작
        mom.start();
        dad.start();
        sister.start();
        me.start();
    }
}

'Back-End' 카테고리의 다른 글

[JAVA] 기초 공부 14 MVC-1  (1) 2025.01.23
[JAVA] 기초 공부 13 MVC  (1) 2025.01.22
[JAVA] 기초공부 11 제네릭과 컬렉션  (1) 2025.01.20
[JAVA] 3중 상속과 인터페이스  (4) 2025.01.17
[JAVA] 기초 공부 10  (1) 2025.01.15