दो फ़ाइलों को स्वैप करने के लिए एक उपयोगिता

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करें, प्रत्येक कॉल के बाद परीक्षण करें, और विफलता से पहले सभी कार्यों को वापस रोल करें। यह भी स्वचालित रूप से noxistent रास्तों का ख्याल रखना होगा।

  • मत करो assert। यह बग को पकड़ने के लिए ही अच्छा है, रनटाइम की समस्या नहीं। उत्पादन कोड में (इसके साथ संकलित -DNDEBUG) यह कुछ नहीं करता है, और आपका कार्यक्रम mkstempविफलता का पता नहीं लगाएगा ।

  • 4 तर्कों के साथ कार्यक्रम को चुपचाप कुछ भी नहीं कहा जाता है। 2 तर्कों के साथ बुलाया जाए तो यह बहुत प्रयास करता है। print_help()किसी भी समय कॉल करना argc != 3अधिक सरल है।

1 Razzle Aug 28 2020 at 20:26

मुझे लगता है कि 'सवाल' कोड आलोचकों के लिए पूछ रहा है। अगर मैं उसके बारे में गलत हूँ, कृपया कोमल रहें .. LOL

मेरी पहली धारणा मुख्य की पठनीयता है। बड़े तर्क पार्सिंग स्विच अधिक महत्वपूर्ण तर्क छिपा रहे हैं।

हो सकता है कि इसे पढ़ना आसान हो (स्ट्रिंग ऑब्जेक्ट्स और केस असंवेदनशील तुलना?) .. इससे भी बेहतर: हाउसकीपिंग हेल्पर विधि को फिर से लागू करें।

(यह भी: कभी-कभी सरलीकृत करना ठीक है। यदि आपको दो फ़ाइल नाम वाले तर्क नहीं मिलते हैं, तो आप उपयोगकर्ता को स्पष्ट रूप से मदद मांगने के लिए कहने के बजाय तुरंत मदद कर सकते हैं।)