icon안동민 개발노트

파일 기반 데이터 처리 프로그램 구현


프로젝트 개요

 이 실습에서는 학생 정보를 관리하는 파일 기반 데이터 처리 프로그램을 구현합니다. 이 프로그램을 통해 파일 입출력, 데이터 구조, 예외 처리 등 C++의 다양한 기능을 실제로 적용해볼 수 있습니다.

요구사항

  1. 학생 정보(이름, 학번, 성적) 입력 및 파일 저장
  2. 저장된 학생 정보 조회
  3. 특정 학생 정보 검색
  4. 학생 정보 수정 및 삭제
  5. 전체 학생의 평균 성적 계산

프로그램 구조

 먼저 프로그램의 기본 구조를 설계합니다.

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <stdexcept>
 
struct Student {
    std::string name;
    int id;
    double grade;
};
 
class StudentManager {
private:
    std::string filename;
    std::vector<Student> students;
 
public:
    StudentManager(const std::string& file);
    void addStudent(const Student& student);
    void saveToFile();
    void loadFromFile();
    void displayAllStudents();
    Student* findStudent(int id);
    void updateStudent(int id, const Student& newData);
    void deleteStudent(int id);
    double calculateAverageGrade();
};

주요 함수 구현

 생성자

StudentManager::StudentManager(const std::string& file) : filename(file) {
    loadFromFile();
}

 학생 추가

void StudentManager::addStudent(const Student& student) {
    students.push_back(student);
}

 파일에 데이터 저장

void StudentManager::saveToFile() {
    std::ofstream file(filename);
    if (!file) {
        throw std::runtime_error("파일을 열 수 없습니다.");
    }
    for (const auto& student : students) {
        file << student.name << "," << student.id << "," << student.grade << "\n";
    }
}

 파일에서 데이터 로드

void StudentManager::loadFromFile() {
    std::ifstream file(filename);
    if (!file) {
        return;  // 파일이 없으면 무시
    }
    students.clear();
    std::string line;
    while (std::getline(file, line)) {
        Student student;
        size_t pos = 0;
        std::string token;
        pos = line.find(",");
        student.name = line.substr(0, pos);
        line.erase(0, pos + 1);
        
        pos = line.find(",");
        student.id = std::stoi(line.substr(0, pos));
        line.erase(0, pos + 1);
        
        student.grade = std::stod(line);
        students.push_back(student);
    }
}

 모든 학생 정보 표시

void StudentManager::displayAllStudents() {
    for (const auto& student : students) {
        std::cout << "이름: " << student.name 
                  << ", 학번: " << student.id 
                  << ", 성적: " << student.grade << std::endl;
    }
}

 학생 검색

Student* StudentManager::findStudent(int id) {
    for (auto& student : students) {
        if (student.id == id) {
            return &student;
        }
    }
    return nullptr;
}

 학생 정보 수정

void StudentManager::updateStudent(int id, const Student& newData) {
    Student* student = findStudent(id);
    if (student) {
        *student = newData;
    } else {
        throw std::runtime_error("학생을 찾을 수 없습니다.");
    }
}

 학생 정보 삭제

void StudentManager::deleteStudent(int id) {
    auto it = std::remove_if(students.begin(), students.end(),
                             [id](const Student& s) { return s.id == id; });
    if (it != students.end()) {
        students.erase(it, students.end());
    } else {
        throw std::runtime_error("학생을 찾을 수 없습니다.");
    }
}

 평균 성적 계산

double StudentManager::calculateAverageGrade() {
    if (students.empty()) {
        return 0.0;
    }
    double sum = 0.0;
    for (const auto& student : students) {
        sum += student.grade;
    }
    return sum / students.size();
}

메인 프로그램

int main() {
    StudentManager manager("students.txt");
 
    try {
        // 학생 추가
        manager.addStudent({"Alice", 1001, 85.5});
        manager.addStudent({"Bob", 1002, 92.0});
        manager.addStudent({"Charlie", 1003, 78.5});
 
        // 파일에 저장
        manager.saveToFile();
 
        // 모든 학생 정보 표시
        std::cout << "모든 학생 정보:" << std::endl;
        manager.displayAllStudents();
 
        // 학생 검색
        int searchId = 1002;
        Student* found = manager.findStudent(searchId);
        if (found) {
            std::cout << "\n학번 " << searchId << "인 학생 정보:" << std::endl;
            std::cout << "이름: " << found->name << ", 성적: " << found->grade << std::endl;
        }
 
        // 학생 정보 수정
        manager.updateStudent(1001, {"Alice Updated", 1001, 88.0});
 
        // 학생 삭제
        manager.deleteStudent(1003);
 
        // 변경 사항 저장
        manager.saveToFile();
 
        // 평균 성적 계산
        std::cout << "\n평균 성적: " << manager.calculateAverageGrade() << std::endl;
 
    } catch (const std::exception& e) {
        std::cerr << "오류 발생: " << e.what() << std::endl;
    }
 
    return 0;
}

에러 처리

 프로그램 전체에 걸쳐 try-catch 블록을 사용하여 예외를 처리합니다. 파일 입출력, 데이터 변환, 학생 검색 등에서 발생할 수 있는 예외를 고려해야 합니다.

성능 최적화

  1. 대용량 데이터 처리를 위해 버퍼링 기법을 적용할 수 있습니다.
  2. 파일 입출력 횟수를 최소화하기 위해 변경 사항을 메모리에 누적했다가 한 번에 저장할 수 있습니다.
  3. 학생 검색 성능 향상을 위해 std::map이나 std::unordered_map을 사용할 수 있습니다.

확장 기능

  1. 정렬 기능 : 이름, 학번, 성적 기준으로 학생 목록을 정렬합니다.
  2. 통계 기능 : 최고/최저 성적, 표준편차 등을 계산합니다.
  3. 백업 및 복원 기능 : 데이터의 안전한 보관을 위한 기능을 추가합니다.

연습 문제

  1. 현재 프로그램을 확장하여 과목별 성적을 관리할 수 있도록 수정하세요.
  2. 간단한 명령줄 인터페이스를 구현하여 사용자가 대화식으로 프로그램을 조작할 수 있게 만드세요.
  3. 학생 정보의 유효성을 검사하는 기능을 추가하세요. (예 : 학번 중복 체크, 성적 범위 확인 등)


참고 자료

  • "Effective C++" by Scott Meyers (항목 23 : 멤버 함수보다는 비멤버 비프렌드 함수를 선호하자)
  • "C++ Primer" by Stanley B. Lippman, Josée Lajoie, and Barbara E. Moo (Chapter 8 : The IO Library)
  • C++ Reference - File I/O : File I/O in C++
  • C++ Core Guidelines : C++ Core Guidelines
  • "Design Patterns : Elements of Reusable Object-Oriented Software" by Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides