C ++仅覆盖文件段

时间:2015-07-17 22:07:07

标签: c++ 2d fstream tiles overwrite

我有一个非常大的一维数组,包含我游戏的Tile值。每个值都是一个4位数字,代表一种瓷砖(污垢,草,空气)。

我使用fstream将值保存在文件中:

std::fstream save("game_save", std::fstream::out | std::fstream::in);

所以让我说我有一张小地图。 3个瓷砖宽,3个瓷砖高,所有污垢(污垢值为0001)。

在游戏中它看起来像

0001 0001 0001

0001 0001 0001

0001 0001 0001

在文件中看起来像(只是一维)

0001 0001 0001 0001 0001 0001 0001 0001 0001

如果我想转到第5个值(第2行第2列)并仅更改该值,让我们说0002,我该怎么办?这样,当我再次运行游戏并且它读取它看到的文件时:

0001 0001 0001 0001 0002 0001 0001 0001

有关如何执行此操作的任何建议将不胜感激

7 个答案:

答案 0 :(得分:2)

如果你完全确定每个元素的4位+正好1个空格,并且文件中没有tabl或换行符,你可以使用 seekp(n*5,ios_base::beg) 来定位你的下一个写作在第n个元素上,只是覆盖它。

<强> Suggesions

如果使用ios::binary模式打开文件,使用这种定位会更安全。

在这种情况下,您还可以考虑使用块函数read() / write()读取/写入二进制数据,并使用n*sizeof(tile)找到正确的位置。然后该文件不再完全独立于平台,并且不可能使用文本编辑器手动编辑它,但是您的性能会有所提高,特别是如果您的地形非常大,如果您经常访问连续的元素,同一行。

答案 1 :(得分:1)

简单的方法是再次编写整个数组。特别是因为它相当短。如果您知道每个元素恰好是5个butes,则可以使用seekp设置写入位置,因此save.seekp((1 * 3 + 1) * 5)然后单独写入该值。但是,如果你的文件不是巨大的(实际文件仍然会在至少1个扇区更新,硬盘上为512或4096字节),那么它的工作量可能超过它的价值。

答案 2 :(得分:0)

int loc=5;
save.seekg ((loc-1)*5, save.beg);
save << "0002";

试试那个人:)

C++ Fstream to replace specific line?上的选定答案似乎是一个很好的解释。

答案 3 :(得分:0)

您要做的是寻找输出流的正确部分。

auto doc = ui->textEdit->document();
auto blockFormat = doc->begin().blockFormat();
blockFormat.setTopMargin(100);
QTextCursor{doc->begin()}.setBlockFormat(blockFormat);

这是一个完整的例子:

fstream save;
...
save.seekp(POSITION_IN_FILE);

答案 4 :(得分:0)

我建议使用内存映射文件而不是fstream。你可以使用boost。 Boost library documentation

有堆栈溢出线程,它涵盖了有关文件映射的信息。 Stack overflow Thred

答案 5 :(得分:0)

这将是

的内容
save.seekp((row * number_columns + col)* size_of_element_data);
save.write(element_data, size_of_element_data);

但是重新读取文件,编辑元素并将整个文件写回来会更容易和更安全。您无法在文件中间调整大小或插入元素,而无需向后移动文件的其余部分(这会增加清除文件末尾现在未使用的空间的问题)或转发(这需要您填充文件流的末尾,然后执行非破坏性移动。)

答案 6 :(得分:0)

我在这里使用内存映射文件,并且最好也使用二进制表示。

这样你就可以存储一组int,而不需要任何(de)序列化代码和/或寻求。

E.g。

<强> Live On Coliru

#include <stdexcept>
#include <iostream>
#include <algorithm>

// memory mapping
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#include <cstring>
#include <cassert>

template <size_t N, size_t M, typename T = int> 
struct Tiles {

    Tiles(char const* fname)
        : fd(open(fname, O_RDWR | O_CREAT)) 
    {
        if (fd == -1) {
            throw std::runtime_error(strerror(errno));
        }

        auto size = N*M*sizeof(T);
        if (int err = posix_fallocate(fd, 0, size)) {
            throw std::runtime_error(strerror(err));
        }

        tiles = static_cast<T*>(mmap(nullptr, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0));

        if (MAP_FAILED == tiles) {
            throw std::runtime_error(strerror(errno));
        }
    }

    ~Tiles() {
        if (-1 == munmap(tiles, N*M*sizeof(T))) {
            throw std::runtime_error(strerror(errno));
        }
        if (-1 == close(fd)) {
            throw std::runtime_error(strerror(errno));
        }
    }

    void init(T value) {
        std::fill_n(tiles, N*M, value);
    }

    T& operator()(size_t row, size_t col) { 
        assert(row >= 0 && row <= N);
        assert(col >= 0 && col <= M);
        return tiles[(row*M)+col];
    }

    T const& operator()(size_t row, size_t col) const { 
        assert(row >= 0 && row <= N);
        assert(col >= 0 && col <= M);
        return tiles[(row*M)+col];
    }
  private:
    int fd   = -1;
    T* tiles = nullptr;
};

int main(int argc, char** argv) {
    using Map = Tiles<3, 3, uint16_t>;

    Map data("tiles.dat");

    if (argc>1) switch(atoi(argv[1])) {
        case 1: 
            data.init(0x0001);
            break;
        case 2:
            data(1, 1) = 0x0002;
            break;
    }
}

打印哪些:

clang++ -std=c++11 -O2 -Wall -pedantic -pthread main.cpp && set -x; for cmd in 0 1 2; do ./a.out $cmd; xxd tiles.dat; done
+ for cmd in 0 1 2
+ ./a.out 0
+ xxd tiles.dat
0000000: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000010: 0000                                     ..
+ for cmd in 0 1 2
+ ./a.out 1
+ xxd tiles.dat
0000000: 0100 0100 0100 0100 0100 0100 0100 0100  ................
0000010: 0100                                     ..
+ for cmd in 0 1 2
+ ./a.out 2
+ xxd tiles.dat
0000000: 0100 0100 0100 0100 0200 0100 0100 0100  ................
0000010: 0100                                     ..