본문 바로가기

C++

c++ 16 ( 파일 입출력 2 )

vs code에서 Solution Explorer에서 마우스 오른쪽 버튼을 클릭하고 

Open folder in File Explorer 를 클릭하면 만들어진 폴더가 나온다.

 

fopen( ... )

fopen을 사용할 때에는 꼭 습관처럼 

fclose( ... ) 먼저 아래쪽에 만들어 두고

그 사이에 코딩을 하는 것이 가장 좋다

 

이는 다른 프로그래밍에서도 마찬가지인데

다른 프로그래밍 언어에서도 똑같이

파일을 열고 파일을 닫고 하기 때문이다.

 

fclose( ... ) 

fclose( ... ) 함수는 nullptr을 파라미터로 받았을 경우

런타임 오류가 발생하게 된다.

 

fopen을 "r" 옵션으로 열었을 경우에 파일이 없다면

nullptr을 리턴하게 되는데

이 경우를 생각해서 프로그래밍을 해야 한다.

 

fopen을 통해서 읽기 전용으로 파일을 열었을 때 파일이 만약에 없다면

반환 값은 null pointer가 된다.

fclose 같은 경우는 nullptr에 대해서는 런타임 오류가 발생하게 된다.

 

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>


int main() {
    
    // fopen
    FILE* infile = fopen("sample.txt", "r");
    
    
    if(infile != nullptr) fclose(infile);
    
    bool fileIsExists = infile != nullptr;
    // fileIsExists이 true면 "sample.txt" 파일이 존재 판단 가능


    return 0;
}

 

 

위 내용을 함수화 해서 파일이 존재하는지 존재하지 않는지 체크하는 함수 만들기

 

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>


bool file_exists(const char* filename) {
    FILE* fp = fopen(filename, "r");
    
    if (fp != nullptr) fclose(fp);
    
    return fp != nullptr;
}



int main() {
   
    bool exists = file_exists("sample.txt");
    
    if (exists) {
        printf("파일이 존재합니다.\n"); 
    }
    else {
        printf("파일이 존재하지 않습니다.\n");
    }
    // sample.txt가 존재하지 않기 때문에 파일이 존재하지 않습니다 출력
    
    return 0;
}

 

out.txt 파일을 만들어 안에 100 Hello World 100 내용이 출력되게 만들기

fprintf 사용

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>


bool file_exists(const char* filename) {
    FILE* fp = fopen(filename, "r");
    
    if (fp != nullptr) fclose(fp);
    
    return fp != nullptr;
}



int main() {
    
    FILE* outfile = fopen("out.txt", "w");
    
    fprintf(outfile, "%d Hello World %d", 100, 100);
     
    fclose(outfile);
    
    return 0;
}

 

 

fscanf의 큰 장점 : format 문자열을 이용해서 여러개의 값을 한꺼번에 받아올 수 있는 것

여러개의 값을 한 줄에서 읽어올 수 있다

 

 

 

fscanf를 이용해서 정수 받아오기

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>


bool file_exists(const char* filename) {
    FILE* fp = fopen(filename, "r");
    
    if (fp != nullptr) fclose(fp);
    
    return fp != nullptr;
}



int main() {
    
    FILE* infile = fopen("in.txt", "r");
    
    int data1;
    int data2;
    
    fscanf(infile, "%d %d", &data1, &data2);
    // in.txt파일에서 숫자를 2개를 받아오는데 공백을 기준으로 나눠서 읽어오게 된다. 
    
    printf("data1 : %d, data2 : %d", data1, data2);
    
    fclose(infile);
    
    return 0;
}

 

fscanf를 이용해서 문자열을 받아오기

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>


bool file_exists(const char* filename) {
    FILE* fp = fopen(filename, "r");
    
    if (fp != nullptr) fclose(fp);
    
    return fp != nullptr;
}




int main() {
    
    FILE* infile = fopen("in.txt", "r");
    
    char line[128];
    
    fscanf(infile, "%127[^\n]s", line);
    
    printf("%d", line);
    
    fclose(infile);
    
    return 0;
}

 

 

텍스트 인코딩 문제

C++는 기본적으로 MS 기본 인코딩이나 ANSI 인코딩을 활용하여

문자열을 저장하고 활용한다.

이를 우리가 인지하지 못한 상태에서

다른 텍스트 에디터를 활용하여 저장된 문자열을 불러오게 되면

한글 등 영문자가 아닌 다른 문자들이 올바르지 않은 문자열로 나타나게 된다.

 

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>


bool file_exists(const char* filename) {
    FILE* fp = fopen(filename, "r");
    
    if (fp != nullptr) fclose(fp);
    
    return fp != nullptr;
}



int main() {
    
    FILE* infile = fopen("in.txt", "r");
    
    char line[128];
    int value1;
    
    fscanf(infile, "%127[^\n]s %d", line, value1);
    
    printf("문자열 : %s\n", line);
    printf("value: %d\n", value1); // value에 오류 발생
    
    fclose(infile);
    
    return 0;
}

 

fscanf로 문자열을 받아올 때 주의해야할 점

: 문자열과 숫자들을 한 줄에서 읽으려고 할 때 문제가 발생할 수 있다.

scanf로 문자열을 입력 받으려고 하면 한 줄을 전부 다 문자열로 읽어오는게 편하다

 

 

 

txt 파일을 두 줄 읽어오고 싶은 경우

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>


bool file_exists(const char* filename) {
    FILE* fp = fopen(filename, "r");
    
    if (fp != nullptr) fclose(fp);
    
    return fp != nullptr;
}



int main() {
    
    FILE* infile = fopen("in.txt", "r");
    
    char line[256];


    
    fscanf(infile, "%255[^\n]s %d", line);
    printf("%s\n", line);
    
    fgetc(infile); // 다음 줄을 읽어오기 위함
    // fgetc는 파일스트림의 현재 포인터에서 문자 하나를 읽어서 리턴해주는 함수
    
    fscanf(infile, "%255[^\n]s %d", line);
    printf("%s\n", line);
   
    fclose(infile);
    
    return 0;
}

 

fscanf는 개행문자(\n) 까지만 읽는 것인데 다시 scanf를 하려고 보면 

다시 fscanf를 하려고 보니까 

바로 뒤에 개행문자가 있어 아무것도 하지않고 그냥 지나가는 거

아무것도 하지 않았으니까 기존 line 문자 배열에 있는 값이 그대로 printf에 출력이 된다.

(물론 이것은 %s 옵션이 아닌 %99[^\n]s 옵션이기 때문에 그렇다.)

그래서 이것을 fgetc를 통해서 문자를 하나 읽어서 버리게 되면 

fscanf가 읽으려고 봤더니 파일 포인터가 다음 줄에 있어서 

그 지점부터 한 줄을 읽게 되는 것이다.

즉, fgetc는 개행문자를 읽어서 버려주는 역할 

fscanf는 바로 뒤에 개행문자가 있으면 아무것도 하지 않기 때문에 

우리가 이 fgetc를 하지 않았을 때는 그냥 같은 줄이 두 번 출력이 되는 것이다.

그리고 fgetc가 있으니까 출력을 하고 다음 입력을 받고 다음 문자열들을 읽어올 수 있게 되는 것

 

 

 

자동으로 모든 줄을 다 읽어오고 싶은 경우

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>


bool file_exists(const char* filename) {
    FILE* fp = fopen(filename, "r");
    
    if (fp != nullptr) fclose(fp);
    
    return fp != nullptr;
}


int main() {
    
    FILE* infile = fopen("in.txt", "r");
    
    while (true) {
        char line[256];
        fscanf(infile, "%255[^\n]s %d", line);
        fgetc(infile);
        
        printf("%s\n", line);
        
        if(feof(infile) == 1) break;
        // infile의 파일포인터가 파일의 끝에 당도했기 때문에 break로 while루프 탈출
    }


    fclose(infile);
    
    return 0;
}

 

feof 함수 사용

feof 함수 : 파일의 끝에 당도하게 되면 1을 리턴 

 

 

 

연습 프로그래밍

 

 

Headerr.h

#ifndef __HEADERR_H__
#define __HEADERR_H__


#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <string>
#include <vector>


int getInteger(const char* prompt);
std::string getString(const char* prompt);


// student 객체 만들기
class Student {
public:
    std::string name;
    int korean;
    int math;
    int english;
    
    Student(std::string name, int korean, int math, int english);
    void printInfo();
};


#endif

 

Source.cpp

#include "Headerr.h"

int main() {
    
    std::vector<Student> students;
    
    // 파일을 읽어서 students라는 vector에 값을 하나하나 집어넣어주기
    FILE* infile = fopen("students.txt", "r");
    while (true) {
        if(feof(infile) == 1) break;
        char name[100];
        int korean;
        int math;
        int english;
        int result;
        result = fscanf(infile, "%99[^\n]s", name);
        if (result == -1) break;
        fgetc(infile);
        result = fscanf(infile, "%d %d %d", &korean, &math, &english);
        if (result == -1) break;
        fgetc(infile);
        
        // 받아온 값들을 벡터 자료구조에 집어넣기
        Student s = Student(name, korean, math, english);
        students.push_back(s);
    }
    fclose(infile);
    
    while (true) {
        for(int i = 0; i < students.size(); i++) {
            students[i].printInfo();
        }
        std::string name = getString("학생 이름 : ");
        int korean = getInteger("국어 점수 : ");
        int math = getInteger("수학 점수 : ");
        int english = getInteger("영어 점수 : ");
        
        Student s = Student(name, korean, math, english); // 정적
        
        // students 자료구조 안에다가 학생 자료를 집어넣기
        students.push_back(s);
        
        FILE* outfile = fopen("students.txt", "w");
        for(int i = 0; i < students.size(); i++) {
            fprintf(outfile, "%s\n", students[i].name.c_str());
            fprintf(outfile, "%d %d %d\n", students[i].korean, students[i].math, students[i].english);
        }
        fclose(outfile);
    }
    
    return 0;
}


int getInteger(const char* prompt){
    int input;
    printf(prompt);
    fseek(stdin, 0, SEEK_END);
    scanf("%d", &input);
    return input;
}


std::string getString(const char* prompt){
    char line[100];
    printf(prompt);
    fseek(stdin, 0, SEEK_END);
    scanf("%99[^\n]", line);
    return line;
}


Student::Student(std::string name, int korean, int math, int english){
    this->name = name;
    this->korean = korean;
    this->math = math;
    this->english = english;
}


void Student::printInfo(){
    printf("이름: %s / 국어: %d / 수학: %d / 영어: %d\n", name.c_str(), korean, math, english);
}