İki dosyayı takas etmek için bir yardımcı program

Aug 17 2020

İki dosyayı değiştirmek zorunda kalmamış olsam da, her zaman iki dosyayı değiştirmek için bir komut olup olmadığını merak ettim ve son zamanlarda bir tane olup olmadığını görmeye karar verdim. Sonunda bir tane olmadığını öğrendim ve sonuç olarak bir tane oluşturmaya karar verdim.

İşte kod: 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);
}

İşte bir kullanım örneği:

swap doc1.txt doc2.txt

Yanıtlar

11 vnp Aug 17 2020 at 06:22
  • file1_path == file2_pathkesinlikle yolların aynı dosyaya başvurduğunu söyler. Ancak file1_path != file2_pathyine de aynı dosyaya başvursalar bile .

  • file1_exists = std::filesystem::exists(file1_path);TOC-TOU yarış koşulu sunar. Dosya test sırasında mevcut olabilir, ancak kullanım sırasında kaybolabilir. Bir sonraki kurşuna bakın.

  • std::filesystem::renameBaşarısız olabilir. Sen buna ağaç zamanları diyorsun. İkinci veya üçüncü çağrı başarısız olursa (bir istisna dışında!), Dosya sistemi tam olarak beklendiği gibi olmaz. noexceptAşırı yükleme kullanın , error_codeher aramadan sonra test edin ve başarısızlıktan önce tüm eylemleri geri alın. Bu aynı zamanda varolmayan yolları otomatik olarak hallederdi.

  • Yapma assert. Çalışma zamanı sorunlarını değil, yalnızca hataları yakalamak iyidir. Üretim kodunda (ile derlenmiş -DNDEBUG) hiçbir şey yapmaz ve programınız bir mkstemparıza tespit etmez .

  • Program, diyelim ki 4 bağımsız değişkenle çağrılırsa sessizce hiçbir şey yapmaz. Ayrıca 2 argümanla çağrılırsa çok çaba gerektirir. print_help()İstediğiniz zaman aramak argc != 3çok daha kolay.

1 Razzle Aug 28 2020 at 20:26

Sanırım 'soru' kod eleştirisi istiyor. Bu konuda yanılıyorsam lütfen nazik ol .. LOL

İlk izlenimim main'in okunabilirliğidir . Büyük bağımsız değişken çözümleme anahtarı, daha önemli mantığı saklıyor ..

Okumayı kolaylaştırabilir (dizgi nesneleri ve büyük / küçük harfe duyarlı olmayan karşılaştırmalar?) .. Daha da iyisi: bir temizlik yardımcı yöntemine yönlendirmek.

(Ayrıca: Bazen basitleştirmek uygundur . İki dosya adı argümanı almazsanız, kullanıcıya açıkça yardım istemesini söylemek yerine hemen yardımdan söz edebilirsiniz.)