c++ 我是否正确理解了fseek和fwrite/fread的组合

66bbxpm5  于 2023-01-18  发布在  其他
关注(0)|答案(2)|浏览(163)

因此,我被要求将一些代码从C“翻译”到C++,真正的要求是尽可能地删除原始C,并用C++ STL函数调用替换它。
我必须更改的一件事是将pwritepread的用法分别更改为std::fwritestd::fread,但是,当前代码大量使用pwritepread的偏移选项,因为数据将被写入Linux或BSD系统上的设备,即/dev/sda1
但是std::fwritestd::fread没有像pwritepread那样的偏移量参数,所以根据我的理解,我需要先使用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;
    }    
}
xj3cbfub

xj3cbfub1#

您交换了一些参数。fseek调用应如下所示

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

否则你的理解是正确的。先查找,然后读取。注意,这个两步解决方案a)较慢(两次系统调用),B)如果在多个线程中使用相同的FILE*,则不安全。
既然您提到了转换为C++:fwritefseek是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());
}
guykilcj

guykilcj2#

您的理解是正确的,但参数是错误的。要从文件开头查找第n个字节,请使用:

// 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.

相关问题