代码之家  ›  专栏  ›  技术社区  ›  Lars Nielsen

我正确理解fseek和fwrite/frad的组合吗

c++
  •  2
  • Lars Nielsen  · 技术社区  · 2 年前

    因此,我被要求将一些代码从C“翻译”到C++,真正的要求是尽可能多地删除原始C,并用C++STL函数调用替换它。

    我必须改变的一件事是的用法 pwrite pread std::fwrite std::fread 分别地然而,当前代码大量使用的偏移选项 pwrite 展开 因为数据正在被写入Linux或BSD系统上的设备。, /dev/sda1 .

    但是 std::fwrite std::fread 没有偏移量参数,如 pwrite 展开 。因此,根据我所收集的信息,我需要首先使用 std::fseek 然后进行写/读操作。 我对此的理解正确吗?

    我已经编写了一个小的测试程序来验证我的假设,见下文,但在启动代码之前,我想更确定一点。

    测试程序是这样编译的: clang++ -std=c++17 main.cpp

    #include <vector>
    #include <string>
    
    #include <ctime>
    #include <algorithm>
    
    #include <cstdio>
    
    void write(const std::string& device, const size_t offset, const std::vector<uint8_t>& data)
    {
    
        std::FILE* fp = std::fopen(device.c_str(), "wb");
    
        if (offset != 0)
        {
            std::fseek(fp, 0, offset);
        }
        
        std::fwrite(data.data(), sizeof(uint8_t), data.size(), fp);
        std::fclose(fp);
    }
    
    std::vector<uint8_t> read(const std::string& device, size_t offset, const size_t size)
    {
        std::vector<uint8_t> data(size);
    
        std::FILE* fp = std::fopen(device.c_str(), "rb");
    
        if (offset != 0)
        {
            std::fseek(fp, 0, offset);
        }
    
        std::fread(data.data(), sizeof(uint8_t), size, fp);
    
        std::fclose(fp);
        return data;
    }
    
    
    std::vector<std::vector<uint8_t>> generate(const size_t num_vec, const size_t data_size)
    {
        std::vector<std::vector<uint8_t>> vectors;
    
        for (size_t i = 0; i < num_vec; ++i)
        {
            std::vector<uint8_t> data(data_size);
            std::generate(data.begin(), data.end(), rand);
            vectors.push_back(data);
        }
        return vectors;
    }
    
    int main(void)
    {
        srand(static_cast<uint32_t>(time(0)));
        
        const std::string device = "/dev/sda1";
        const size_t size = 4096;
        const size_t num_vec = 4;
    
        auto vectors = generate(num_vec, size);
    
        size_t offset = 0;
        
        for (const auto& vec : vectors)
        {
            write(device, offset, vec);
            auto other = read(device, offset, size);
    
            if (vec != other)
            {
                std::puts("success");
            }
            else
            {
                std::puts("failure");
            }
    
            offset = offset + size;
        }    
    }
    
    2 回复  |  直到 2 年前
        1
  •  2
  •   Homer512    2 年前

    您交换了一些参数。你的 fseek 电话应该是这样的

        if (offset != 0)
        {
            std::fseek(fp, offset, SEEK_SET);
        }
    

    否则,您的理解是正确的。你先寻找,然后阅读。请注意,此两步解决方案a)速度较慢(两个系统调用),b)如果使用相同的解决方案,则不具有线程安全性 FILE* 在多个线程中。

    既然你提到翻译成C++: fwrite fseek 是C库的一部分。如果您想要STL C++解决方案,请切换到 std::fstream 相反

    #include <fstream>
    
    void write(const std::string& device, const size_t offset,
               const std::vector<uint8_t>& data)
    {
    
        std::ofstream fp {device, std::ios::binary };
        if (offset != 0)
        {
            fp.seekg(offset);
        }
        
        fp.write(reinterpret_cast<const char*>(data.data()),
                sizeof(uint8_t) * data.size());
    }
    
        2
  •  1
  •   rgnt    2 年前

    你的理解是正确的,但参数是错误的。 寻求 n 从文件开始使用的第th个字节:

    // parameter descriptions:
    //  descriptor, offset, origin
    std::fseek(file, n, SEEK_SET);
    

    根据功能描述:

    如果流是以二进制模式打开的,则如果原点是SEEK_SET,则新位置是从文件的开头测量的偏移字节,如果原点是SEEK_CUR,则是从当前文件的位置测量的偏移,如果原点为SEEK_end,则是到文件的结尾测量的偏移。二进制流不需要支持SEEK_END,特别是当输出额外的空字节时。

    See example at the bottom of a cppreference page .