2023-01-12用C++完成一個(gè)多平臺可用的學(xué)生管理系統(tǒng)-創(chuàng)新互聯(lián)

用C++完成一個(gè)跨平臺可用的學(xué)生管理系統(tǒng)
  • 前言
  • 一、Windows和Linux終端編碼
  • 二、完整代碼
  • 總結(jié)

創(chuàng)新互聯(lián)成立于2013年,先為商城等服務(wù)建站,商城等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為商城企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。
前言

學(xué)生管理系統(tǒng)可能是計(jì)算機(jī)相關(guān)專業(yè)學(xué)生在學(xué)完C語言和C++之后能做的比較完整的一個(gè)程序, 所以問答中經(jīng)常出現(xiàn). 例如設(shè)計(jì)學(xué)生成績管理系統(tǒng).

用C語言或C++的基本特性就完全能夠解決, 基本用不到多線程, 多態(tài), 模板等.

但有個(gè)小問題, 在Windows平臺和Linux平臺, 對中文的編碼和輸出不太一樣, Windows終端是GBK編碼, 雖然可以用簡單命令轉(zhuǎn)為UTF-8編碼, 但實(shí)踐操作中發(fā)現(xiàn), 這只適用于漢字輸出, 對于輸入是一塌糊度. 如果是純Windows平臺程序, 我倒是建議將代碼編碼格式直接設(shè)置為GBK, 這樣對于輸入輸出不會(huì)有什么問題, 只不過會(huì)有一些編碼警告, 忽略即可.

如果要跨平臺, 甚至日后進(jìn)行拓展, 則需要UTF-8編碼, 而對編碼的轉(zhuǎn)換, 需要一些函數(shù)實(shí)現(xiàn), 目前問題主要集中在Windows中.


一、Windows和Linux終端編碼

可能是某些歷史原因, Windows終端選擇用GBK編碼, 而Linux終端則選擇用utf8編碼, 這導(dǎo)致了跨平臺IO的不同, 一套代碼不能在兩個(gè)平臺使用.

以前我們討論過, 關(guān)于中文編碼, 存儲, 編碼轉(zhuǎn)換, 多字節(jié), 寬字節(jié)等等, 總之到處是坑, 防不勝防, 不親自踩幾個(gè)坑, 很難知道這水有多混.

由于Linux終端編碼如此貼心, 無需太多顧忌, 我們只需處理號Windows終端編碼就好了.

我們需要判斷目前程序是在哪個(gè)平臺, 于是要用到宏:

#ifdef __WIN64
    SetConsoleOutputCP(CP_UTF8); // 終端輸出的是UTF8編碼
    SetConsoleCP(CP_ACP);        // 終端輸入的是GBK編碼
#endif

以上代碼的意思是如果定義了win64, 也就是在Windows64平臺, 那么設(shè)置輸出編碼是UTF8, 輸入編碼是系統(tǒng)的local編碼, 也就是中文GBK. 當(dāng)然以上編碼需要windows.h頭文件, 這個(gè)會(huì)在下面程序代碼中.

為什么不把輸入編碼設(shè)為UTF8, 和Linux一樣, 這樣就省事多了. 開始我也是這么想的, 但是馬上就踩坑了, 當(dāng)輸入設(shè)置為utf8, 漢字無法輸入, 我不知到它究竟將漢字轉(zhuǎn)換成什么東西, 總之是不可以, 這個(gè)終端黑盒沒有透露什么細(xì)節(jié), 所以沒辦法.

退而求其次, 用終端local編碼進(jìn)行輸入, 因?yàn)槲覀兊某绦蚓幋a是utf8, 輸入進(jìn)來的是GBK, 中間沒有轉(zhuǎn)化, 所以如果直接看, 就是亂碼, 直接輸出也是亂碼.

為了補(bǔ)足編碼轉(zhuǎn)換, 我用了下面這個(gè)Windows專有的轉(zhuǎn)換函數(shù). 通過終端傳過來的四不像編碼文字轉(zhuǎn)換為寬字符( MultiByteToWideChar() ), 再將寬字符轉(zhuǎn)回真正的utf8編碼文字( WideCharToMultiByte() ).

細(xì)節(jié)上, 由于名字很可能有空格, 所以需要讀取整行, 但是通常如果直接讀會(huì)讀到上一個(gè)輸入剩下的換行符, 完美錯(cuò)過真正的內(nèi)容, 所以需要進(jìn)行輸入流清空( ignore() ). 同時(shí)為防止姓名字過多, 一般是故意的, 但某些國家名字百八十個(gè)字母也是有的, 需要在超出緩沖區(qū)后清空輸入流, 以防止后續(xù)直接跳出.

為了節(jié)省點(diǎn)創(chuàng)建緩沖區(qū)時(shí)間, 我把緩沖區(qū)設(shè)置為靜態(tài)區(qū)域, 但要注意, 靜態(tài)區(qū)域在你用完后不會(huì)自己清空, 所以需要手動(dòng)清空,以備后用.

#ifdef __WIN64
#includevoid gbkToUtf8(std::istream &istm, char *chrs)
{static char temp[BUFFERSIZE] = {};
    static wchar_t wTemp[WCHARBUFFERSIZE] = {};
    istm.clear();
    istm.ignore(CLEARCHARSIZE, '\n');
    if (!istm.getline(temp, BUFFERSIZE))
    {istm.clear();
        istm.ignore(CLEARCHARSIZE, '\n');
    }
    MultiByteToWideChar(CP_ACP, 0, temp, -1, wTemp, WCHARBUFFERSIZE);
    WideCharToMultiByte(CP_UTF8, 0, wTemp, -1, chrs, BUFFERSIZE, nullptr,
                        nullptr);
    memset(reinterpret_cast(temp), 0, BUFFERSIZE);
    memset(reinterpret_cast(wTemp), 0, BUFFERSIZE);
}
#endif
二、完整代碼

這是基本要求, 不難實(shí)現(xiàn):

設(shè)計(jì)和實(shí)現(xiàn)一個(gè)“學(xué)生成績管理系統(tǒng)”,滿足以下要求:

  1. 系統(tǒng)以菜單方式工作;
  2. 使用鏈表或結(jié)構(gòu)數(shù)組對學(xué)生成績進(jìn)行管理和維護(hù);
  3. 使用二進(jìn)制文件在磁盤上保存學(xué)生記錄信息;
  4. 鏈表中各結(jié)點(diǎn)或結(jié)構(gòu)數(shù)組中各元素包括“學(xué)號、姓名、成績數(shù)組(含計(jì)算機(jī)、數(shù)學(xué)、
    物理、外語四門課程)、總分”基本字段;
  5. 實(shí)現(xiàn)如下基本功能:
    (1) 查看所有學(xué)生信息
    (2) 計(jì)算總分
    (3) 排序
    按學(xué)號排序
    按總分排序
    (4) 添加學(xué)生(此功能下可實(shí)現(xiàn)學(xué)生信息的錄入)
    (5) 插入學(xué)生(在已按學(xué)號有序的提下,將學(xué)生插入到合適的位置)
    (6) 查找學(xué)生(查找并顯示學(xué)生的學(xué)號、姓名、四門課程成績、總分)
    按學(xué)號查找
    按姓名查找
    查詢每門課成績都在80分以上的學(xué)生信息
    (7) 刪除學(xué)生 ( 刪除指定學(xué)號的學(xué)生 )
    (8) 將學(xué)生記錄保存到文件存盤 ( 將數(shù)據(jù)以文件的形式存盤 )
    (9) 從文件中讀入學(xué)生記錄 ( 將已經(jīng)存盤的文件數(shù)據(jù)讀入內(nèi)存 )

為了滿足二進(jìn)制存儲和讀取, 學(xué)生類不能用string或vector這種非平凡類對象成員, 這樣只能用char數(shù)組表示名字的字符串, 但為了進(jìn)行方便比較, 還是實(shí)現(xiàn)一個(gè)函數(shù)將字符串轉(zhuǎn)換為string.

為了便于添加和排序以及查找, 我用list鏈表存儲學(xué)生數(shù)據(jù). 由于各成員函數(shù)使用了大量算法庫中的函數(shù), 所以也大量引入了簡單的lambda閉包, 如果你對lambda不熟悉, 請務(wù)必盡快熟悉, 否則C++高級性質(zhì)就基本與你無緣. 另外, 某些算法函數(shù)會(huì)再套用算法函數(shù), 不必害怕, 都非常簡單, 相比于自己實(shí)現(xiàn), 這種函數(shù)套用更為方便, 且語義更為準(zhǔn)確.

菜單實(shí)現(xiàn)選擇太多, 就不要用if語句了, swich語句是最為合適的.

#include 
#include#include#include#include#include#define BUFFERSIZE 32
#define WCHARBUFFERSIZE 16
#define CLEARCHARSIZE 1024

#ifdef __WIN64
#includevoid gbkToUtf8(std::istream &istm, char *chrs)
{static char temp[BUFFERSIZE] = {};
    static wchar_t wTemp[WCHARBUFFERSIZE] = {};
    istm.clear();
    istm.ignore(CLEARCHARSIZE, '\n');
    if (!istm.getline(temp, BUFFERSIZE))
    {istm.clear();
        istm.ignore(CLEARCHARSIZE, '\n');
    }
    MultiByteToWideChar(CP_ACP, 0, temp, -1, wTemp, WCHARBUFFERSIZE);
    WideCharToMultiByte(CP_UTF8, 0, wTemp, -1, chrs, BUFFERSIZE, nullptr,
                        nullptr);
    memset(reinterpret_cast(temp), 0, BUFFERSIZE);
    memset(reinterpret_cast(wTemp), 0, BUFFERSIZE);
}
#endif

struct student
{student() = default;

    explicit student(std::istream &istm)
    {std::cout<< "輸入學(xué)號: "<< std::endl;
        istm >>xueHao;
        std::cout<< "輸入姓名"<< std::endl;

#ifdef __WIN64
        gbkToUtf8(istm, xingMing);
#endif

#ifndef __WIN64
        istm >>xingMing;
#endif
        std::cout<< "輸入計(jì)算機(jī)成績"<< std::endl;
        istm >>jiSuanJiChengJi;
        std::cout<< "輸入數(shù)學(xué)成績"<< std::endl;
        istm >>shuXueChengJi;
        std::cout<< "輸入物理成績"<< std::endl;
        istm >>wuLiChengJi;
        std::cout<< "輸入外語成績"<< std::endl;
        istm >>waiYuChengJi;
        zongFen = jiSuanJiChengJi + shuXueChengJi + wuLiChengJi + waiYuChengJi;
    }

    [[nodiscard]] auto showZongFen() const ->int
    {return zongFen;
    }

    [[nodiscard]] auto showXueHao() const ->int
    {return xueHao;
    }

    [[nodiscard]] auto showXingMing() const ->std::string
    {return xingMing;
    }

    void print() const
    {std::cout<< xueHao<< '\t'<< xingMing<< '\t'<< jiSuanJiChengJi
       << '\t'<< shuXueChengJi<< '\t'<< wuLiChengJi<< '\t'
       << waiYuChengJi<< '\t'<< zongFen<< '\n';
    }

    void printZongFen() const
    {std::cout<< xueHao<< '\t'<< xingMing<< '\t'<< zongFen<< '\n';
    }

    void printTo(std::ostream &ostm) const
    {ostm.write(reinterpret_cast(this), sizeof(student));
    }

    [[nodiscard]] auto everyScoreUp(int num) const ->bool
    {return jiSuanJiChengJi >num && shuXueChengJi >num &&
               wuLiChengJi >num && waiYuChengJi >num;
    }

  private:
    int xueHao = 0;
    char xingMing[BUFFERSIZE] = {};
    int jiSuanJiChengJi = 0;
    int shuXueChengJi = 0;
    int wuLiChengJi = 0;
    int waiYuChengJi = 0;
    int zongFen = 0;
};

struct studentVector
{studentVector() = default;

    void viewStudent()
    {std::cout<< "學(xué)號\t姓名\t計(jì)算機(jī)\t數(shù)學(xué)\t物理\t外語\t總分\n";
        std::for_each(studentList.begin(), studentList.end(),
                      [](const student &stu) {stu.print(); });
    }

    void viewZongFen()
    {std::cout<< "學(xué)號\t姓名\t總分\n";
        std::for_each(studentList.begin(), studentList.end(),
                      [](const student &stu) {stu.printZongFen(); });
    }

    void sortWithXueHao()
    {studentList.sort([](const student &stuA, const student &stuB) {return stuA.showXueHao()< stuB.showXueHao();
        });

        sortXueHao = true;
    }

    void sortWithZongFen()
    {studentList.sort([](const student &stuA, const student &stuB) {return stuA.showZongFen() >stuB.showZongFen();
        });

        sortXueHao = false;
    }

    void pushStudent(const student &stu)
    {studentList.push_back(stu);

        sortXueHao = false;
    }

    void insertStudent(const student &stu)
    {if (sortXueHao)
        {studentList.insert(
                std::find_if(studentList.begin(), studentList.end(),
                             [&stu](const student &stuA) { return stu.showXueHao()< stuA.showXueHao();
                             }),
                stu);
        }
        else
        {sortWithXueHao();

            studentList.insert(
                std::find_if(studentList.begin(), studentList.end(),
                             [&stu](const student &stuA) { return stu.showXueHao()< stuA.showXueHao();
                             }),
                stu);
        }
    }

    void searchStudentWithXueHao(int xueHao_)
    {auto result = std::find_if(studentList.begin(), studentList.end(),
                                   [&xueHao_](const student &stu) {   return xueHao_ == stu.showXueHao();
                                   });

        if (result != studentList.end())
        {result->print();
        }
    }

    void searchStudentWithXingMing(const std::string &xingMing_)
    {auto result = std::find_if(studentList.begin(), studentList.end(),
                                   [&xingMing_](const student &stu) {   return xingMing_ == stu.showXingMing();
                                   });

        if (result != studentList.end())
        {result->print();
        }
    }

    void searchStudentWithEveryScoreUp80()
    {std::for_each(studentList.begin(), studentList.end(),
                      [](const student &stu) {  if (stu.everyScoreUp(80))
                          {  stu.print();
                          }
                      });
    }

    auto deleteStudent(int xueHao_) ->bool
    {auto result = std::find_if(studentList.begin(), studentList.end(),
                                   [&xueHao_](const student &stu) {   return xueHao_ == stu.showXueHao();
                                   });

        if (result != studentList.end())
        {studentList.erase(result);
            return true;
        }

        return false;
    }

    void save()
    {std::ofstream file("studentVector.bak");

        std::for_each(studentList.begin(), studentList.end(),
                      [&file](const student &stu) {stu.printTo(file); });
    }

    void read()
    {std::ifstream file("studentVector.bak");

        student stu;

        while (file.read(reinterpret_cast(&stu), sizeof(student)))
        {studentList.push_back(stu);
        }
    }

  private:
    std::liststudentList;
    bool sortXueHao = false;
};

void pritnCaiDan()
{std::cout<< "學(xué)生成績管理系統(tǒng), 請按相應(yīng)數(shù)字進(jìn)行操作\n";
    std::cout<< "————————————————————命令概覽—————————————————————\n";
    std::cout<< "| 1 查看學(xué)生信息\t\t\t\t|\n";
    std::cout<< "| 2 計(jì)算學(xué)生總分\t\t\t\t|\n";
    std::cout<< "| 3 按照學(xué)號排序(由小到大)\t\t\t|\n";
    std::cout<< "| 4 按照總成績排序(由高到低)\t\t\t|\n";
    std::cout<< "| 5 添加學(xué)生\t\t\t\t\t|\n";
    std::cout<< "| 6 插入學(xué)生\t\t\t\t\t|\n";
    std::cout<< "| 7 按學(xué)號查找學(xué)生信息\t\t\t\t|\n";
    std::cout<< "| 8 按姓名查找學(xué)生信息\t\t\t\t|\n";
    std::cout<< "| 9 查找每門高于80分學(xué)生信息\t\t\t|\n";
    std::cout<< "| 10 刪除指定學(xué)號的學(xué)生\t\t\t\t|\n";
    std::cout<< "| 11 保存到指定文件\t\t\t\t|\n";
    std::cout<< "| 12 從指定文件讀取數(shù)據(jù)\t\t\t\t|\n";
    std::cout<< "| 15 退出程序\t\t\t\t\t|\n";
    std::cout<< "—————————————————————————————————————————————————\n";
}

auto main() ->int
{#ifdef __WIN64
    SetConsoleOutputCP(CP_UTF8); // 終端輸出的是UTF8編碼
    SetConsoleCP(CP_ACP);        // 終端輸入的是GBK編碼
#endif

    studentVector stuVec;
    pritnCaiDan();
    int caiDanXuanXiang = 0;
    while (std::cin >>caiDanXuanXiang)
    {switch (caiDanXuanXiang)
        {case 1:
            stuVec.viewStudent();
            break;
        case 2:
            stuVec.viewZongFen();
            break;
        case 3:
            stuVec.sortWithXueHao();
            break;
        case 4:
            stuVec.sortWithZongFen();
            break;
        case 5:
            stuVec.pushStudent(student(std::cin));
            break;
        case 6:
            stuVec.insertStudent(student(std::cin));
            break;
        case 7: {std::cout<< "請輸入學(xué)號: "<< std::endl;
            int xueHao_;
            std::cin >>xueHao_;
            std::cout<< "學(xué)號\t姓名\t計(jì)算機(jī)\t數(shù)學(xué)\t物理\t外語\t總分\n";
            stuVec.searchStudentWithXueHao(xueHao_);
            break;
        }
        case 8: {std::cout<< "請輸入學(xué)生姓名: "<< std::endl;

#ifdef __WIN64
            static char temp[BUFFERSIZE] = {};
            gbkToUtf8(std::cin, temp);
            std::cout<< "學(xué)號\t姓名\t計(jì)算機(jī)\t數(shù)學(xué)\t物理\t外語\t總分\n";
            stuVec.searchStudentWithXingMing(temp);
            memset(reinterpret_cast(temp), 0, BUFFERSIZE);
            break;
#endif

#ifndef __WIN64
            std::string xingMing_;
            std::cin >>xingMing_;

            std::cout<< "學(xué)號\t姓名\t計(jì)算機(jī)\t數(shù)學(xué)\t物理\t外語\t總分\n";
            stuVec.searchStudentWithXingMing(xingMing_);
            break;
#endif
        }
        case 9: {std::cout<< "學(xué)號\t姓名\t計(jì)算機(jī)\t數(shù)學(xué)\t物理\t外語\t總分\n";
            stuVec.searchStudentWithEveryScoreUp80();
            break;
        }
        case 10: {std::cout<< "請輸入學(xué)號: "<< std::endl;
            int xueHao_;
            std::cin >>xueHao_;
            stuVec.deleteStudent(xueHao_);
            break;
        }
        case 11:
            stuVec.save();
            break;
        case 12:
            stuVec.read();
            break;
        case 15:
            return 0;
            break;
        }
        std::cout<< "\n\n";
        pritnCaiDan();
    }

    return 0;
}

總結(jié)

學(xué)生管理系統(tǒng), 可以說是最簡單, 只需要基礎(chǔ) IO 知識就可以編寫的完整有用的程序, 如果不用跨平臺, 那是相當(dāng)簡單. 簡單程序也可以逐漸擴(kuò)展, 比如socket編程, 遠(yuǎn)程調(diào)用, 比如連接數(shù)據(jù)庫進(jìn)行增刪改查, 甚至多線程調(diào)用, 比如 qt 加圖形菜單稱為一個(gè)界面程序.

路漫漫其修遠(yuǎn)兮, 吾將上下而求索.

你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧

分享名稱:2023-01-12用C++完成一個(gè)多平臺可用的學(xué)生管理系統(tǒng)-創(chuàng)新互聯(lián)
文章URL:http://www.muchs.cn/article14/hgoge.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制開發(fā)、網(wǎng)站收錄小程序開發(fā)、搜索引擎優(yōu)化外貿(mào)網(wǎng)站建設(shè)、App設(shè)計(jì)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)