ยูทิลิตี้สำหรับสลับสองไฟล์

Aug 17 2020

ในขณะที่ฉันไม่เคยต้องสลับไฟล์สองไฟล์ฉันสงสัยมาตลอดว่ามีคำสั่งสำหรับการสลับสองไฟล์หรือไม่และเมื่อเร็ว ๆ นี้ฉันตัดสินใจดูว่ามีหรือไม่ ในที่สุดฉันก็พบว่าไม่มีเลยและด้วยเหตุนี้ฉันจึงตัดสินใจสร้างขึ้นมา

นี่คือรหัส: main.cc

#include <iostream>
#include <filesystem>
#include <cstring>
#include <cassert>

static auto print_help() -> void {
    std::cout << "Usage: swap [file1] [file2]\n";
    std::cout << "swaps the contents of file1 and file2\n";
    std::cout << "use swap --help to print this message\n";
}

static auto validate_files(const std::filesystem::path& file1_path, const std::filesystem::path& file2_path) -> void {
    {
        /* check if file exists */
        const auto file1_exists = std::filesystem::exists(file1_path);
        const auto file2_exists = std::filesystem::exists(file2_path);
        const auto exists = file1_exists && file2_exists;


        if (!exists) {
            if (!file1_exists) std::cerr << "cannot find file " << file1_path << '\n';
            if (!file2_exists) std::cerr << "cannot find file " << file2_path << '\n';
            exit(EXIT_FAILURE);
        }
    }

    {
        if (file1_path == file2_path) {
            std::cerr << "swaping the same two files does nothing\n";
            exit(EXIT_SUCCESS);
        }
    }
}

static auto get_temp_filename(char* template_name) -> void {
    /* tmpnam_s does not work on linux */
#if defined(WIN32) || defined(_WIN32)
    errno_t err = tmpnam_s(template_name, L_tmpnam);
    assert(!err);
#else
    int err = mkstemp(template_name);
    assert(err != -1);
#endif
}

int main(int argc, char** argv) {
    std::ios::sync_with_stdio(false);
    switch (argc) {
        case 2: {
            /* convert the second arg to upper case */
            std::transform(argv[1],
                argv[1] + strlen(argv[1]),
                argv[1],
                ::toupper);
            if (!strcmp(argv[1], "--HELP")) {
                print_help();
                return EXIT_SUCCESS;
            }
            else {
                std::cerr << "Invalid args see --help for usage\n";
                return EXIT_FAILURE;
            }
        }
        case 3:
            break;
        default: {
            std::cerr << "Invalid args see --help for usage\n";
            return EXIT_FAILURE;
        }
    }
    const auto file1_path = std::filesystem::path{ argv[1] };
    const auto file2_path = std::filesystem::path{ argv[2] };


    validate_files(file1_path, file2_path);
    char temp_filename[L_tmpnam] = "XXXXXX";
    get_temp_filename(temp_filename);
    const auto temp_filepath = std::filesystem::path{ temp_filename };
    /* move-swap the files instead of copy-swaping */
    /* renaming a file is the same as moving it */
    std::filesystem::rename(file1_path, temp_filepath);
    std::filesystem::rename(file2_path, file1_path);
    std::filesystem::rename(temp_filepath, file2_path);
}

นี่คือตัวอย่างการใช้งาน:

swap doc1.txt doc2.txt

คำตอบ

11 vnp Aug 17 2020 at 06:22
  • file1_path == file2_pathก็บอกได้ว่าพา ธ อ้างถึงไฟล์เดียวกัน อย่างไรก็ตามแม้ว่าจะfile1_path != file2_pathยังคงอ้างถึงไฟล์เดียวกันก็ตาม

  • file1_exists = std::filesystem::exists(file1_path);แนะนำเงื่อนไขการแข่งขัน TOC-TOU ไฟล์อาจมีอยู่ในขณะทดสอบ แต่ก็หายไปตามเวลาที่ใช้งาน ดูหัวข้อย่อยถัดไป

  • std::filesystem::renameอาจล้มเหลว คุณเรียกมันว่าต้นไม้ครั้ง หากการเรียกครั้งที่สองหรือครั้งที่สามล้มเหลว (มีข้อยกเว้น!) ระบบไฟล์จะจบลงไม่ตรงตามที่คาดหวังไว้ ใช้การnoexceptโอเวอร์โหลดทดสอบerror_codeหลังการโทรแต่ละครั้งและย้อนกลับการกระทำทั้งหมดก่อนที่จะล้มเหลว นอกจากนี้ยังจะดูแลเส้นทางที่ไม่มีอยู่จริงโดยอัตโนมัติ

  • อย่าassert. เป็นการดีที่จะจับบั๊กไม่ใช่ปัญหารันไทม์ ในรหัสการผลิต (คอมไพล์ด้วย-DNDEBUG) จะไม่ทำอะไรเลยและโปรแกรมของคุณจะตรวจไม่พบmkstempความล้มเหลว

  • โปรแกรมจะไม่ทำอะไรเลยหากเรียกด้วย, พูด, 4 อาร์กิวเมนต์ นอกจากนี้ยังต้องใช้ความพยายามอย่างมากหากเรียกด้วย 2 อาร์กิวเมนต์ โทรprint_help()ครั้งใดก็ได้argc != 3ง่ายกว่ามาก

1 Razzle Aug 28 2020 at 20:26

ฉันเดาว่า 'คำถาม' กำลังถามถึงการวิจารณ์โค้ด ถ้าฉันผิดเรื่องนั้นโปรดอ่อนโยน .. LOL

ความประทับใจแรกของฉันคือความสามารถในการอ่านหลัก สวิตช์แยกวิเคราะห์อาร์กิวเมนต์ขนาดใหญ่ซ่อนตรรกะที่สำคัญกว่า ..

อาจทำให้ง่ายต่อการอ่าน (สตริงออบเจ็กต์และการเปรียบเทียบแบบไม่คำนึงถึงตัวพิมพ์เล็กหรือใหญ่?) .. ยิ่งไปกว่านั้น: หลีกเลี่ยงวิธีการช่วยทำความสะอาด

(เช่นกัน: บางครั้งความเรียบง่ายก็ใช้ได้หากคุณไม่ได้รับอาร์กิวเมนต์ชื่อไฟล์สองรายการคุณสามารถคายความช่วยเหลือได้ทันทีแทนที่จะบอกให้ผู้ใช้ขอความช่วยเหลืออย่างชัดเจน)