跳过正文

丙加·第9章·答案

·1413 字·7 分钟·
  1. 图书登记

    C++版本:

    // 50本上限是写给C看的。这里我们使用不限量容器vector(下一章学习)
    // 不会也可以模仿后面C的版本创建普通数组。
    #include <iostream>
    #include <string>
    #include <vector>
    #include <limits>
    
    using namespace std;
    
    struct Book {
        string name;
        double price;
        long long isbn;
    };
    
    void eatline() {
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(), '\n');
    }
    
    int main() {
        vector<Book> books;
    
        // 50本上限是写给C看的,所以 i < 50 完全可以删掉
        for (int i = 0; i < 50; i++) {
            cout << "-----Book" << (i + 1) << "-----" << endl;
    
            // 书名
            cout << "Name: ";
            string name;
            getline(cin, name);
    
            if (name == "%%") {
                cout << endl;
                break;
            }
    
            // 价格
            double price;
            cout << "Price: ";
            while (!(cin >> price)) {
                cout << "无效的价格,请重新输入: ";
                eatline();
            }
    
            // ISBN
            long long isbn;
            cout << "ISBN: ";
            while (!(cin >> isbn)) {
                cout << "无效的ISBN,请重新输入: ";
                eatline();
            }
            eatline();
    
            books.push_back({name, price, isbn});
        }
    
        // 输出结果
        for (size_t i = 0; i < books.size(); i++) {
            cout << "-----Book" << (i + 1) << "-----" << endl;
            cout << "Name: " << books[i].name << endl;
            cout << "Price: " << books[i].price << endl;
            cout << "ISBN: " << books[i].isbn << endl;
        }
    
        cout << "\n" << books.size() << " Book(s) in total." << endl;
        return 0;
    }
    
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    #define MAX_BOOKS 50
    #define MAX_NAME_LENGTH 100
    
    typedef struct {
        char name[MAX_NAME_LENGTH];
        float price;
        long long isbn;
    } Book;
    
    // 用于清除缓冲区多余内容
    void eatline() {
        int c;
        while ((c = getchar()) != '\n' && c != EOF);
    }
    
    int main() {
        Book books[MAX_BOOKS];
        int count = 0;
    
    
        for (int i = 0; i < MAX_BOOKS; i++) {
            printf("-----Book%d-----\n", i + 1);
    
            // 书名
            printf("Name: ");
    
            // 简单解释下scanf的格式化
            // 空格:  会跳过所有空白字符(包括换行符、空格、制表符等),
            //        避免前一次输入留下的换行符导致本次读取立即结束
            // %99:  限制了最多读取99个字符(留一个给\0)
            // [^\n]:表示读取直到遇到换行符(但不包括换行符)
            if (scanf(" %99[^\n]", books[i].name) != 1) {
                break;
            }
    
            // 检查结束条件
            if (strcmp(books[i].name, "%%") == 0) {
                printf("\n");
                break;
            }
    
            // 价格(记得错误检查)
            printf("Price: ");
            while (scanf("%f", &books[i].price) != 1) {
                printf("无效的价格,请重新输入: ");
                eatline();
            }
    
            // ISBN
            printf("ISBN: ");
            while (scanf("%lld", &books[i].isbn) != 1) {
                printf("无效的ISBN,请重新输入: ");
                eatline();
            }
    
            count++;
    
            // 清空输入缓冲区,避免多余的东西跑到下一本书
            // 比如isbn结尾不小心多输入了字母啥的
            eatline(); 
        }
    
        for (int i = 0; i < count; i++) {
            printf("-----Book%d-----\n", i + 1);
            printf("Name: %s\n", books[i].name);
            printf("Price: %.2f\n", books[i].price);
            printf("ISBN: %lld\n", books[i].isbn);
        }
    
        printf("\n%d Book(s) in total.\n", count);
        return 0;
    }
    
  2. (接下来的习题只提供 C++版本的答案)

    #include <iostream>
    #include <limits>
    #include <cstring>
    #include <cstdio>
    #include <cctype>
    
    using namespace std;
    
    const double PI = 3.14159265358;
    
    enum class ShapeType {
        RECT,
        CIRCLE,
    };
    
    struct Shape {
        ShapeType type;
    
        struct {
            int x, y;
        } pos;
    
        union {
            struct {
                int width, height;
            } rect;
            struct {
                int radius;
            } circle;
        };
    };
    
    void eatline() {
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(), '\n');
    }
    
    ShapeType getShapeType();
    void enter_shapeInfo(ShapeType type, Shape& shape);
    void calc_shapeInfo(const Shape& shape);
    
    int main() {
        ShapeType type = getShapeType();
        Shape shape;
    
        enter_shapeInfo(type, shape);
        calc_shapeInfo(shape);
    
        return 0;
    }
    
    
    
    ShapeType getShapeType() {
        char choice;
        cout << "Shape (R=Rect; C=Circle): ";
    
        while (true) {
            cin >> choice;
            choice = toupper(choice);
    
            if (cin.fail() || (choice != 'R' && choice != 'C')) {
                cout << "Error: Invalid shape '" << choice << "'!\n";
                eatline();
                continue;
            }
            break;
        }
        eatline();
    
        return (choice == 'C') ? ShapeType::CIRCLE : ShapeType::RECT;
    }
    
    void enter_shapeInfo(ShapeType type, Shape& shape) {
        shape.type = type;
    
        cout << "Enter its position: ";
        while (!(cin >> shape.pos.x >> shape.pos.y)) {
            eatline();
            cout << "Error: Invalid position!\n";
        }
    
        if (type == ShapeType::RECT) {
            cout << "Enter its width and height: ";
            while (!(cin >> shape.rect.width >> shape.rect.height) ||
                   shape.rect.width <= 0 || shape.rect.height <= 0) {
                eatline();
                cout << "Error: Invalid width and height! Values must be positive numbers.\n";
            }
        } else {
            cout << "Enter its radius: ";
            while (!(cin >> shape.circle.radius) || shape.circle.radius <= 0) {
                eatline();
                cout << "Error: Invalid radius! Value must be a positive number.\n";
            }
        }
    }
    
    void calc_shapeInfo(const Shape& shape) {
        int posx, posy;
        string pointPos;
        double area;
    
        if (shape.type == ShapeType::CIRCLE) {
            posx = shape.pos.x;
            posy = shape.pos.y + shape.circle.radius;
            pointPos = "at the bottom";
            area = PI * shape.circle.radius * shape.circle.radius;
        } else {
            posx = shape.pos.x + shape.rect.width;
            posy = shape.pos.y + shape.rect.height;
            pointPos = "in the bottom right corner";
            area = shape.rect.height * shape.rect.width;
        }
    
        printf("Then the coordinates of the point %s are (%d, %d)\n",
               pointPos.c_str(), posx, posy);
        printf("The area of it is %.2lf\n", area);
    }
    

    碎碎念:版本答案?

    阅读了这么多章节,读者肯定注意到了获取正确输入基本都有一个固定的模式,所以我们完全可以封装起来:

    template<typename T>
    T get_validInput(const string& prompt, const string& errorMsg) {
     T value;
     cout << prompt;
    
     while (!(cin >> value)) {
         eatline();
         cout << errorMsg << endl;
     }
     return value;
    }
    
    // 在main中使用
    char choice = get_validInput<char>(
     "Shape (R=Rect; C=Circle): ",
     "Invalid shape choice!"
    );
    choice = toupper(choice);
    

    或者利用 C++11 的元组特性,可以输入多个,顺便引入错误检查:

    template<typename... Args>
    tuple<Args...> get_validInputs(const string& prompt, const string& errorMsg) {
     tuple<Args...> values;
     apply([&](auto&... args) {
         cout << prompt;
    
         while (!((cin >> ... >> args))) {
             eatline();
             cout << errorMsg << endl;
         }
     }, values);
     return values;
    }
    
    // 使用示例:
    // 不过需要额外进行负数检查
    auto [shape.pos.x, shape.pos.y] = get_validInputs<int, int>("Enter its width and height: ", "Invalid width and height!\n");
    

    这样代码会更简洁些

  3. 稍微改造下前面的通用输入函数:

    #include <iostream>
    #include <cstdio>
    #include <ctime>
    #include <string>
    #include <limits>
    
    using namespace std;
    
    // 清除输入缓冲区
    void eatline() {
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(), '\n');
    }
    
    // 通用输入验证模板函数
    template<typename T>
    T get_validInput(const string& prompt, const string& errorMsg, 
                     bool (*validateFunc)(T) = nullptr) {
        T value;
        while (true) {
            cout << prompt;
            if (cin >> value) {
                // 如果有验证函数,检查值是否有效
                if (validateFunc && !validateFunc(value)) {
                    cout << errorMsg << endl;
                    eatline();
                    continue;
                }
                eatline(); // 清除缓冲区中的换行符
                return value;
            } else {
                eatline();
                cout << errorMsg << endl;
            }
        }
    }
    
    // 验证函数 - 检查数据包类型
    bool isValidPacketType(char type) {
        return type == 'A' || type == 'a' || type == 'B' || type == 'b';
    }
    
    // 打印时间戳的函数
    void print_timestamp(long long timestamp) {
        time_t raw_time = (time_t)(timestamp / 1000); // 假设是毫秒时间戳
        struct tm *time_info = localtime(&raw_time);
        printf("%04d-%02d-%02d %02d:%02d:%02d",
               time_info->tm_year + 1900, time_info->tm_mon + 1, time_info->tm_mday,
               time_info->tm_hour, time_info->tm_min, time_info->tm_sec);
    }
    
    // 定义数据包类型
    enum class PacketType { A, B };
    
    // 使用结构体和联合定义数据包
    struct Packet {
        PacketType type;
        union {
            struct {
                int command_code;
                int data_length;
            } typeA;
            struct {
                int status_code;
                long long timestamp;
            } typeB;
        } data;
    };
    
    int main() {
        Packet packet;
    
        // 获取数据包类型(带验证)
        char packetType = get_validInput<char>(
            "Packet Type (A=Command, B=Status): ",
            "Error: Invalid packet type! Please enter A or B.",
            isValidPacketType
            );
    
        if (packetType == 'A' || packetType == 'a') {
            packet.type = PacketType::A;
    
            // 获取命令码
            // 使用scanf的理由是
            // 可以一次性处理八、十、十六进制各种格式的输入
            // 八进制: 0100   -> 64
            // 十进制:  100   -> 100
            // 十六进制:0x100 -> 256
            cout << "Enter command code: ";
            while (true) {
                if (scanf("%i", &packet.data.typeA.command_code) == 1) {
                    eatline();
                    break;
                } else {
                    eatline(); // 清除错误输入
                    cout << "Error: Invalid command code format! Please enter a valid integer: ";
                }
            }
    
            // 获取数据长度(带验证)
            // 注意前后类型匹配:<int> 和 [](int v)
            packet.data.typeA.data_length = get_validInput<int>(
                "Enter data length: ",
                "Error: Data length must be a positive integer!",
                [](int v) { return v > 0; }
                );
    
            cout << "--- Packet A Info ---" << endl;
            // 这里hex是std::hex控制符
            cout << "Command: 0x" << hex << uppercase << packet.data.typeA.command_code << endl;
            cout << "Data Length: " << dec << packet.data.typeA.data_length << " bytes" << endl;
        }
        else if (packetType == 'B' || packetType == 'b') {
            packet.type = PacketType::B;
    
            // 获取状态码
            packet.data.typeB.status_code = get_validInput<int>(
                "Enter status code: ",
                "Error: Status code must be a positive integer!",
                [](int v) { return v > 0; }
                );
    
            // 获取时间戳
            packet.data.typeB.timestamp = get_validInput<long long>(
                "Enter timestamp: ",
                "Error: Timestamp must be a non-negative integer!",
                [](long long int v) { return v >= 0; }
                );
    
            cout << "--- Packet B Info ---" << endl;
            cout << "Status: " << packet.data.typeB.status_code << endl;
            cout << "Timestamp: ";
            print_timestamp(packet.data.typeB.timestamp);
            cout << endl;
        }
    
        return 0;
    }
    
  4. 修改 calc_shapeInfo 即可:

    void calc_shapeInfo(const Shape& shape) {
        int posx, posy;
        string pointPos;
        double area;
    
        // 放大后的变量
        int resized_posx, resized_posy;
        double resized_area;
    
        if (shape.type == ShapeType::CIRCLE) {
            posx = shape.pos.x;
            posy = shape.pos.y + shape.circle.radius;
            pointPos = "at the bottom";
            area = PI * shape.circle.radius * shape.circle.radius;
    
            // 放大2倍后的计算
            resized_posx = shape.pos.x;
            resized_posy = shape.pos.y + shape.circle.radius * 2;
            resized_area = PI * shape.circle.radius * 2 * shape.circle.radius * 2;
        } else {
            posx = shape.pos.x + shape.rect.width;
            posy = shape.pos.y + shape.rect.height;
            pointPos = "in the bottom right corner";
            area = shape.rect.height * shape.rect.width;
    
            // 放大2倍后的计算
            resized_posx = shape.pos.x + shape.rect.width * 2;
            resized_posy = shape.pos.y + shape.rect.height * 2;
            resized_area = shape.rect.height * 2 * shape.rect.width * 2;
        }
    
        printf("Then the coordinates of the point %s are (%d, %d)\n",
               pointPos.c_str(), posx, posy);
        printf("The area of it is %.0lf\n", area);
    
        // 添加放大后的输出
        printf("If resized to 2x:\n");
        printf("Then the coordinates of the point %s are (%d, %d)\n",
               pointPos.c_str(), resized_posx, resized_posy);
        printf("The area of it is %.0lf\n", resized_area);
    }
    
命令提示符@CommandPrompt-Wang
作者
命令提示符@CommandPrompt-Wang