c++ - 将float数组写入&追加到C++中的hdf5文件中的唯一数据集

标签 c++ arrays file append hdf5

我正在处理大量文件,对文件的每次处理将输出数千个float数组,并将将所有文件的数据存储在单个hdf5 中的一个巨大数据集中,以进行进一步处理。

目前,我对如何将数据附加到hdf5文件感到困惑。 (上面代码中的注释)在上面的2个for循环中,如您所见,我想一次将1维浮点数组附加到hdf5中,而不是整体。我的数据以TB为单位,我们只能将数据附加到文件中。

有几个问题:

  • 在这种情况下如何追加数据?我必须使用哪种功能?
  • 现在,我有fdim [0] = 928347543,我尝试放入HDF5的无穷大标志,但运行时执行失败。有没有办法做到这一点?我不想计算每次的数据;有没有一种方法可以简单地继续添加数据而无需关心fdim的值?

  • 还是不可能?

    编辑:

    我一直遵循西蒙的建议,目前这是我的更新代码:
    hid_t desFi5;
    hid_t fid1;
    hid_t propList;
    hsize_t fdim[2];
    
    desFi5 = H5Fcreate(saveFilePath, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
    
    fdim[0] = 3;
    fdim[1] = 1;//H5S_UNLIMITED;
    
    fid1 = H5Screate_simple(2, fdim, NULL);
    
    cout << "----------------------------------Space done\n";
    
    propList = H5Pcreate( H5P_DATASET_CREATE);
    
    H5Pset_layout( propList, H5D_CHUNKED );
    
    int ndims = 2;
    hsize_t chunk_dims[2];
    chunk_dims[0] = 3;
    chunk_dims[1] = 1;
    
    H5Pset_chunk( propList, ndims, chunk_dims );
    
    cout << "----------------------------------Property done\n";
    
    hid_t dataset1 = H5Dcreate( desFi5, "des", H5T_NATIVE_FLOAT, fid1, H5P_DEFAULT, propList, H5P_DEFAULT);
    
    cout << "----------------------------------Dataset done\n";
    
    bufi = new float*[1];
    bufi[0] = new float[3];
    bufi[0][0] = 0;
    bufi[0][1] = 1;
    bufi[0][2] = 2;
    
    //hyperslab
    hsize_t start[2] = {0,0};
    hsize_t stride[2] = {1,1};
    hsize_t count[2] = {1,1};
    hsize_t block[2] = {1,3};
    
    H5Sselect_hyperslab( fid1, H5S_SELECT_OR, start, stride, count, block);     
    cout << "----------------------------------hyperslab done\n";   
    
    H5Dwrite(dataset1, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, *bufi);
    
    fdim[0] = 3;
    fdim[1] = H5S_UNLIMITED;    // COMPLAINS HERE
    H5Dset_extent( dataset1, fdim );
    
    cout << "----------------------------------extent done\n";
    
    //hyperslab2
    hsize_t start2[2] = {1,0};
    hsize_t stride2[2] = {1,1};
    hsize_t count2[2] = {1,1};
    hsize_t block2[2] = {1,3};
    
    H5Sselect_hyperslab( fid1, H5S_SELECT_OR, start2, stride2, count2, block2);     
    cout << "----------------------------------hyperslab2 done\n";  
    
    H5Dwrite(dataset1, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, *bufi);
    
    cout << "----------------------------------H5Dwrite done\n";        
    H5Dclose(dataset1);
    cout << "----------------------------------dataset closed\n";   
    H5Pclose( propList );   
    cout << "----------------------------------property list closed\n"; 
    H5Sclose(fid1); 
    cout << "----------------------------------dataspace fid1 closed\n";    
    H5Fclose(desFi5);       
    cout << "----------------------------------desFi5 closed\n";    
    

    我当前的输出是:
    bash-3.2$ ./hdf5AppendTest.out
    ----------------------------------Space done
    ----------------------------------Property done
    ----------------------------------Dataset done
    ----------------------------------hyperslab done
    HDF5-DIAG: Error detected in HDF5 (1.8.10) thread 0:
      #000: /home/hdftest/snapshots-bin-hdf5_1_8_10/current/src/H5D.c line 1103 in H5Dset_extent(): unable to set extend dataset
        major: Dataset
        minor: Unable to initialize object
      #001: /home/hdftest/snapshots-bin-hdf5_1_8_10/current/src/H5Dint.c line 2179 in H5D__set_extent(): unable to modify size of data space
        major: Dataset
        minor: Unable to initialize object
      #002: /home/hdftest/snapshots-bin-hdf5_1_8_10/current/src/H5S.c line 1874 in H5S_set_extent(): dimension cannot exceed the existing maximal size (new: 18446744073709551615 max: 1)
        major: Dataspace
        minor: Bad value
    ----------------------------------extent done
    ----------------------------------hyperslab2 done
    ----------------------------------H5Dwrite done
    ----------------------------------dataset closed
    ----------------------------------property list closed
    ----------------------------------dataspace fid1 closed
    ----------------------------------desFi5 closed
    

    目前,我看到使用H5Dset_extent无限设置内容仍然会在运行时引起问题。 (问题代码在上面的代码中用//COMPLAINS HERE标记。)我已经得到了Simon所指定的块数据,那么这是什么问题呢?

    另一方面,没有H5Dset_extent,我可以编写一个[0,1,2]的测试数组就可以了,但是我们如何使输出数组上方的代码像这样:
    [0, 1, 2]
    [0, 1, 2]
    [0, 1, 2]
    [0, 1, 2]
    ...
    ...
    

    回想一下:这只是一个测试数组,实际数据更大,并且我无法将所有内容都保存在RAM中,因此我必须一次将一部分数据逐个放入。

    编辑2:

    我遵循西蒙的建议。这是关键部分:
    hsize_t n = 3, p = 1;
    float *bufi_data = new float[n * p];
    float ** bufi = new float*[n];
    for (hsize_t i = 0; i < n; ++i){
        bufi[i] = &bufi_data[i * n];
    }
    
    bufi[0][0] = 0.1;
    bufi[0][1] = 0.2;
    bufi[0][2] = 0.3;
    
    //hyperslab
    hsize_t start[2] = {0,0};
    hsize_t count[2] = {3,1};
    
    H5Sselect_hyperslab( fid1, H5S_SELECT_SET, start, NULL, count, NULL);
    cout << "----------------------------------hyperslab done\n";   
    
    H5Dwrite(dataset1, H5T_NATIVE_FLOAT, H5S_ALL, fid1, H5P_DEFAULT, *bufi);
    
    bufi[0][0] = 0.4;
    bufi[0][1] = 0.5;
    bufi[0][2] = 0.6;
    
    hsize_t fdimNew[2];
    fdimNew[0] = 3;
    fdimNew[1] = 2;
    H5Dset_extent( dataset1, fdimNew );
    
    cout << "----------------------------------extent done\n";
    
    //hyperslab2
    hsize_t start2[2] = {0,0}; //PROBLEM
    hsize_t count2[2] = {3,1};
    
    H5Sselect_hyperslab( fid1, H5S_SELECT_SET, start2, NULL, count2, NULL);     
    cout << "----------------------------------hyperslab2 done\n";  
    
    H5Dwrite(dataset1, H5T_NATIVE_FLOAT, H5S_ALL, fid1, H5P_DEFAULT, *bufi);
    

    从上面,我得到了hdf5的以下输出:
    0.4 0.5 0.6
      0   0   0
    

    经过对start2count2的进一步实验,我看到这些变量仅影响bufi的起始索引和递增索引。它根本不会移动我的数据集的写作索引的位置。

    回想一下:最终结果必须是:
    0.1 0.2 0.3
    0.4 0.5 0.6
    

    另外,对于bufi和Simon,它必须是*bufi而不是H5Dwrite,因为bufi给我完全随机数。

    更新3:

    对于Simon建议的选择部分:
    hsize_t start[2] = {0, 0};
    hsize_t count[2] = {1, 3};
    
    hsize_t start[2] = {1, 0};
    hsize_t count[2] = {1, 3};
    

    这些将给出以下错误:
    HDF5-DIAG: Error detected in HDF5 (1.8.10) thread 0:
      #000: /home/hdftest/snapshots-bin-hdf5_1_8_10/current/src/H5Dio.c line 245 in H5Dwrite(): file selection+offset not within extent
        major: Dataspace
        minor: Out of range
    

    我想count[2]应该是{3,1},而不是{1,3}?对于start[2],如果我不将其设置为{0,0},它将始终大喊上述错误。

    您确定这是正确的吗?

    最佳答案

    How to append the data in this case? What kind of function must I use?



    您必须使用 hyperslabs 。那就是您只需要写数据集的一部分。
    要做的功能是 H5Sselect_hyperslab 。在fd1上使用它,并在fd1调用中将H5Dwrite用作文件数据空间。

    I have tried put infinity flag of HDF5 in, but the runtime execution complains.



    您需要创建一个分块数据集才能将其最大大小设置为无穷大。创建一个数据集创建属性列表,并使用 H5Pset_layout 对其进行分块。使用 H5Pset_chunk 设置块大小。然后使用此属性列表创建数据集。

    I don't want to calculate the data that I have each time; is there a way to just simply keep on adding data in, without caring the value of fdim?



    您可以做两件事:
  • 预计算最终大小,以便您可以创建足够大的数据集。看来这就是您在做什么。
  • 使用 H5Dset_extent 扩展数据集。为此,您需要将最大尺寸设置为无穷大,因此需要分块的数据集(请参见上文)。

  • 在这两种情况下,都需要在H5Dwrite调用中在文件数据空间上选择一个 super 平板(请参见上文)。

    演练working code
    #include <iostream>
    #include <hdf5.h>
    
    // Constants
    const char saveFilePath[] = "test.h5";
    const hsize_t ndims = 2;
    const hsize_t ncols = 3;
    
    int main()
    {
    

    首先,创建一个hdf5文件。
        hid_t file = H5Fcreate(saveFilePath, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
        std::cout << "- File created" << std::endl;
    

    然后创建一个2D数据空间。
    第一维的大小是无限的。我们最初将其设置为0,以显示如何在每一步扩展数据集。您还可以将其设置为例如要写入的第一个缓冲区的大小。
    第二维的大小是固定的。
        hsize_t dims[ndims] = {0, ncols};
        hsize_t max_dims[ndims] = {H5S_UNLIMITED, ncols};
        hid_t file_space = H5Screate_simple(ndims, dims, max_dims);
        std::cout << "- Dataspace created" << std::endl;
    

    然后创建一个数据集创建属性列表。
    使用无限尺寸时,必须对数据集的布局进行分块。
    块大小的选择会影响时间和磁盘空间上的性能。如果块很小,那么您将有很多开销。如果它们太大,那么您可能会分配不需要的空间,并且文件可能最终变得太大。
    这是一个玩具示例,因此我们将选择一行的块。
        hid_t plist = H5Pcreate(H5P_DATASET_CREATE);
        H5Pset_layout(plist, H5D_CHUNKED);
        hsize_t chunk_dims[ndims] = {2, ncols};
        H5Pset_chunk(plist, ndims, chunk_dims);
        std::cout << "- Property list created" << std::endl;
    

    创建数据集。
        hid_t dset = H5Dcreate(file, "dset1", H5T_NATIVE_FLOAT, file_space, H5P_DEFAULT, plist, H5P_DEFAULT);
        std::cout << "- Dataset 'dset1' created" << std::endl;
    

    关闭资源。现在已经创建了数据集,因此我们不再需要属性列表。
    我们不再需要文件数据空间,因为当扩展数据集时,由于仍然保留先前的范围,它将变得无效。
    因此,无论如何我们都必须获取更新的文件数据空间。
        H5Pclose(plist);
        H5Sclose(file_space);
    

    现在,我们将两个缓冲区附加到数据集的末尾。
    第一个将是两行。
    第二个将是三行。

    第一个缓冲区

    我们创建一个2D缓冲区(在内存中连续,行主要顺序)。
    我们将分配足够的内存来存储3行,因此我们可以重用缓冲区。
    让我们创建一个指针数组,以便我们可以使用b[i][j]表示法
    而不是buffer[i * ncols + j]。这纯粹是审美的。
        hsize_t nlines = 3;
        float *buffer = new float[nlines * ncols];
        float **b = new float*[nlines];
        for (hsize_t i = 0; i < nlines; ++i){
            b[i] = &buffer[i * ncols];
        }
    

    缓冲区中要写入数据集中的初始值:
        b[0][0] = 0.1;
        b[0][1] = 0.2;
        b[0][2] = 0.3;
        b[1][0] = 0.4;
        b[1][1] = 0.5;
        b[1][2] = 0.6;
    

    我们创建一个内存数据空间来指示内存中缓冲区的大小。
    请记住,第一个缓冲区只有两行长。
        dims[0] = 2;
        dims[1] = ncols;
        hid_t mem_space = H5Screate_simple(ndims, dims, NULL);
        std::cout << "- Memory dataspace created" << std::endl;
    

    现在,我们需要扩展数据集。
    我们将数据集的初始大小设置为0x3,因此我们需要首先对其进行扩展。
    请注意,我们扩展了数据集本身,而不是其数据空间。
    请记住,第一个缓冲区只有两行长。
        dims[0] = 2;
        dims[1] = ncols;
        H5Dset_extent(dset, dims);
        std::cout << "- Dataset extended" << std::endl;
    

    在文件数据集上选择hyperslab。
        file_space = H5Dget_space(dset);
        hsize_t start[2] = {0, 0};
        hsize_t count[2] = {2, ncols};
        H5Sselect_hyperslab(file_space, H5S_SELECT_SET, start, NULL, count, NULL);
        std::cout << "- First hyperslab selected" << std::endl;
    

    将缓冲区写入数据集。
    现在,mem_spacefile_space应该具有相同数量的所选元素。
    注意buffer&b[0][0]是等效的。
        H5Dwrite(dset, H5T_NATIVE_FLOAT, mem_space, file_space, H5P_DEFAULT, buffer);
        std::cout << "- First buffer written" << std::endl;
    

    现在,我们可以关闭文件数据空间。
    我们现在可以关闭内存数据空间,并为第二个缓冲区创建一个新的数据空间,
    但我们只会更新其大小。
        H5Sclose(file_space);
    

    第二缓冲区

    缓冲区中要附加到数据集的新值:
        b[0][0] = 1.1;
        b[0][1] = 1.2;
        b[0][2] = 1.3;
        b[1][0] = 1.4;
        b[1][1] = 1.5;
        b[1][2] = 1.6;
        b[2][0] = 1.7;
        b[2][1] = 1.8;
        b[2][2] = 1.9;
    

    调整内存数据空间的大小以指示缓冲区的新大小。
    第二个缓冲区长三行。
        dims[0] = 3;
        dims[1] = ncols;
        H5Sset_extent_simple(mem_space, ndims, dims, NULL);
        std::cout << "- Memory dataspace resized" << std::endl;
    

    扩展数据集。
    请注意,在这个简单的示例中,我们知道2 + 3 = 5。
    通常,您可以从文件数据空间读取当前范围
    并添加所需的行数。
        dims[0] = 5;
        dims[1] = ncols;
        H5Dset_extent(dset, dims);
        std::cout << "- Dataset extended" << std::endl;
    

    在文件数据集上选择hyperslab。
    同样在这个简单的例子中,我们知道0 + 2 = 2。
    通常,您可以从文件数据空间读取当前范围。
    第二个缓冲区长三行。
        file_space = H5Dget_space(dset);
        start[0] = 2;
        start[1] = 0;
        count[0] = 3;
        count[1] = ncols;
        H5Sselect_hyperslab(file_space, H5S_SELECT_SET, start, NULL, count, NULL);
        std::cout << "- Second hyperslab selected" << std::endl;
    

    将缓冲区追加到数据集
        H5Dwrite(dset, H5T_NATIVE_FLOAT, mem_space, file_space, H5P_DEFAULT, buffer);
        std::cout << "- Second buffer written" << std::endl;
    

    最后:关闭所有资源:
        delete[] b;
        delete[] buffer;
        H5Sclose(file_space);
        H5Sclose(mem_space);
        H5Dclose(dset);
        H5Fclose(file);
        std::cout << "- Resources released" << std::endl;
    }
    

    注意:我删除了以前的更新,因为答案太长。如果您有兴趣,请浏览历史记录。

    关于c++ - 将float数组写入&追加到C++中的hdf5文件中的唯一数据集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15379399/

    相关文章:

    java - 如何检查 int 是否包含字母

    C++从文件中读取char,转换为long long int

    JavaFX 从文件读取抛出 "InvocationTargetException"?

    java - 文件复制在 Eclipse 测试环境中有效,但在打包程序中产生 URI 错误

    c++ - 如何在 C++ 中使用指针翻转 Char 数组

    c++ - C++ 异常参数存储在内存中的位置?

    C++静态成员初始化(模板好玩里面)

    c++ - C++ 中的标量类型附加

    c - 将 fgets 与字符数组一起使用

    javascript - 根据复选框状态分配数组项值的正确方法是什么?