D+37데이터입출력.2진파일읽고쓰기, txt파일읽고쓰기, 보조스트림.buffer,데코레이션패턴,폴더및파일만들기, 그외다양한클래스들.optional,generic,stream,filter,fkaektlr

Bku·2024년 2월 19일

학원 일기

목록 보기
35/67
post-thumbnail

데이터 입출력

2진 파일에 출력하기(쓰기)

특정 길이만큼 쓰기

public class WriteApplication {
    public static void main(String[] args) throws IOException {
        OutputStream outputStream
                = new FileOutputStream("src/main/resources/test3.txt");
        byte[] array = {10,20,30,40,50};
        outputStream.write(array,1,3); // 배열을 한번에 넣는 방법이다.
        // 배열이름 , 인덱스 번호, 인덱스 마지막 번호이다.
        
    }
}

배열을 한번에 출력하는 방법이다. write함수를 사용한는 것은 똑같지만 배열의 이름과 인덱스 번호로 여러 개의 값을 한번에 파일쓰기가 가능하다.

2진 파일 읽기

2진 파일 읽기

이진파일은 그냥 읽을 수가 없다. 그래서 이 파일을 읽기 위해 읽기처리를 하여야한다.

public class ReadApplication {
    public static void main(String[] args) throws Exception {
        InputStream inputStream
                = new FileInputStream("src/main/resources/test1.db");

        // 파일읽기 : 반복문 : 파일의 끝까지 실행해서 읽을 수 있다.
        // (파일의 끝은 항상 -1이 나오기때문에 반복문을 돌리다가 -1이 나오면 탈출하면된다.)

        while (true){
            int data = inputStream.read();
            if (data == -1) break; // 조건문이 한줄이면 한줄로도 조건문을 만들수 있다.
            System.out.println(data);// 읽은 내용 화면에 출력
        }
        // 사용후 파일 닫기
        inputStream.close();
    }
}

저번 시간에 만든 test1.db파일을 읽었다. read함수는 정수를 반환하는데 파일의 마지막은 항상 -1이므로 read함수가 -1을 반환하면 파일의 마지막이라는 뜻이므로 반복문을 탈출해야한다.

write로 쓴 값이 잘 나온다. 쓸 때는 write함수 읽을 때는 read함수를 사용한다.

buffer배열을 이용한 파일 읽기

public class ReadApplication {
    public static void main(String[] args) throws IOException {
        InputStream inputStream
                = new FileInputStream("src/main/resources/test2.db");
        // 버퍼 : 배열생성 : 속도향상 : 한번에 크게 읽기
        byte[] buffer = new byte[100];
        // 읽기 반복문 : 파일끝까지 읽기
        while (true){
            int data = inputStream.read(buffer); // 읽은 것을 배열에 넣는다. 배열크기만큼 최대한 읽기
            if (data == -1) break;
        }
        for (byte b : buffer) {
            System.out.println(b);
        }
        inputStream.close();
    }

아까와는 달리 buffer라는 배열을 만들었다. 그리고 read() 배열을 넣어주어 읽는 값을 배열에 넣고 한번에 출력하게 했다. 전에는 데이터를 하나씩 읽어서 총 세번을 읽었지만 이렇게 하면 read함수가 모든 데이터를 한번만 읽고 그 값을 다 배열에 넣기 때문에
그냥 read를 읽는 것보다 3배정도 좋은 성능을 낼 수 있다.(아마 시간복잡도는 읽는 횟수를 기준으로 하니까 그런듯)

지정된 길이만큼 읽기

쓰기에도 특정 길이만큼만 쓰기가 가능했다. 읽기도 이것이 가능하다. 이 전까지는 끝까지 읽었지만 이번에는 지정된 길이만큼 읽기를 해보자.

public class ReadApplication {
    public static void main(String[] args) throws IOException {
        InputStream inputStream
                = new FileInputStream("src/main/resources/test3.txt");

        byte[] buffer = new byte[5];
        // 파일을 읽어서 buffer의 특정 인덱스 위치에 저장
            int data = inputStream.read(buffer, 2, 3);
        for (byte b : buffer) {
            System.out.println(b);
        }
        inputStream.close();
    }
}

text파일에서 파일을 읽어서 buffer의 2번 인덱스부터 3개를 넣을 것이다.

buffer함수의 2번부터 text3파일을 차례로 넣었다.

text파일 쓰기

text파일 쓰기

2진 파일이 아닌 text파일도 쓰기가 가능하다.
차이는 새로운 클래스인 Writer클래스를 사용한다는 것이다.

public class writeApplication {
    public static void main(String[] args) throws IOException {
        // Reader 문자기반 파일이고 2byte 읽기이다. 최상위 추상클래스이다.
                // 자식 클래스의 형태는 000Reader이다.
        // Writer 문자기반 파일이고 2byte 쓰기이다. 최상위 추상클래스이다.
                // 자식 클래스의 형태는 000Writer이다.

        Writer writer = new
                FileWriter("src/main/resources/text7.txt");

        char a = 'A';
        char b = 'B';
        char c = 'C';
        
        writer.write(a);
        writer.write(b);
        writer.write(c);
        
        writer.flush(); 
        
        writer.close();
    }
}


text7파일이 잘 만들어졌고 txt파일은 참고로 2진파일과는 다르게 사용자가 읽을 수 있는 파일이다.

배열을 이용하여 txt파일 쓰기

public class WriteApplication {
    public static void main(String[] args) throws IOException {
        Writer writer = new FileWriter("src/main/resources/text8.txt");
        char[] array = {'A', 'B', 'C'};

        // 배열로 쓰기 
        writer.write(array);
        
        writer.flush();
        
        writer.close();


    }
}

배열을 write함수에 넣어서 한번에 출력할 수 있다.

txt파일에 배열의 일부만 쓰기

public class WriteApplication {
    public static void main(String[] args) throws IOException {
        Writer writer
                = new FileWriter("src/main/resources/text9.txt");
        char[] array = {'A','B','C','D','E'};

        writer.write(array , 1, 3); // B, C, D

        writer.flush();
        writer.close();
    }
}

방법은 이진파일과 같다.

예상대로 B,C,D가 잘 만들어졌다.

txt파일에 문자열 쓰기

지금까지 char형태로 문자만 넣었다. 이제 문자열을 넣어보자.

public class WriteApplication {
    public static void main(String[] args) throws IOException {
        Writer writer
                = new FileWriter("src/main/resources/text10.txt");
        String str = "ABC";
        writer.write(str);
        writer.flush();
        writer.close();
    }
}


잘 만들어진 것을 확인 할 수 있다.

text파일 읽기

text파일 읽기

public class ReadApplication {
    public static void main(String[] args) throws IOException {
        Reader reader = new FileReader("src/main/resources/text7.txt");
        while (true){
            int data = reader.read();
            if (data == -1) {
                break;
            }
            System.out.println((char)data);
        }

        reader.close();
    }
}

우리가 입력한 데이터의 타입은 char인데 read는 int를 반환하므로 출력시 다운캐스팅을 해줄 필요가 있다.(안 하면 정수가 출력됨)

buffer배열을 이용한 txt파일 읽기

public class ReadApplication {
    public static void main(String[] args) throws IOException {
        Reader reader
                = new FileReader("src/main/resources/text8.txt");

        char[] buffer = new char[5];

        while (true){
            int num = reader.read(buffer);
            if (num == -1) break;
        }
        for (char c : buffer) {
            System.out.println(c);
        }
        reader.close();
    }
}


방법은 똑같다. 읽어서 배열에 넣고 배열을 출력하는 거라서 read함수를 한번만 사용해도 된다는 속도면에서 장점이 있다.

특정 길이만클 txt파일 읽기

public class ReadApplication {
    public static void main(String[] args) throws IOException {
        Reader reader = new FileReader("src/main/resources/text9.txt");
        char[] buffer = new char[6];

        int num = reader.read(buffer, 2, 3);
        for (char c : buffer) {
            System.out.println(c);
        }
        reader.close();

    }
}

텍스트 파일의 문자를 배열의 특정 인덱스부터 크기만큼 집어넣어주는 것이다. 이진파일의 특정길이만큼 읽는 것과 같은 방식이다

보조스트림

다른스트림과 결합되어 편리한 기능을 제공하는 클래스이다. 결합할 때는 데코레이션 패턴을 이용한다.

데코레이션패턴

new 보조스트림(new 기본스트림)

다음과 같이 생성자에 새로운 클래스를 집어넣어서 사용하는 것이다. 두개의 클래스를 합칠때 새로운 클래스를 만들지 않고 저렇게 합칠 수 있게 해준다.

보조스트림으로 읽고 쓰기

public class CharConvertApplication {

    public static void main(String[] args) throws Exception {
       write("ABC");
        System.out.println(read());
    }

    // 쓰기 전역함수
    public static void write (String str) throws IOException{
        FileOutputStream fileOutputStream
                = new FileOutputStream("src/main/resources/test1.txt");

        // 보조스트림을 이용해서 문자기반 파일로 변경가능
        Writer writer = new OutputStreamWriter(fileOutputStream);// 이진 파일을 텍스트 파일로OutputStreamWriter을 통해 바꿔준다.
        // 이때 바꿀 클래스를 생성자에 넣고 최종으로 만들어지는 형태를 타입으로 지정한다.
        // 여기서는 이진파일을 텍스트 파일로 바꿀거니까, 이진파일을 스트림해주는 클래스를 생성자의 괄호안에,
        // 텍스트 파일을 스트림 해주는 Writer을 타입으로 해주었다.

        // 버퍼 출력
        writer.write(str);
        writer.flush();
        writer.close();
    }
    // 읽기 전역함수
    public static String read() throws Exception {
        FileInputStream fileInputStream =
                new FileInputStream("src/main/resources/test1.txt");

        Reader reader = new InputStreamReader(fileInputStream);

        char[] buffer = new char[100];
        int readCharNum = reader.read(buffer); // 100문자 읽기 -> buffer 저장
        reader.close(); // 파일 닫기

        String data = new String(buffer, 0, readCharNum);

        return data;
    }
}

성능향상 보조스트림 bufferedReader, bufferdWriter

스캐너보다 빠른 읽기 기능이 있다. 바로 bufferdReader클래스이다.
이건 문자 기반 이므로 Reader클래스를 사용하고 InputStreamReder를 Reader클래스에 넣어주어야한다. 파일 입력이 아닌 키보드 입력을 받아야하므로 파일경로가 아닌 "System.in"을 사용한다. 출력은 모든 in을 out으로 바꿔주면된다.

public class GetLineApplication {
    // 키보드 입력으로는 System.in을 사용
    // 뫁니터 출력으로는 System.out을 사용 예) System.out.print()
    public static void main(String[] args) throws IOException {
        // 1) 문자기반 보조스트림(기능추가1)
        Reader reader
                = new InputStreamReader(System.in); // 파일이 아니라 키보드 입력을 받아보자.

        // 2) 성능향상 보조스트림 (기능추가2)
        BufferedReader bufferedReader
                =new BufferedReader(reader);
                
        // 축약형
        BufferedReader bufferedReader1
                = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter writer
                = new BufferedWriter(new OutputStreamWriter(System.out));

        // 문제 : q가 입력될때까지 입력된 값을 하면에 출력하기
        while (true){
            String str = bufferedReader.readLine();
            if (str.equals("q")) {
                writer.write(str);
                break;
            }


        }
        writer.flush();
        writer.close();
    }
}

마지막에는 꼭 flush, close 처리를 해주어야한다.
간단하게 다음과 같이 표현할 수도 있다.
scanner보다 빠르고 성능이 좋다.

폴더, 파일 만들기

코드로 폴더와 파일을 만들 수 있다.

public class FileApplication {
    public static void main(String[] args) throws Exception {
        File dir = new File("src/main/resources/images");
        File file = new File("src/main/resources/file1.txt");
//        폴더 만들기 : 변수.mkdir()
        dir.mkdir();
//        파일 만들기 : 변수.createNewFile();
        file.createNewFile();

//        폴더 내 정보보기 : 폴더, 파일 등
        File temp = new File("src/main/resources/");
        File[] contents = temp.listFiles();  // 폴더내 정보를 배열로 가져옴

        System.out.println(contents[0].getName()); // file1.txt : 파일
        System.out.println(contents[1].getName()); // images    : 폴더

    }
}

그 외 다양한 클래스들

optional 클래스

자바에서는 null예외가 발생할 수 있다. 이것을 대비하기 위한 함수들이 있는 클래스이다.

Optional에 값넣기

Optional<String> str = Optional.of("abcde");

of함수를 이용하여 제네릭에 넣어둔타입과 같은 타입의 값을 넣으면된다.

Optional에서 값 가져오기

System.out.println(str.get());

get함수를 이용하여 값을 가져올 수 있다.

Optional에 값 있는지 확인하기

 Optional<Integer> num = Optional.of(0);

        System.out.println(num.isPresent());


0이라는 값이 존재하므로 true가 나온다.

빈 객체 저장하기

Optional optional = Optional.empty();

System.out.println(optional);


Optional이 비어있으므로 false가 나온다.

빈 객체일 경우 메세지 출력하기

// 만약 빈객체이면(null)이면 메세지 출력하기
        System.out.println(optional.orElse("값 없음"));

orElse함수를 이용하면 null이 발생할시 예외가 아닌 대체 메세지가 나가게 할 수있다.

Generic클래스

해당 클래스에는 특정 타입의 변수만 받아야하는 경우가 있다. 예를 들어 List함수. 그럴때 타입을 정해주는 것이 Generic클래스이다.

부모에 자식 넣기

그렇다면 상속관계에서 자식 클래스는 부모타입에 들어갈 수 있을까?

다형성과 마찬가지로 들어갈 수 있다.

자식에 부모넣기

반대로 자식클래스는 부모타입에 들어갈까?

역시 안된다.

다운캐스팅

자식타입의 함수를 사용하고 싶을경우는 일반 상속과도 마찬가지로 다운캐스팅을 사용한다.

제네릭함수는 상속관계에서 다형성과 거의 같은 성질을 띈다고 생각하면된다.

제네릭함수의 여러가지 형태

 public static void printAll(ArrayList<Product> list) {
        // 일반 제네릭 함수 사용법이다.
        for(Object p : list) {
            System.out.println(p);
        }
    }

    public static void printAll2(ArrayList<? extends Tv> list) { // Tv클래스의 자식클래스만 들어올 수 있다.
        // ?는 클래스를 나타내는 변수같은 느낌이고 Tv를 상속받는 클래스 즉 Tv의 자식만 들어올 수 있다.
        for(Object p : list) {
            System.out.println(p);
        }
    }

    public static void printAll3(ArrayList<? super Product> list) {
        // Product의 부모클래스만 들어올 수 있다.
        for(Object p : list) {
            System.out.println(p);
        }
    }

    public static void printAll4(ArrayList<?> list) {
        // 모든 클래스가 들어올 수 있다. 제네릭을 사용하지 않은것과 같다.
        for(Object p : list) {
            System.out.println(p);
        }
    }

일반적인 사용, 부모만, 자식만 들어오게 할 수 있고, 모든 함수가 들어오게도 제네릭 함수로 표현할 수 있다.

Stream 클래스

stream의 장점은 간단하게 병렬처리(multi-threading)가 가능하다는 점입니다. 하나의 작업을 둘 이상의 작업으로 잘게 나눠서 동시에 진행하는 것을 병렬 처리(parallel processing)라고 합니다. 즉 쓰레드를 이용해 많은 요소들을 빠르게 처리할수 있습니다.

stream과정

데이터가 3단계를 거쳐 완성되는데 이게 파이프라인을 흐르면서 마지막에 나온다고 해서 stream이라고 보면된다.
3단계 : 1) stream선언 -> 데이터가공(map, filter) -> 결과내보내기

데이터 가공 과정

함수형 프로그래밍에서는 일반적으로 세 가지 주요 작업이 있다.

매핑(Mapping): 각 요소를 다른 요소로 변환하거나 매핑하는 것.
필터링(Filtering): 조건을 기반으로 요소를 선택하여 필터링하는 것.
추출(Extracting) 또는 리듀싱(Reducing): 모든 요소를 하나로 결합하거나 요약하는 것.

  • map()은 매핑 작업을 담당합니다. 이 외에도 Java 스트림에서는 다른 중요한 함수들이 있다.

  • filter(Predicate)는 주어진 조건에 맞는 요소만을 걸러낸다.

  • reduce(identity, BinaryOperator)는 요소를 하나로 축소하는 작업을 수행한다. 이는 합계를 계산하거나 최댓값 또는 최솟값을 찾는 데 사용될 수 있다.

  • flatMap(Function)은 각 요소를 다른 스트림으로 매핑하고 그 결과 스트림들을 하나의 스트림으로 평면화합니다. 이것은 중첩된 리스트를 펼치는 데 유용하다.

이러한 함수들을 결합하여 복잡한 데이터 처리 작업을 쉽게 수행할 수 있다.

map이용 데이터 가공

public class StreamApplication {
    public static void main(String[] args) {
        // 함수형 프로그래밍 패러다임이 곽광받는다.
        //  절차적프로그래밍 -> 구조적 프로그래밍 -> 객체지향 프로그래밍 -> 만약 간다면 함수형 프로그래밍
        // 그래서 자바도 급하게 함수형 프로그래밍의 여러 문법들을 가져오게 되었다.
        // 그 중 하나가 stream이다. 특징은 자동 반복문 실행이다.

        // 사용법
        // 1) 정의부 :
        // 2) 가공 :
        // 3) 결과 내보내기 :
        List<String> list = new ArrayList<>();
        list.add("apple");
        list.add("ball");
        list.add("car");
        list.add("daddy");
        list.add("ear");
        list.add("fox");

        // forEach : 단순 출력용
        list.forEach(s -> System.out.println(s)); // 이걸로 반복문을 대신한다. 람다식
        list.forEach(System.out::println); // 화살표 없이 사용하는 법. 단축키 soutc

        // map 사용 데이터 가공
        // 예제 : 배열의 값을 대문자로 바꿔서 출력하기
        List<String> list2 = list.stream() // 1) 정의부 : stream을 사용할 것이다라고 정의해준다.
                .map(s -> s.toUpperCase()) // 2) 가공 : map함수로 데이터를 가공한다. 여기선 대문자 바꿔주기 함수로 가공
                .collect(Collectors.toList()); 
        // 3) 결과 내보내기 : 가공된 데이터를 모아서 내보내는 코드이다.
        // toList로 List형태로 내보냈지만,
        // toSet, toMap같은 다양한 자료구조가 존재한다.

        list2.forEach(s -> System.out.println(s));


    }
}


소문자 배열이 대문자로 모두 바꼈다. 이렇게 배열의 데이터를 한번에 가공할때 사용할 수 있다.

이외에도 값으 제곱으로 바꾸기 등 데이터를 변환시킬 수 있다.

filter이용 데이터 가공

public class StreamApplication {
    public static void main(String[] args) {
        // 함수형 프로그래밍 패러다임이 곽광받는다.
        //  절차적프로그래밍 -> 구조적 프로그래밍 -> 객체지향 프로그래밍 -> 만약 간다면 함수형 프로그래밍
        // 그래서 자바도 급하게 함수형 프로그래밍의 여러 문법들을 가져오게 되었다.
        // 그 중 하나가 stream이다. 특징은 자동 반복문 실행이다.

        // 사용법
        // 1) 정의부 :
        // 2) 가공 :
        // 3) 결과 내보내기 :
        List<String> list = new ArrayList<>();
        list.add("apple");
        list.add("ball");
        list.add("car");
        list.add("daddy");
        list.add("ear");
        list.add("fox");
         // filter사용 : 일부만 걸러내기
        System.out.println("========filter=========");
        // 예제 : 배열값에서 문자길이가 3초과인 값만으로 새로운 배열 만들기
        List<String> list3 = list.stream()
               .filter(s -> s.length() > 3) // 문자길이가 3초과인 애만 거른다.
                .collect(Collectors.toList());
        list3.forEach(s -> System.out.println(s));
    }
}


문자길이가 4이상인 애만 걸러져 나왔다.

filter는 조건을 주고 그 조건에 맞는 애들만 거르는 역할을 한다.

람다식

함수형 인터페이스

자바의 함수는 매개변수나 리턴값으로 함수가 올 수가 없다. 그래서 이것을 하기 위해서 생긴것이 함수형 인터페이스고 5가지가 있다.

참고만하자

public class RandaApplication {
    public static void main(String[] args) {
        // 사용처 stream에서 가공시 사용
        //       함수형 인터페이스에서 사용
        //       익명 크래스에서 사용

        // 함수형 인터페이스 :
        //  -목적 : 자바의 함수는 매개변수나 return값으로 함수를 사용할 수 없다.
        //  그래서 만든 것이 함수형 인터페이스

        // 함수형 인터페이스의 종류
        //  Runnable 함수 : 매개변수(X), 반환값(X) ex) void run()
//          Supplier 함수 : 매개변수도(X), 반환값(O) EX) T get() // T는 대표 T라고 하며 어떤 타입이든 들어올 수 있다.
//          Consumer 함수 : 매개변수(O), 반환값(X) ex) void accect(T t)
//          Function 함수 : 일반적인 함수, 매개변수(O), 반환값(O) ex) T apply(T t)
//          Predicate 함수 : 매개변수(O), 반환값 이 boolean(참/거짓) ex) boolean test(T t)
        Supplier<Integer> s = () -> (int)(Math.random()*100 + 1);

        List<Integer> list = new ArrayList<>();
        makeRandomList(s, list);

    }
    //    1~100까지 중 10개의 랜덤 숫자 넣기
    static <T> void makeRandomList(Supplier<T> s, List<T> list) {
        for (int i = 0; i < 10; i++) {
            list.add(s.get());     // s.get() : 매개함수의 값을 조회하는 함수
        }
        System.out.println(list);
    }
}
profile
기억보단 기록

0개의 댓글