タスク1:コンテスト参加者データの処理
このタスクでは、コンテスト参加者の情報を含むファイルを読み込み、特定のルールでソートし、結果を表示および保存するプログラムを作成します。
1. ソースコード
(1) participant.hpp
#pragma once
#include <iomanip>
#include <iosfwd>
#include <string>
struct Participant {
long student_id; // 学籍番号
std::string participant_name; // 参加者名
std::string field_of_study; // 専門分野
int problems_solved; // 解いた問題数
int total_penalty; // 罰則時間
};
// 出力ストリーム演算子のオーバーロード
inline std::ostream& operator<<(std::ostream& out, const Participant& p) {
out << std::left;
out << std::setw(15) << p.student_id
<< std::setw(15) << p.participant_name
<< std::setw(15) << p.field_of_study
<< std::setw(10) << p.problems_solved
<< std::setw(10) << p.total_penalty;
return out;
}
// 入力ストリーム演算子のオーバーロード
inline std::istream& operator>>(std::istream& in, Participant& p) {
in >> p.student_id >> p.participant_name >> p.field_of_study >> p.problems_solved >> p.total_penalty;
return in;
}
(2) utilities.hpp
#pragma once
#include <fstream>
#include <stdexcept>
#include <string>
#include <vector>
#include "participant.hpp"
// ACMコンテストのソートルール:解いた問題数の降順、罰則時間の昇順
inline bool compare_participants(const Participant& a, const Participant& b) {
if (a.problems_solved != b.problems_solved) {
return a.problems_solved > b.problems_solved;
}
return a.total_penalty < b.total_penalty;
}
// コンテナの内容を任意の出力ストリームに書き込む
inline void output_to_stream(std::ostream& os, const std::vector<Participant>& v) {
for (const auto& p : v) {
os << p << '
';
}
}
// コンテナの内容を標準出力に表示する
inline void display_to_console(const std::vector<Participant>& v) {
output_to_stream(std::cout, v);
}
// コンテナの内容をファイルに保存する
inline void save_to_file(const std::string& filename, const std::vector<Participant>& v) {
std::ofstream os(filename);
if (!os) {
throw std::runtime_error("ファイル " + filename + " を開けません");
}
output_to_stream(os, v);
}
// ファイルからデータを読み込み、タイトル行をスキップする
inline std::vector<Participant> read_from_file(const std::string& filename) {
std::ifstream is(filename);
if (!is) {
throw std::runtime_error("ファイル " + filename + " を開けません");
}
std::string header_line;
std::getline(is, header_line); // タイトル行をスキップ
std::vector<Participant> participants;
Participant temp;
int sequence_number;
while (is >> sequence_number >> temp) {
participants.push_back(temp);
}
return participants;
}
(3) task1.cpp
#include <algorithm>
#include <iostream>
#include <stdexcept>
#include <vector>
#include "participant.hpp"
#include "utilities.hpp"
const std::string input_filename = "./input_data.txt";
const std::string output_filename = "./sorted_results.txt";
void application() {
std::vector<Participant> participants;
try {
participants = read_from_file(input_filename);
std::sort(participants.begin(), participants.end(), compare_participants);
display_to_console(participants);
save_to_file(output_filename, participants);
} catch (const std::exception& e) {
std::cerr << "エラー: " << e.what() << '
';
return;
}
}
int main() {
application();
return 0;
}
2. 実験テストコードの実行結果
入力ファイル input_data.txt と出力ファイル sorted_results.txt の内容を確認し、期待通りの結果が得られることを確認します。
タスク2:学生データの管理
このタスクでは、学生の情報を管理するクラスを作成し、ファイルからデータを読み込み、特定のルールでソートし、結果を表示および保存するプログラムを作成します。
1. ソースコード
(1) student.hpp
#pragma once
#include <iosfwd>
#include <string>
class Student {
public:
Student() = default;
~Student() = default;
const std::string get_major() const;
int get_score() const;
friend std::ostream& operator<<(std::ostream& os, const Student& s);
friend std::istream& operator>>(std::istream& is, Student& s);
private:
int student_id;
std::string student_name;
std::string major;
int score; // 0-100
};
(2) student.cpp
#include "student.hpp"
#include <iomanip>
const std::string Student::get_major() const {
return major;
}
int Student::get_score() const {
return score;
}
std::ostream& operator<<(std::ostream& os, const Student& s) {
os << std::left;
os << std::setw(10) << s.student_id
<< std::setw(10) << s.student_name
<< std::setw(10) << s.major
<< std::setw(10) << s.score;
return os;
}
std::istream& operator>>(std::istream& is, Student& s) {
is >> s.student_id >> s.student_name >> s.major >> s.score;
return is;
}
(3) student_manager.hpp
#pragma once
#include <string>
#include <vector>
#include "student.hpp"
class StudentManager {
public:
void load_data(const std::string& file); // データファイルをロードする(スペース区切り)
void sort_students(); // ソート:専門分野の辞書順昇順、同専門のスコア降順
void display() const; // 画面に表示する
void save_data(const std::string& file) const; // ファイルに保存する
private:
void write_to_stream(std::ostream& os) const; // 任意の出力ストリームにデータを書き込む
private:
std::vector<Student> students;
};
(4) student_manager.cpp
#include "student_manager.hpp"
#include <fstream>
#include <stdexcept>
#include <sstream>
#include <iostream>
#include <algorithm>
void StudentManager::load_data(const std::string& file) {
std::ifstream is(file);
if (!is) {
throw std::runtime_error("ファイル " + file + " を開けません");
}
std::string line;
std::getline(is, line); // タイトル行をスキップ
int line_number = 1;
while (std::getline(is, line)) {
std::istringstream iss(line);
Student s;
if (iss >> s) {
if (s.get_score() < 0 || s.get_score() > 100) {
std::cerr << "[警告] 行 " << line_number << " の形式が不正です。スキップしました: " << s << std::endl;
} else {
students.push_back(s);
}
} else {
std::cerr << "[警告] 行 " << line_number << " のスコアが無効です。スキップしました: " << s << std::endl;
}
++line_number;
}
}
void StudentManager::sort_students() {
if (students.empty()) {
std::cerr << "(空です)" << std::endl;
return;
}
std::sort(students.begin(), students.end(),
[](const Student& a, const Student& b) {
if (a.get_major() != b.get_major()) {
return a.get_major() < b.get_major();
}
return a.get_score() > b.get_score();
});
}
void StudentManager::display() const {
write_to_stream(std::cout);
}
void StudentManager::save_data(const std::string& file) const {
std::ofstream os(file);
if (!os) {
throw std::runtime_error("ファイル " + file + " を開けません");
}
write_to_stream(os);
}
void StudentManager::write_to_stream(std::ostream& os) const {
for (const Student& s : students) {
os << s << '
';
}
}
(5) task2.cpp
#include <iostream>
#include <limits>
#include <string>
#include "student_manager.hpp"
const std::string input_file = "./student_data.txt";
const std::string output_file = "./sorted_students.txt";
void show_menu() {
std::cout << "
********** 簡易アプリケーション **********
"
"1. ファイルをロード
"
"2. ソート
"
"3. 画面に表示
"
"4. ファイルに保存
"
"5. 終了
"
"選択してください: ";
}
void application() {
StudentManager manager;
while (true) {
show_menu();
int choice;
std::cin >> choice;
try {
switch (choice) {
case 1:
manager.load_data(input_file);
std::cout << "ロード成功
";
break;
case 2:
manager.sort_students();
std::cout << "ソート完了
";
break;
case 3:
manager.display();
std::cout << "表示完了
";
break;
case 4:
manager.save_data(output_file);
std::cout << "保存成功
";
break;
case 5:
return;
default:
std::cout << "無効な入力
";
}
} catch (const std::exception& e) {
std::cout << "エラー: " << e.what() << '
';
}
}
}
int main() {
application();
return 0;
}
2. テストコードの実行結果
入力ファイル student_data.txt と出力ファイル sorted_students.txt の内容を確認し、期待通りの結果が得られることを確認します。
実験のまとめ
ファイルストリームを利用することで、ファイルと標準出力間のデータのやり取りが可能になります。しかし、ファイル操作にはエラーが伴う可能性があるため、例外処理(throwとtry-catch)を用いて、プログラムの継続的な実行を妨げずにエラーを適切に処理する必要があります。