#include "key.h" #include #include #include #include #define MAX_COLUMN 20 #define MAX_ROW 20 #define MAX_COLUMN_WIDTH 10 #define MAX_DATA_LENGTH 100 #define MAX_TICK 200 #define MAX_MESSAGE_LENGTH 100 #define FUNCTION_NUM 8 int tick = 0; int current_x = 0; int current_y = 0; int alive = 1; int select_mode = 0; int select_entry = 0; int input_mode = 0; char functions[FUNCTION_NUM][100] = {"Save", "Load", "Sum", "Average", "Max", "Min", "Sort", "Exit"}; char *message = "Hi~"; typedef struct { int current_width; int current_height; int column_width[MAX_COLUMN]; char data[MAX_ROW][MAX_COLUMN][MAX_DATA_LENGTH]; } TableInfo; int max(int a, int b) { return a > b ? a : b; } int min(int a, int b) { return a < b ? a : b; } int int_length(int num) { if (num == 0) return 1; int length = 0; while (num) { num /= 10; length++; } return length; } int is_string_all_number(char *str) { int length = strlen(str); for (int i = 0; i < length; i++) { if (str[i] < '0' || str[i] > '9') { return 0; } } return 1; } int is_index_all_number(TableInfo *table, int type, int index) { if (type == 0) { // 行 index = index - 1; for (int i = 0; i < table->current_width; i++) { if (strlen(table->data[index][i]) > 0) { if (!is_string_all_number(table->data[index][i])) { return 0; } } } } else { // 列 index = index - 'A'; for (int i = 0; i < table->current_height; i++) { if (strlen(table->data[i][index]) > 0) { if (!is_string_all_number(table->data[i][index])) { return 0; } } } } return 1; } // 交换两列 void changeTwoColumn(TableInfo *table, int column1, int column2) { // 验证两列是否合法且不等 if (column1 == column2 || column1 < 0 || column2 < 0 || column1 >= table->current_width || column2 >= table->current_width) { return; } char tmp[MAX_DATA_LENGTH]; for (int i = 0; i < table->current_height; i++) { strcpy(tmp, table->data[i][column1]); strcpy(table->data[i][column1], table->data[i][column2]); strcpy(table->data[i][column2], tmp); } } // 交换两行 void changeTwoRow(TableInfo *table, int row1, int row2) { // 验证两行是否合法且不等 if (row1 == row2 || row1 < 0 || row2 < 0 || row1 >= table->current_height || row2 >= table->current_height) { return; } char tmp[MAX_DATA_LENGTH]; for (int i = 0; i < table->current_width; i++) { strcpy(tmp, table->data[row1][i]); strcpy(table->data[row1][i], table->data[row2][i]); strcpy(table->data[row2][i], tmp); } } int str_cmp(char *str1, char *str2, int case_sensitive) { if (case_sensitive) { return strcmp(str1, str2); } else { char *tmp_str1 = (char *)malloc(sizeof(char) * strlen(str1)); char *tmp_str2 = (char *)malloc(sizeof(char) * strlen(str2)); strcpy(tmp_str1, str1); strcpy(tmp_str2, str2); for (int i = 0; i < strlen(tmp_str1); i++) { if (tmp_str1[i] >= 'A' && tmp_str1[i] <= 'Z') { tmp_str1[i] += 32; } } for (int i = 0; i < strlen(tmp_str2); i++) { if (tmp_str2[i] >= 'A' && tmp_str2[i] <= 'Z') { tmp_str2[i] += 32; } } int result = strcmp(tmp_str1, tmp_str2); free(tmp_str1); free(tmp_str2); return result; } } int check_index_valid(TableInfo *table, char *index) { // 合法的 index 应该是 A1,A23 这样的格式 int length = strlen(index); if (length == 0) { return 0; } if (index[0] >= 'A' && index[0] <= 'A' + table->current_width - 1) { // 列号合法 int row = 0; for (int i = 1; i < length; i++) { if (index[i] < '0' || index[i] > '9') { return 0; } row = row * 10 + index[i] - '0'; } if (row >= 1 && row <= table->current_height) { return 1; } } return 0; } // 传入实际的 index int check_valid_formatter(TableInfo *table, int x, int y) { // 合法的公示应该以 = 开头,并且只包含 + - 和 char *data = malloc(sizeof(char) * MAX_DATA_LENGTH); strcpy(data, table->data[y][x]); if (data[0] != '=') { return 0; } int length = strlen(data); char *tmp_data = malloc(sizeof(char) * MAX_DATA_LENGTH); for (int i = 1; i < length; i++) { if (data[i] == ' ') continue; // 获取 =,-,+,\n 之间的字符串 int j = i; while (j < length && data[j] != '=' && data[j] != '-' && data[j] != '+' && data[j] != '\n') { tmp_data[j - i] = data[j]; j++; } tmp_data[j - i] = '\0'; // 判断是否是合法的 index if (tmp_data[0] >= 'A' && tmp_data[0] <= 'Z') { if (!check_index_valid(table, tmp_data)) return 0; } else { for (int k = 0; k < j - i; k++) { if (tmp_data[k] < '0' || tmp_data[k] > '9') { return 0; } } } } return 1; } // 获取是行还是列 int get_index_type(int index) { return index >> 7; } // 获取具体的值 int get_index_value(int index) { return index & 0b01111111; } // 进入选择模式,选择某行或某列 int start_select_index(int entry) { message = "Select a row or column"; current_x = 0; current_y = 1; select_mode = 1; select_entry = entry; } // 退出选择模式 void end_select_index(TableInfo *table) { // 一个 8 位数字 // 最高位: 0 代表行 1 代表列 // 低 7 位: 代表行号或列号对应字符的 ASCII 码 int select_value = 0; if (current_x == 0) { // x = 0 表示选择行 select_value = 0b00000000 | current_y; } else { // x > 0 表示选择列 select_value = 0b10000000 | (current_x + 'A' - 1); } int type = get_index_type(select_value); int value = get_index_value(select_value); int ascii_sort = 0; // 0 代表不区分大小写,1 代表区分大小写 int case_sensitive = 0; // 判断是否和数字操作有关 if (select_entry == 2 || select_entry == 3 || select_entry == 4 || select_entry == 5 || select_entry == 6) { int valid = is_index_all_number(table, type, value); if (!valid) { if (select_entry == 6) { ascii_sort = 1; message = "This row or column is not all number, use ASCII to " "sort!\n Do you want to use Case Sensitive? ([Y]/n): "; // 等待完善:选择大小写敏感 // char tmp; // input_mode = 1; // scanf("%c", &tmp); // input_mode = 0; // if (tmp == 'n' || tmp == 'N') { // case_sensitive = 0; // } else { // case_sensitive = 1; // } } else { message = "This row or column is not all number!"; return; } } } if (select_entry == 2) { // sum int sum = 0; if (type == 0) { // 行 value = value - 1; for (int i = 0; i < table->current_width; i++) { if (strlen(table->data[value][i]) > 0) { sum += atoi(table->data[value][i]); } } } else { // 列 value = value - 'A'; for (int i = 0; i < table->current_height; i++) { if (strlen(table->data[i][value]) > 0) { sum += atoi(table->data[i][value]); } } } char *tmp_message = (char *)malloc(sizeof(char) * MAX_MESSAGE_LENGTH); sprintf(tmp_message, "Sum: %d", sum); message = tmp_message; } else if (select_entry == 3) { // average int sum = 0; int count = 0; if (type == 0) { // 行 value = value - 1; for (int i = 0; i < table->current_width; i++) { if (strlen(table->data[value][i]) > 0) { sum += atoi(table->data[value][i]); count++; } } } else { // 列 value = value - 'A'; for (int i = 0; i < table->current_height; i++) { if (strlen(table->data[i][value]) > 0) { sum += atoi(table->data[i][value]); count++; } } } char *tmp_message = (char *)malloc(sizeof(char) * MAX_MESSAGE_LENGTH); sprintf(tmp_message, "Average: %f", (double)sum / count); message = tmp_message; } else if (select_entry == 4) { // max int max_ = -0x7fffffff; if (type == 0) { // 行 value = value - 1; for (int i = 0; i < table->current_width; i++) { if (strlen(table->data[value][i]) > 0) { max_ = atoi(table->data[value][i]) > max_ ? atoi(table->data[value][i]) : max_; } } } else { // 列 value = value - 'A'; for (int i = 0; i < table->current_height; i++) { if (strlen(table->data[i][value]) > 0) { max_ = atoi(table->data[i][value]) > max_ ? atoi(table->data[i][value]) : max_; } } } char *tmp_message = (char *)malloc(sizeof(char) * MAX_MESSAGE_LENGTH); sprintf(tmp_message, "Max: %d", max_); message = tmp_message; } else if (select_entry == 5) { // min int min_ = 0x7fffffff; if (type == 0) { // 行 value = value - 1; for (int i = 0; i < table->current_width; i++) { if (strlen(table->data[value][i]) > 0) { min_ = atoi(table->data[value][i]) < min_ ? atoi(table->data[value][i]) : min_; } } } else { // 列 value = value - 'A'; for (int i = 0; i < table->current_height; i++) { if (strlen(table->data[i][value]) > 0) { min_ = atoi(table->data[i][value]) < min_ ? atoi(table->data[i][value]) : min_; } } } char *tmp_message = (char *)malloc(sizeof(char) * MAX_MESSAGE_LENGTH); sprintf(tmp_message, "Min: %d", min_); message = tmp_message; } else if (select_entry == 6) { // sort if (type == 0) { // 行 value = value - 1; for (int i = 0; i < table->current_width; i++) { for (int j = i + 1; j < table->current_width; j++) { if (!ascii_sort) { if (atoi(table->data[value][i]) > atoi(table->data[value][j])) { changeTwoColumn(table, i, j); } message = "Sorted by number!"; } else { if (str_cmp(table->data[value][i], table->data[value][j], case_sensitive) > 0) { changeTwoColumn(table, i, j); } message = "Sorted by ASCII!"; } } } } else { // 列 value = value - 'A'; for (int i = 0; i < table->current_height; i++) { for (int j = i + 1; j < table->current_height; j++) { if (!ascii_sort) { if (atoi(table->data[i][value]) > atoi(table->data[j][value])) { changeTwoRow(table, i, j); } message = "Sorted by number!"; } else { if (str_cmp(table->data[i][value], table->data[j][value], case_sensitive) > 0) { changeTwoRow(table, i, j); } message = "Sorted by ASCII!"; } } } } } else { // 选择行或列 if (type == 0) { // 行 value = value - 1; char *tmp_message = (char *)malloc(sizeof(char) * MAX_MESSAGE_LENGTH); sprintf(tmp_message, "Selected row: %d", value + 1); message = tmp_message; } else { // 列 value = value - 'A'; char *tmp_message = (char *)malloc(sizeof(char) * MAX_MESSAGE_LENGTH); sprintf(tmp_message, "Selected column: %c", value + 'A'); message = tmp_message; } } // char *tmp_message = (char *)malloc(sizeof(char) * MAX_MESSAGE_LENGTH); // sprintf(tmp_message, "Selected: %d,%c", get_index_type(select_value), // get_index_value(select_value)); // message = tmp_message; select_mode = 0; current_y = -1; current_x = select_entry; } char *int_to_string(int num) { char *str = (char *)malloc(sizeof(char) * 100); sprintf(str, "%d", num); return str; } void init_table(int width, int height, int column_width, TableInfo *table) { table->current_width = width; table->current_height = height; for (int i = 0; i < width; i++) { table->column_width[i] = column_width; } for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { table->data[i][j][0] = '\0'; } } } int cal_formula(TableInfo *table, int x, int y) { char *data = malloc(sizeof(char) * MAX_DATA_LENGTH); strcpy(data, table->data[y][x]); int length = strlen(data); char *tmp_data = malloc(sizeof(char) * MAX_DATA_LENGTH); int result = 0; for (int i = 1; i < length; i++) { if (data[i] == ' ') continue; // 标注 - int minus = 0; // 获取 =,-,+,\n 之间的字符串 int j = i; while (j < length && data[j] != '=' && data[j] != '-' && data[j] != '+' && data[j] != '\n') { tmp_data[j - i] = data[j]; j++; } tmp_data[j - i] = '\0'; // 判断是否是 - if (data[i - 1] == '-') { minus = 1; } // 判断是否是合法的 index if (tmp_data[0] >= 'A' && tmp_data[0] <= 'Z') { if (!check_index_valid(table, tmp_data)) return -1; // 0 表示 列,剩下的是行 int col = tmp_data[0] - 'A'; int row = 0; for (int k = 1; k < strlen(tmp_data); k++) { row = row * 10 + tmp_data[k] - '0'; } // 判断 table->data[row - 1][col] 是否是数字 if (strlen(table->data[row - 1][col]) == 0) { return -1; } if (!is_string_all_number(table->data[row - 1][col])) { return -1; } if (minus) { result -= atoi(table->data[row - 1][col]); } else { result += atoi(table->data[row - 1][col]); } } else { for (int k = 0; k < j - i; k++) { if (tmp_data[k] < '0' || tmp_data[k] > '9') { return -1; } } if (minus) { result -= atoi(tmp_data); } else { result += atoi(tmp_data); } } i += j - i; } return result; } void printTable(TableInfo *table) { system("cls"); printf("Table v0.2 by Luthics\n"); int max_l = int_length(table->current_height) + 1; // render table for (int i = -1; i < table->current_height; i++) { for (int j = -1; j < table->current_width; j++) { printf("+"); if (j == -1) { for (int k = 0; k < max_l; k++) { printf("-"); } continue; } for (int k = 0; k < table->column_width[j]; k++) { printf("-"); } } printf("+\n"); if (i == -1) { // 输出 A-Z 的列名 for (int j = -1; j < table->current_width; j++) { if (j == -1) { printf("|"); for (int k = 0; k < max_l; k++) { printf(" "); } continue; } printf("|"); for (int k = 0; k < table->column_width[j] - 1; k++) { printf(" "); } if (select_mode) { if (current_x - 1 == j && current_y == 0) { // RED printf("\033[31m"); // BOLD printf("\033[1m"); } } printf("%c", 'A' + j); if (select_mode) { if (current_x - 1 == j && current_y == 0) { // RESET printf("\033[0m"); } } } printf("|\n"); continue; } for (int j = -1; j < table->current_width; j++) { if (j == -1) { printf("|"); int data_length = int_length(i + 1); for (int k = 0; k < max_l - data_length; k++) { printf(" "); } if (select_mode) { if (current_x == 0 && current_y == i + 1) { // RED printf("\033[31m"); // BOLD printf("\033[1m"); } } printf("%d", i + 1); if (select_mode) { if (current_x == 0 && current_y == i + 1) { // RESET printf("\033[0m"); } } continue; } printf("|"); int data_length = table->data[i][j] == NULL ? 0 : strlen(table->data[i][j]); if (data_length == 0) { if (i == current_y && j == current_x && !select_mode) { printf("_"); } else { printf(" "); } for (int k = 0; k < table->column_width[j] - 1; k++) { printf(" "); } } else { int formatter = 0; if (table->data[i][j][0] == '=') { formatter = 1; } int add_ = 0; if (select_mode) { if (current_x == j + 1 || current_y == i + 1) { // RED printf("\033[31m"); // BOLD printf("\033[1m"); } } else if (i == current_y && j == current_x) { // RED printf("\033[31m"); // BOLD printf("\033[1m"); } else if (formatter) { // BLUE printf("\033[34m"); // BOLD printf("\033[1m"); } int formatter_success = 0; if (formatter && !select_mode && !(i == current_y && j == current_x)) { // todo 先计算后渲染,修复列宽不合适的bug int data = cal_formula(table, j, i); if (data == -1) { formatter_success = 0; } else { char *tmp_data = (char *)malloc(sizeof(char) * MAX_DATA_LENGTH); sprintf(tmp_data, "%d", data); data_length = strlen(tmp_data); printf("%s", tmp_data); table->column_width[j] = max(table->column_width[j], strlen(tmp_data)); formatter_success = 1; } } if (!formatter_success) { for (int k = 0; k < data_length; k++) { printf("%c", table->data[i][j][k]); } } if (select_mode) { if (current_x == j + 1 || current_y == i + 1) { // RESET printf("\033[0m"); } } else if (i == current_y && j == current_x) { // RESET printf("\033[0m"); // 如果还有空间,就补上下划线 if (table->column_width[j] - data_length > 0) { printf("_"); add_ = 1; } } else if (formatter) { // Reset printf("\033[0m"); } if (formatter) { // 补上空格 for (int k = 0; k < table->column_width[j] - data_length; k++) { printf(" "); } } else { for (int k = 0; k < table->column_width[j] - data_length - add_; k++) { printf(" "); } } } } printf("|\n"); } for (int j = -1; j < table->current_width; j++) { if (j == -1) { printf("+"); for (int k = 0; k < max_l; k++) { printf("-"); } continue; } printf("+"); for (int k = 0; k < table->column_width[j]; k++) { printf("-"); } } printf("+\n"); // render functions for (int i = 0; i < FUNCTION_NUM; i++) { if (current_y == -1 && i == current_x) { // RED printf("\033[31m"); // BOLD printf("\033[1m"); } printf("%s", functions[i]); if (current_y == -1 && i == current_x) { // RESET printf("\033[0m"); } if (i != FUNCTION_NUM - 1) { printf(" "); } } printf("\n"); printf("%s\n", message); } int is_data_file_exist() { FILE *fp = fopen("table.dat", "r"); if (fp == NULL) { return 0; } else { fclose(fp); return 1; } } TableInfo load_table_from_file(char *filename) { TableInfo table; FILE *fp = fopen(filename, "r"); fread(&table, sizeof(TableInfo), 1, fp); fclose(fp); return table; } int save_table_to_file(char *filename, TableInfo table) { FILE *fp = fopen(filename, "w"); fwrite(&table, sizeof(TableInfo), 1, fp); fclose(fp); return 0; } void setCell(TableInfo *table, int row, int column, char *data) { if (row >= table->current_height || column >= table->current_width || row < 0 || column < 0) { return; } strcpy(table->data[row][column], data); if (strlen(data) > table->column_width[column]) { table->column_width[column] = strlen(data); } } int get_max_width_in_a_column(TableInfo *table, int column) { int max_width = 0; for (int i = 0; i < table->current_height; i++) { int data_length = table->data[i][column] == NULL ? 0 : strlen(table->data[i][column]); if (data_length > max_width) { max_width = data_length; } } return max(max_width, table->column_width[column]); } void add_char_to_cell(TableInfo *table, int row, int column, char c) { if (row >= table->current_height || column >= table->current_width || row < 0 || column < 0) { return; } int data_length = strlen(table->data[row][column]); if (data_length < MAX_DATA_LENGTH - 1) { table->data[row][column][data_length] = c; table->data[row][column][data_length + 1] = '\0'; table->column_width[column] = get_max_width_in_a_column(table, column); } } void del_char_from_cell(TableInfo *table, int row, int column) { if (row >= table->current_height || column >= table->current_width || row < 0 || column < 0) { return; } int data_length = strlen(table->data[row][column]); if (data_length > 0) { table->data[row][column][data_length - 1] = '\0'; table->column_width[column] = get_max_width_in_a_column(table, column); } } // 返回 1 代表响应成功 // 返回 0 代表响应失败 int handleKeyPress(int key, TableInfo *table) { int status = 0; if (key == 224) { // 特殊键 key = getch(); // 获取特殊键码 if (key == KEY_LEFT_ARROW) { if (select_mode) { // 选择模式,0,0是左上角,0,1 0,2 0,3 代表每行行号 1,0 2,0 3,0 // 代表每列列号 if (current_y == 0) { if (current_x > 1) current_x--; else current_x = table->current_width; } if (current_x == 0) { current_y = 0; current_x = table->current_width; } status = 1; } else { if (current_x > 0) { current_x--; } else if (current_x == 0) { if (current_y == -1) { current_x = FUNCTION_NUM - 1; } else { current_x = table->current_width - 1; } } status = 1; } } if (key == KEY_RIGHT_ARROW) { if (select_mode) { if (current_y == 0) { if (current_x < table->current_width) current_x++; else current_x = 1; } if (current_x == 0) { current_y = 0; current_x = 1; } status = 1; } else { if (current_y == -1) { current_x = (current_x + 1) % FUNCTION_NUM; } else { current_x = (current_x + 1) % table->current_width; } status = 1; } } if (key == KEY_UP_ARROW) { if (select_mode) { if (current_x == 0) { if (current_y > 1) current_y--; else current_y = table->current_height; } if (current_y == 0) { current_x = 0; current_y = table->current_height; } status = 1; } else { if (current_y > 0) { current_y--; } else if (current_y == 0) { current_y = -1; current_x = min(FUNCTION_NUM - 1, current_x); } else if (current_y == -1) { current_y = table->current_height - 1; current_x = min(table->current_width - 1, current_x); } status = 1; } } if (key == KEY_DOWN_ARROW) { if (select_mode) { if (current_x == 0) { if (current_y < table->current_height) current_y++; else current_y = 1; } if (current_y == 0) { current_x = 0; current_y = 1; } status = 1; } else { if (current_y < table->current_height - 1 && current_y >= 0) { current_y++; } else if (current_y == table->current_height - 1) { // TO FUNCTIONS AREA current_y = -1; current_x = min(FUNCTION_NUM - 1, current_x); } else if (current_y == -1) { current_y = 0; current_x = min(table->current_width - 1, current_x); } status = 1; } } } else { // ctrl + c if (key == 3) { alive = 0; message = "Bye!"; status = 1; } // ctrl + s if (key == 19) { save_table_to_file("table.dat", *table); message = "Saved!"; status = 1; } if (select_mode) { // ENTER if (key == KEY_ENTER) { end_select_index(table); status = 1; } // ESC if (key == KEY_ESC) { select_mode = 0; current_y = -1; current_x = select_entry; message = "Canceled!"; status = 1; } } else { // 判断 key 是可打印字符 if (key >= 32 && key <= 126) { add_char_to_cell(table, current_y, current_x, key); status = 1; } // 退格键 if (key == 8) { del_char_from_cell(table, current_y, current_x); status = 1; } // ENTER if (key == KEY_ENTER) { if (current_y == -1) { if (current_x == 0) { save_table_to_file("table.dat", *table); message = "Saved!"; } if (current_x == 1) { *table = load_table_from_file("table.dat"); message = "Loaded!"; } if (current_x == 2) { start_select_index(2); } if (current_x == 3) { start_select_index(3); } if (current_x == 4) { start_select_index(4); } if (current_x == 5) { start_select_index(5); } if (current_x == 6) { start_select_index(6); } if (current_x == 7) { alive = 0; message = "Bye!"; } } else { // 如果以 = 开头 if (table->data[current_y][current_x][0] == '=') { if (!check_valid_formatter(table, current_x, current_y)) { message = "Not a valid formatter!"; } } } } } } int debug = 0; if (debug) { char *tmp_message = (char *)malloc(sizeof(char) * MAX_MESSAGE_LENGTH); sprintf(tmp_message, "x: %d, y: %d", current_x, current_y); message = tmp_message; } return status; } int main() { TableInfo table; int load_from_file = 0; if (is_data_file_exist()) { char tmp; printf("Load from file? ([Y]/n): "); scanf("%c", &tmp); if (tmp == 'n' || tmp == 'N') { load_from_file = 0; } else { load_from_file = 1; } } if (load_from_file) { table = load_table_from_file("table.dat"); } else { printf("Let's create a table!\n"); int width = 0, height = 0; int column_width = 0; while (width <= 0 || width > MAX_COLUMN) { printf("Input width (1-%d): ", MAX_COLUMN); scanf("%d", &width); } while (height <= 0 || height > MAX_ROW) { printf("Input height (1-%d): ", MAX_ROW); scanf("%d", &height); } while (column_width <= 0 || column_width > MAX_COLUMN_WIDTH) { printf("Input column width (1-%d): ", MAX_COLUMN_WIDTH); scanf("%d", &column_width); } init_table(width, height, column_width, &table); } setCell(&table, 0, 0, "Hello"); while (alive) { printTable(&table); // drawGUI(currentSelection); int keyPressed = getch(); // 获取键盘输入 handleKeyPress(keyPressed, &table); // tick = (tick + 1) % MAX_TICK; } int save_to_file = 0; char tmp; printf("Save to file? ([Y]/n): "); scanf("%c", &tmp); if (tmp == 'n' || tmp == 'N') { save_to_file = 0; } else { save_to_file = 1; } if (save_to_file) { save_table_to_file("table.dat", table); } return 0; }