C++におけるファイルI/Oとデータ検証の実装パターン

データ処理モジュールの設計

コンテスト参加者データを処理するシステムをC++で実装する際の核心技術について解説する。以下に主要コンポーネントの実装例を示す。

データモデルの定義

#pragma once
#include <iomanip>
#include <string>

struct Participant {
    long   student_id;
    std::string full_name;
    std::string department;
    int    problem_count;
    int    penalty_time;
};

std::ostream& operator<<(std::ostream& os, const Participant& p) {
    os << std::left
       << std::setw(12) << p.student_id
       << std::setw(18) << p.full_name
       << std::setw(20) << p.department
       << std::setw(8)  << p.problem_count
       << std::setw(8)  << p.penalty_time;
    return os;
}

std::istream& operator>>(std::istream& is, Participant& p) {
    is >> p.student_id >> p.full_name 
       >> p.department >> p.problem_count >> p.penalty_time;
    return is;
}

ファイル操作の実装

#include <fstream>
#include <sstream>
#include <vector>
#include "participant.h"

std::vector<Participant> load_data(const std::string& path) {
    std::ifstream input(path);
    if (!input) throw std::runtime_error("ファイルオープン失敗: " + path);

    std::string header;
    std::getline(input, header);
    
    std::vector<Participant> records;
    std::string line;
    int line_number = 2;

    while (std::getline(input, line)) {
        std::istringstream line_stream(line);
        int sequence;
        Participant entry;
        
        if (line_stream >> sequence >> entry) {
            records.push_back(entry);
        } else {
            std::cerr << "警告: 行 " << line_number 
                      << " のフォーマット不正、スキップします\n";
        }
        line_number++;
    }
    return records;
}

データ検証の強化手法

不正データに対する堅牢性を向上させるため、入力ストリームの状態フラグを明示的に管理する。特に成績データの検証では、数値範囲のチェックを追加する。

std::istream& operator>>(std::istream& is, Learner& l) {
    if (!(is >> l.student_id >> l.full_name 
           >> l.department >> l.score)) {
        is.setstate(std::ios::failbit);
        return is;
    }

    if (l.score < 0 || l.score > 100) {
        is.setstate(std::ios::failbit);
    }
    return is;
}

ストリームポリモーフィズムの応用

std::ostreamを基底クラスとして、コンソール出力とファイル出力を同一インターフェースで処理可能にする。派生クラスstd::ofstreamは暗黙的に基底クラス参照に変換され、汎用出力関数で利用できる。

void export_data(std::ostream& output, 
                const std::vector<Participant>& data) {
    for (const auto& item : data) {
        output << item << '\n';
    }
}

// 使用例
export_data(std::cout, participant_list);  // コンソール出力
std::ofstream file("results.txt");
export_data(file, participant_list);       // ファイル出力

例外処理の設計

ファイル操作失敗時の例外処理は、アプリケーション層で一元管理する。具体的にはtry-catchブロック内でエラーメッセージを標準エラー出力に転送し、後続処理を中止する。

try {
    auto participants = load_data("input.dat");
    sort_by_solutions(participants);
    export_data(std::cout, participants);
} catch (const std::exception& e) {
    std::cerr << "実行エラー: " << e.what() << std::endl;
}

ソートアルゴリズムの実装

ACMルールに基づくソートでは、解題数の降順とペナルティ時間の昇順を組み合わせる。ラムダ式を使用して比較関数を定義し、std::sortと統合する。

void sort_by_solutions(std::vector<Participant>& list) {
    std::sort(list.begin(), list.end(), 
        [](const Participant& a, const Participant& b) {
            if (a.problem_count != b.problem_count) {
                return a.problem_count > b.problem_count;
            }
            return a.penalty_time < b.penalty_time;
        }
    );
}

タグ: C++ STL file-stream data-validation exception-handling

7月28日 21:19 投稿