C++ Creating excel part 1.
1.C++ Creating excel.
본격적으로 Excel 을 구현하기에 앞서서, 몇 가지 자료 구조를 만들 것이다. 자료구조는 컴퓨터에서 데이터를 저장하는 방식이라 할 수 있는데,
그 구현에 따라서 장단점이 각각 있다. => 장점만을 가진 자료 구조는 없다. Excel 프로그램에서 사용할 자료구조는 크게 Vector 와 Stack.
참고로 이들은 수식을 분석하기 위해, 즉 ExprCell 객체의 to_numeric 함수 내에서 사용될 예정이다. 각 자료구조들은 다음과 같은 특징을 가지고 있다. 벡터 (Vector) : 수학의 그 벡터와는 살짝 다른 느낌인데, 배열의 크기를 맘대로 조절할 수 있는 가변길이 배열이라 보면 된다.
즉, 배열 처럼 [] 연산자로 임의의 위치에 있는 원소에 마음대로 접근할 수 있고 또 임의의 위치에 원소를 추가하거나 뺄 수 있다.
벡터를 만드는 방법은 이전에 문자열 클래스를 만들 때와 거의 비슷하다. 문자열 역시 char 데이터를 담는 가변 길이 배열과 마찬가지다. 스택 (Stack) : 벡터와는 다르게 임의의 위치에 있는 원소에 접근할 수 없고 항상 최 상단에 있는 데이터에만 접근할 수 있다.
그리고 새로운 데이터를 추가하면 최상단에 오게 된다. 쉽게 말해서 설거지 용으로 쌓아 놓는 접시들이라 보면 된다.
새로운 설거지 거리가 오면 쌓여 있는 접시 맨 위에 오게 되고(push). 설거지를 위해서 접시를 뺄 때 맨 위의 접시 부터 뺀다.(pop). 물론 스택은 그냥 벡터를 활용해서 만들 수 있다. 하지만 이는 마치 소 잡는 칼을 닭 잡는 데 쓰는 것이라고나 할까.
보통 스택의 경우 링크드 리스트를 이용해서 구현을 한다.
스택은 임의의 위치에 데이터에 접근할 필요가 없습니다.
단순히 최상위에 뭐가 있을 지 궁금하고 또 거기에 새로운 것을 추가하던지 빼기만 하면 된다.
2.C++ Vector
class Vector {
string* data;
int capacity;
int length;
public:
// 생성자
Vector(int n = 1);
// 맨 뒤에 새로운 원소를 추가한다.
void push_back(string s);
// 임의의 위치의 원소에 접근한다.
string operator[] (int i);
// x 번째 위치한 원소를 제거한다.
void remove(int x);
// 현재 벡터의 크기를 구한다.
int size();
~Vector();
};
string* data;
int capacity;
int length;
// 생성자
Vector(int n = 1);
Vector::Vector(int n) : data(new string[n]), capacity(n), length(0) {
}
struct Node {
Node* prev;
string s;
Node(Node *prev, string s) : prev(prev), s(s) { }
};
#pragma once
#ifndef A
#define A
// 헤더파일 내용
#endif
class Cell
{
protected:
int x, y;
Table* table;
string data;
public:
virtual string stringify();
virtual int to_numeric();
Cell(string data, int x, int y, Table* table);
};
Cell::Cell(string data, int x, int y, Table* table) : data(data), x(x), y(y), table(table) { }
string Cell::stringify() {
return data;
}
int Cell::to_numeric() {
return 0;
}
class Table
{
protected:
// 행 및 열의 최대 크기
int max_row_size, max_col_size;
// 데이터를 보관하는 테이블
// Cell* 을 보관하는 2차원 배열이라 생각하면 편하다
Cell*** data_table;
public:
Table(int max_row_size, int max_col_size);
~Table();
// 새로운 셀을 row 행 col 열에 등록한다.
void reg_cell(Cell* c, int row, int col);
// 해당 셀의 정수값을 반환한다.
// s : 셀 이름 (Ex. A3, B6 과 같이)
int to_numeric(const string& s);
// 행 및 열 번호로 셀을 호출한다.
int to_numeric(int row, int col);
// 해당 셀의 문자열을 반환한다.
string stringify(const string& s);
string stringify(int row, int col);
virtual string print_table() = 0;
};
Table::Table(int max_row_size, int max_col_size) :
max_row_size(max_row_size), max_col_size(max_col_size)
{
data_table = new Cell**[max_row_size];
for (int i = 0; i < max_row_size; i++) {
data_table[i] = new Cell*[max_col_size];
for (int j = 0; j < max_col_size; j++) {
data_table[i][j] = NULL;
}
}
}
Table::~Table()
{
for (int i = 0; i < max_row_size; i++) {
for (int j = 0; j < max_col_size; j++) {
if (data_table[i][j]) delete data_table[i][j];
}
}
for (int i = 0; i < max_row_size; i++) {
delete[] data_table[i];
}
delete[] data_table;
}
void Table::reg_cell(Cell* c, int row, int col) {
if (!(row < max_row_size && col < max_col_size)) return;
if (data_table[row][col]) {
delete data_table[row][col];
}
data_table[row][col] = c;
}
int Table::to_numeric(const string& s) {
// Cell 이름으로 받는다.
int row = s[0] - 'A';
int col = atoi(s.c_str() + 1) - 1;
if (row < max_row_size && col < max_col_size) {
if (data_table[row][col]) {
return data_table[row][col]->to_numeric();
}
}
return 0;
}
int Table::to_numeric(int row, int col) {
if (row < max_row_size && col < max_col_size && data_table[row][col]) {
return data_table[row][col]->to_numeric();
}
return 0;
}
string Table::stringify(const string& s) {
// Cell 이름으로 받는다.
int col = s[0] - 'A';
int row = atoi(s.c_str() + 1) - 1;
if (row < max_row_size && col < max_col_size) {
if (data_table[row][col]) {
return data_table[row][col]->stringify();
}
}
return 0;
}
string Table::stringify(int row, int col) {
if (row < max_row_size && col < max_col_size && data_table[row][col]) {
return data_table[row][col]->stringify();
}
return "";
}
ostream& operator<< (ostream& o, Table& table) {
o << table.print_table();
return o;
}
class TxtTable : public Table
{
string repeat_char(int n, char c);
// 숫자로 된 열 번호를 A, B, .... Z, AA, AB, ... 이런 순으로 매겨준다.
string col_num_to_str(int n);
public:
TxtTable(int row, int col);
// 텍스트로 표를 깨끗하게 출력해준다.
string print_table();
};
TxtTable::TxtTable(int row, int col) : Table(row, col) {}
// 텍스트로 표를 깨끗하게 출력해준다.
string TxtTable::print_table()
{
string total_table;
int* col_max_wide = new int[max_col_size];
for (int i = 0; i < max_col_size; i++) {
unsigned int max_wide = 2;
for (int j = 0; j < max_row_size; j++) {
if (data_table[j][i] && data_table[j][i]->stringify().length() > max_wide) {
max_wide = data_table[j][i]->stringify().length();
}
}
col_max_wide[i] = max_wide;
}
// 맨 상단에 열 정보 표시
total_table += " ";
int total_wide = 4;
for (int i = 0; i < max_col_size; i++) {
if (col_max_wide[i]) {
int max_len = max(2, col_max_wide[i]);
total_table += " | " + col_num_to_str(i);
total_table += repeat_char(max_len - col_num_to_str(i).length(), ' ');
total_wide += (max_len + 3);
}
}
total_table += "\n";
// 일단 기본적으로 최대 9999 번째 행 까지 지원한다고 생각한다.
for (int i = 0; i < max_row_size; i++) {
total_table += repeat_char(total_wide, '-');
total_table += "\n" + to_string(i + 1);
total_table += repeat_char(4 - to_string(i + 1).length(), ' ');
for (int j = 0; j < max_col_size; j++) {
if (col_max_wide[j]) {
int max_len = max(2, col_max_wide[j]);
string s = "";
if (data_table[i][j]) {
s = data_table[i][j]->stringify();
}
total_table += " | " + s;
total_table += repeat_char(max_len - s.length(), ' ');
}
}
total_table += "\n";
}
return total_table;
}
string TxtTable::repeat_char(int n, char c)
{
string s = "";
for (int i = 0; i < n; i++)
s.push_back(c);
return s;
}
// 숫자로 된 열 번호를 A, B, .... Z, AA, AB, ... 이런 순으로 매겨준다.
string TxtTable::col_num_to_str(int n)
{
string s = "";
if (n < 26) { s.push_back('A' + n); }
else {
char first = 'A' + n / 26 - 1;
char second = 'A' + n % 26;
s.push_back(first);
s.push_back(second);
}
return s;
}
repeat_char 과 col_num_to_str 함수는 단순히 print_table 에서 사용할 부가적인 함수들이다. print_table 함수는 각 열의 최대 문자열 길이를 계산한 뒤에, 이를 바탕으로 각 열의 폭을 결정해서 표를 출력해준다. 참고로 이 구현 방식에서 한 가지 중요한 것이 빠졌는데, 셀의 문자열 데이터에서 개행 문자가 있는 경우(즉 특정 셀이 여러 줄이 될 때)를 고려하지 않았다. 즉, 모든 셀은 최대 1 줄로만 그려지게 된다.
int main()
{
TxtTable table(5, 5);
ofstream out("test.txt");
table.reg_cell(new Cell("Hello~", 0, 0, &table), 0, 0);
table.reg_cell(new Cell("C++", 0, 1, &table), 0, 1);
table.reg_cell(new Cell("Programming", 1, 1, &table), 1, 1);
cout << endl << table;
out << table;
}
또한 test.txt.
'#Programming Language > C++' 카테고리의 다른 글
C++ Template. (0) | 2018.04.02 |
---|---|
C++ Creating excel part 2. (0) | 2018.04.02 |
C++ I / O in C++ (0) | 2018.04.01 |
C++ Virtual functions and inheritance. (0) | 2018.04.01 |
C++ Virtual function. (0) | 2018.04.01 |