파일처리 PA03

학번 : 20021558	성명 : 경규성

◎ 개요: 키정렬 (Keysorting)

입력 파일은 교과목에 대한 정보로 구성되어 있다. 입력 파일을 읽어 교과목에 대한 레코드를 설계한 후 이진 파일로 저장하여 데이터 파일을 생성한다. 데이터 파일에서 교과목 레코드를 읽어 레코드에 대한 인덱스를 만든 후 키정렬을 사용하여 데이터 파일을 출력한다.

◎ 코딩환경

Windows XP, Visual Studio 2005

◎ 실행조건

소스파일이 있는 폴더에 student.bin 파일 있고,

(에디터)Visual Studio 의 프로세스->옵션->디버그->매개변수에 course.txt 가 추가되어있어야함.

Visual Studio 2005 경우 프로젝트-> (프로젝트이름)속성->구성속성->디버깅->명령인수 에 추가

1. 소스코드

#include <iostream>

#include <fstream>

#include <string>

using namespace std;

const int MAXSIZE = 20;

const char delimiter = '|'; // 구분문자

class CourseRecord

{

private: // 레코드정의

int cno; //Course Number

int year; //Course Year

string cterm; // Course Seson

string cname; // Course Name

string professor; //Course Professor

public:

CourseRecord() {;}

CourseRecord(int no, int ye, string ct, string cn, string pr)

{cno=no, year=ye, cterm=ct, cname=cn, professor=pr;}

// 외부에서 레코드 설정시

void setCno(int no) {cno = no;}

void setYear(int ye) {year = ye;}

void setCterm(string ct) {cterm=ct;}

void setCname(string cn) {cname=cn;}

void setProfessor(string pr) {professor=pr;}

// 외부에서 레코드 접근시

int getCno() const {return cno;}

int getYear() const {return year;}

string getCterm() const {return cterm;}

string getCname() const {return cname;}

string getProfessor() const {return professor;}

void readFromFile(ifstream&);	// 파일에서 읽기

void writeToFile(ofstream&);	// 파일에 쓰기

void printRecord(ostream&);	// 화면출력

//int size();

};

void CourseRecord::writeToFile(ofstream& BinOut)// 이진파일에 쓰기

{

BinOut.write((char*) &cno, sizeof(int));	// 필드하나 쓰고

BinOut.write((char*) &delimiter, sizeof(char)); // 구분문자하나 쓴다

BinOut.write((char*) &year, sizeof(int));

BinOut.write((char*) &delimiter, sizeof(char));

int tlen = (int)cterm.length();	// 문자열 길이 미리 구하고

BinOut.write(cterm.c_str(), (std::streamsize)tlen);	// 문자열은 길이만큼 쓴다.

BinOut.write((char*) &delimiter, sizeof(char));

int nlen = (int)cname.length();

BinOut.write(cname.c_str(), (std::streamsize)nlen);

BinOut.write((char*) &delimiter, sizeof(char));

int plen = (int)professor.length();

BinOut.write(professor.c_str(), (std::streamsize)plen);

BinOut.write((char*) &delimiter, sizeof(char));

}

void CourseRecord::readFromFile(ifstream& Bin) // 이진파일 읽기

{

//int tlen, nlen, plen;

Bin.read((char*) &cno, sizeof(int));	// 필드하나읽고

Bin.seekg(1, ios::cur);	// 파일포인터 하나증가

Bin.read((char*) &year, sizeof(int));	// 필드하나읽고

Bin.seekg(1, ios::cur);	// 파일포인터 하나증가

//Bin.read((char*) &tlen, sizeof(int));

getline(Bin, cterm, delimiter);	// 구분문자를 만날때까지 한 줄 읽기

//Bin.read((char*) &nlen, sizeof(int));

getline(Bin, cname, delimiter);	// 구분문자를 만날때까지 한 줄 읽기

//Bin.read((char*) &plen, sizeof(int));

getline(Bin, professor, delimiter);	// 구분문자를 만날때까지 한 줄 읽기

}

void CourseRecord::printRecord(ostream&)// 화면에 출력

{

cout << cno <<'\n'<< year <<'\n'<< cterm <<'\n'<< cname <<'\n'<< professor << endl << endl;

}

class KeyIndex

{

private: // 키필드설정

unsigned short int courseNumber; // 14****

unsigned char year; // 1990->0, 1991->1, … 2050->60  (0~60)

unsigned char term; // Spring->0, Summer->1, Fall->2, Winter->3

int offset; //address

public:

KeyIndex() {;}

KeyIndex(unsigned short int cnum, unsigned char y, unsigned char t, int o)

{courseNumber = cnum, year = y, term = t, offset = o;}

// 키필드 설정하기

void Set(unsigned short int cnum, unsigned char y, unsigned char t, int o)

{courseNumber = cnum, year = y, term = t, offset = o;}

// 외부에서 키필드 접근함수

int getIndexCn() const {return courseNumber;}

int getIndexYe() const {return year;}

int getIndexCt() const {return term;}

int getOffset() const {return offset;}

};

// 정렬 템플릿

template <class Type>

class Sort

{

private:

void exchange(Type &a, Type &b);

public:

void bubble(KeyIndex *data, int n);

};

template <class Type> //버블소트로 인덱스정렬

void Sort<Type>::bubble(KeyIndex *data, int n)

{

int i=0, j=0;

for(i=0; i<n; i++)

for(j=0; j<n; j++)

{

if(data[i].getIndexCn() < data[j].getIndexCn())	// 과정번호 비교

exchange(data[i], data[j]);	// i < j 이면 교환

else if(data[i].getIndexCn() == data[j].getIndexCn())	// 과정번호 같으면

{

if(data[i].getIndexYe() < data[j].getIndexYe())	// 과정년도 비교

exchange(data[i], data[j]);	// i < j 이면 교환

else if(data[i].getIndexYe() == data[j].getIndexYe())	// 과정년도 같으면

{

if(data[i].getIndexCt() < data[j].getIndexCt())	// 과정계절 비교

exchange(data[i], data[j]);	// i < j 이면 교환

}

}

}

}

template <class Type> // 교환함수

void Sort<Type>::exchange(Type &a, Type &b)

{

Type c;

c=a;

a=b;

b=c;

}

int main(int argc, char* argv[])

{

CourseRecord S[MAXSIZE], R[MAXSIZE];

int cno; //Course Number

int year; //Course Year

string cterm; // Course Seson

string cname; // Course Name

string professor; //Course Professor

ifstream textin(argv[1], ios::skipws);

int i=0;

// student 파일에서 레코드 읽어 배열에 저장

while (textin.good() && textin.peek() != EOF)

{

// 과정번호와 과정년도는 int 이므로 >> 연산자로 받았다.

textin >> cno >> year;

textin.ignore(300, '\n');

S[i].setCno(cno);

S[i].setYear(year);

getline(textin, cterm);	// 한 줄씩 받기

S[i].setCterm(cterm);	// 받은 레코드를 배열에 저장

getline(textin, cname);

S[i].setCname(cname);

getline(textin, professor);

S[i].setProfessor(professor);

i++;

}

if(!textin.is_open())

{

cout << "파일을 열수 없습니다. " << endl;

return 0;

}

textin.close();

cout << "---------텍스트파일 내용을 출력--------" << endl;

for(int k=0; k<i; k++)

{

S[k].printRecord(cout);

}

KeyIndex *K = new KeyIndex[i]; // 인덱스정렬 할 배열

int t;

ofstream binout("binout.bin", ios::binary);

// 이진파일에 쓰면서 인덱스 배열 생성

for(int j=0; j<i; j++)

{

if(S[j].getCterm() == "spring")

t=0;

else if(S[j].getCterm() == "summer")

t=1;

else if(S[j].getCterm() == "fall")

t=2;

else if(S[j].getCterm() == "winter")

t=3;

// 과정번호는 14****,

// 과정년도는 1990->0, 1991->1, … 2050->60  (0~60)

// 과정계절은 Spring->0, Summer->1, Fall->2, Winter->3

K[j].Set(S[j].getCno()-140000,S[j].getYear()-1990, t, binout.tellp());

S[j].writeToFile(binout);

}

binout.close();

ifstream binin("binout.bin", ios::binary);

cout << "---------이진파일로 바꾼내용을 출력--------" << endl;

for(int k=0; k<i; k++)

{

R[k].readFromFile(binin);

R[k].printRecord(cout);

}

binin.close(); // 이진파일 출력

Sort<KeyIndex> sort;

sort.bubble(K, i); // 인덱스배열 [K]를 키필드에 따라 정렬

ifstream bintest("binout.bin", ios::binary);

CourseRecord *TEST = new CourseRecord; // 임시 레코드

cout << "정렬된 인덱스배열을 이용하여 파일의 레코드 출력" << endl;

for(int k=0; k<i; k++)

{

// 파일의 처음에서 인덱스 배열의 오프셋 만큼 이동

bintest.seekg(K[k].getOffset(), ios::beg);

// 파일에서 오프셋에 있는 레코드를 읽어서

TEST->readFromFile(bintest);

// 출력

TEST->printRecord(cout);

}

bintest.close();

//delete TEST;

delete K;

return 0;

}

2. 화면출력

( 출력이 너무 길어서 “정렬된 인덱스배열을 이용하여 파일의 레코드 출력” 부분만 캡쳐를 잘랐음.

캡쳐 윗 부분의 텍스트 파일과 이진파일의 시험출력은 생략 )