2009-02-24 23 views
15

Đưa ra một chuỗi các chuỗi, cách tốt nhất để viết chúng ra một tập dữ liệu HDF5 là gì? Hiện nay tôi đang làm một cái gì đó như sau:Làm thế nào để viết tốt nhất một std :: vector <std :: string> container vào một tập dữ liệu HDF5?

const unsigned int MaxStrLength = 512; 

    struct TempContainer { 
    char string[MaxStrLength]; 
    }; 

    void writeVector (hid_t group, std::vector<std::string> const & v) 
    { 
    // 
    // Firstly copy the contents of the vector into a temporary container 
    std::vector<TempContainer> tc; 
    for (std::vector<std::string>::const_iterator i = v.begin() 
               , end = v.end() 
     ; i != end 
     ; ++i) 
    { 
     TempContainer t; 
     strncpy (t.string, i->c_str(), MaxStrLength); 
     tc.push_back (t); 
    } 


    // 
    // Write the temporary container to a dataset 
    hsize_t  dims[] = { tc.size() } ; 
    hid_t dataspace = H5Screate_simple(sizeof(dims)/sizeof(*dims) 
           , dims 
           , NULL); 

    hid_t strtype = H5Tcopy (H5T_C_S1); 
    H5Tset_size (strtype, MaxStrLength); 

    hid_t datatype = H5Tcreate (H5T_COMPOUND, sizeof (TempConainer)); 
    H5Tinsert (datatype 
     , "string" 
     , HOFFSET(TempContainer, string) 
     , strtype); 

    hid_t dataset = H5Dcreate1 (group 
          , "files" 
          , datatype 
          , dataspace 
          , H5P_DEFAULT); 

    H5Dwrite (dataset, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, &tc[0]); 

    H5Dclose (dataset); 
    H5Sclose (dataspace); 
    H5Tclose (strtype); 
    H5Tclose (datatype); 
} 

Tối thiểu, tôi thực sự muốn thay đổi trên để:

  1. Nó sử dụng chuỗi dài biến
  2. tôi don không cần phải có thùng chứa tạm thời

Tôi không có giới hạn về cách lưu trữ dữ liệu, chẳng hạn, nó không phải là COMPOUND datatype nếu có một cách tốt hơn để làm điều này.

CHỈNH SỬA: Chỉ để thu hẹp sự cố, tôi khá quen thuộc với việc chơi với dữ liệu ở phía C++, đó là mặt HDF5 nơi tôi cần hầu hết sự trợ giúp.

Cảm ơn sự giúp đỡ của bạn.

Trả lời

10

[Rất cám ơn đến dirkgently sự giúp đỡ của ông trong việc trả lời này.]

để viết một chuỗi dài biến trong HDF5 sử dụng như sau:

// Create the datatype as follows 
hid_t datatype = H5Tcopy (H5T_C_S1); 
H5Tset_size (datatype, H5T_VARIABLE); 

// 
// Pass the string to be written to H5Dwrite 
// using the address of the pointer! 
const char * s = v.c_str(); 
H5Dwrite (dataset 
    , datatype 
    , H5S_ALL 
    , H5S_ALL 
    , H5P_DEFAULT 
    , &s); 

Một giải pháp để viết vùng chứa là viết từng phần tử riêng lẻ. Điều này có thể đạt được bằng cách sử dụng hyperslabs.

Ví dụ:

class WriteString 
{ 
public: 
    WriteString (hid_t dataset, hid_t datatype 
     , hid_t dataspace, hid_t memspace) 
    : m_dataset (dataset), m_datatype (datatype) 
    , m_dataspace (dataspace), m_memspace (memspace) 
    , m_pos() {} 

private: 
    hid_t m_dataset; 
    hid_t m_datatype; 
    hid_t m_dataspace; 
    hid_t m_memspace; 
    int m_pos; 

// ...

public: 
    void operator()(std::vector<std::string>::value_type const & v) 
    { 
    // Select the file position, 1 record at position 'pos' 
    hsize_t count[] = { 1 } ; 
    hsize_t offset[] = { m_pos++ } ; 
    H5Sselect_hyperslab(m_dataspace 
     , H5S_SELECT_SET 
     , offset 
     , NULL 
     , count 
     , NULL); 

    const char * s = v.c_str(); 
    H5Dwrite (m_dataset 
     , m_datatype 
     , m_memspace 
     , m_dataspace 
     , H5P_DEFAULT 
     , &s); 
    }  
}; 

// ...

void writeVector (hid_t group, std::vector<std::string> const & v) 
{ 
    hsize_t  dims[] = { m_files.size() } ; 
    hid_t dataspace = H5Screate_simple(sizeof(dims)/sizeof(*dims) 
            , dims, NULL); 

    dims[0] = 1; 
    hid_t memspace = H5Screate_simple(sizeof(dims)/sizeof(*dims) 
            , dims, NULL); 

    hid_t datatype = H5Tcopy (H5T_C_S1); 
    H5Tset_size (datatype, H5T_VARIABLE); 

    hid_t dataset = H5Dcreate1 (group, "files", datatype 
          , dataspace, H5P_DEFAULT); 

    // 
    // Select the "memory" to be written out - just 1 record. 
    hsize_t offset[] = { 0 } ; 
    hsize_t count[] = { 1 } ; 
    H5Sselect_hyperslab(memspace, H5S_SELECT_SET, offset 
        , NULL, count, NULL); 

    std::for_each (v.begin() 
     , v.end() 
     , WriteStrings (dataset, datatype, dataspace, memspace)); 

    H5Dclose (dataset); 
    H5Sclose (dataspace); 
    H5Sclose (memspace); 
    H5Tclose (datatype); 
}  
+0

Bạn biết không? HDF5 là một trong những điều tôi luôn muốn đọc và viết. Nhưng sự trì hoãn là tên đệm của tôi đã không được thực hiện. Nhờ bạn, tôi đã quyết định cho nó một shot chuyên dụng hơn lần này. Tôi rất, rất quan tâm để biết, nơi bạn đang sử dụng này, nếu có thể. – dirkgently

+0

Chúng tôi đang tìm cách thay đổi cách công cụ phân tích tĩnh lưu trữ dữ liệu mà nó tập hợp từ phân tích của nó. Dữ liệu sẽ chứa cây như cấu trúc (phạm vi, loại vv) và danh sách chẩn đoán. Ở giai đoạn này, tôi chỉ đánh giá HDF5 xử lý các loại dữ liệu khác nhau như thế nào. –

+0

Câu hỏi này (mà tôi đã hỏi) phác thảo loại tính năng mà chúng tôi đang đánh giá: http://stackoverflow.com/questions/547195/evaluating-hdf5-what-limitations-features-does-hdf5-provide-for-modelling -data –

1

Nếu bạn đang xem xét mã sạch hơn: Tôi khuyên bạn nên tạo một hàm functor sẽ lấy một chuỗi và lưu nó vào Vùng chứa HDF5 (ở chế độ mong muốn). Richard, tôi đã sử dụng thuật toán sai, vui lòng kiểm tra lại!

std::for_each(v.begin(), v.end(), write_hdf5); 

struct hdf5 : public std::unary_function<std::string, void> { 
    hdf5() : _dataset(...) {} // initialize the HDF5 db 
    ~hdf5() : _dataset(...) {} // close the the HDF5 db 
    void operator(std::string& s) { 
      // append 
      // use s.c_str() ? 
    } 
}; 

Điều đó có giúp bắt đầu không?

+0

Vâng - vâng tôi hy vọng để có thể đạt được loại này của phong cách - tuy nhiên, tôi không chắc chắn nếu đó là (a) có thể và (b) hiệu quả. Cảm ơn câu trả lời. –

+0

Tôi thực sự rất mới với HDF5, vì vậy tôi không có ý tưởng những gì cần phải được viết nơi bạn có "// chắp thêm". –

+0

Tôi đã nghe rất nhiều về HDF5. Tôi có nghĩa là bằng cách nối thêm bất cứ điều gì bạn đang làm theo bình luận // Viết container tạm thời vào một tập dữ liệu. – dirkgently

-1

Tôi không biết về HDF5, nhưng bạn có thể sử dụng

struct TempContainer { 
    char* string; 
}; 

và sau đó sao chép các dây theo cách này:

TempContainer t; 
t.string = strdup(i->c_str()); 
tc.push_back (t); 

này sẽ phân bổ một chuỗi với kích thước chính xác, và cũng có thể cải thiện rất nhiều khi chèn hoặc đọc từ container (trong ví dụ của bạn có một mảng sao chép, trong trường hợp này chỉ là một con trỏ). Bạn cũng có thể sử dụng std :: vector:

std::vector<char *> tc; 
... 
tc.push_back(strdup(i->c_str()); 
+0

Chắc chắn. Lý tưởng nhất là tôi sẽ không cần container tạm thời cả. Mã này cho biết thêm những bất lợi nhỏ mà bộ nhớ cần phải được giải phóng một cách rõ ràng. –

0

Thay vì một TempContainer, bạn có thể sử dụng một std :: vector đơn giản (bạn cũng có thể templatized nó để phù hợp với T -> basic_string Something như thế này:

.
4

Dưới đây là một số mã làm việc để viết vectơ các chuỗi có độ dài thay đổi bằng API HDF5 C++.

tôi kết hợp một số những gợi ý trong bài viết khác:

  1. sử dụng H5T_C_S1 và H5T_VARIABLE
  2. sử dụng string::c_str() để có được con trỏ đến chuỗi
  3. nơi con trỏ vào một vector của char* và chuyển cho API HDF5

Đây là không cần thiết để tạo các bản sao đắt tiền của chuỗi (ví dụ: với strdup()). c_str() trả về một con trỏ tới dữ liệu được kết thúc bằng null của chuỗi cơ bản. Đây chính xác là những gì mà hàm này dành cho. Tất nhiên, chuỗi với null nhúng sẽ không làm việc với điều này ...

std::vector được đảm bảo phải có bộ nhớ nằm bên dưới liền kề, vì vậy sử dụng vectorvector::data() cũng giống như sử dụng các mảng thô nhưng tất nhiên là gọn gàng và an toàn hơn nhiều so với clunky, c-cách cũ làm việc.

#include "H5Cpp.h" 
void write_hdf5(H5::H5File file, const std::string& data_set_name, 
       const std::vector<std::string>& strings) 
{ 
    H5::Exception::dontPrint(); 

    try 
    { 
     // HDF5 only understands vector of char* :-(
     std::vector<const char*> arr_c_str; 
     for (unsigned ii = 0; ii < strings.size(); ++ii) 
      arr_c_str.push_back(strings[ii].c_str()); 

     // 
     // one dimension 
     // 
     hsize_t  str_dimsf[1] {arr_c_str.size()}; 
     H5::DataSpace dataspace(1, str_dimsf); 

     // Variable length string 
     H5::StrType datatype(H5::PredType::C_S1, H5T_VARIABLE); 
     H5::DataSet str_dataset = file.createDataSet(data_set_name, datatype, dataspace); 

     str_dataset.write(arr_c_str.data(), datatype); 
    } 
    catch (H5::Exception& err) 
    { 
     throw std::runtime_error(string("HDF5 Error in ") 
            + err.getFuncName() 
            + ": " 
            + err.getDetailMsg()); 


    } 
} 
+0

đẹp! Nhưng làm thế nào để bạn đọc từ tập tin trở lại vào một 'std :: vector '? – Walter

+0

:-(thật không may, tôi nhận được segfault với điều này ... – Walter

+0

Những gì hệ điều hành này hoạt động trên? Bởi vì nó trông như thế này sẽ gây ra segfaults trên rất nhiều máy móc, hoặc chỉ bị hỏng dữ liệu của bạn.Tôi nghi ngờ vì tôi đã làm một cái gì đó tương tự đã hoạt động trên Linux nhưng không thành công trên OSX. – Shep

0

Vì lợi ích của việc có khả năng đọcstd::vector<std::string> tôi gửi bài giải pháp của tôi, dựa trên các gợi ý từ Leo đây https://stackoverflow.com/a/15220532/364818.

Tôi đã trộn lẫn các API C và C++. Xin vui lòng chỉnh sửa điều này và làm cho nó đơn giản hơn.

Lưu ý rằng API HDF5 trả về danh sách các con trỏ char* khi bạn gọi đọc. Những con trỏ char* này phải được giải phóng sau khi sử dụng, nếu không có rò rỉ bộ nhớ.

dụ Cách sử dụng

H5::Attribute Foo = file.openAttribute("Foo"); 
std::vector<std::string> foos 
Foo >> foos; 

Dưới đây là đoạn code

const H5::Attribute& operator>>(const H5::Attribute& attr0, std::vector<std::string>& array) 
    { 
     H5::Exception::dontPrint(); 

     try 
     { 
      hid_t attr = attr0.getId(); 

      hid_t atype = H5Aget_type(attr); 
      hid_t aspace = H5Aget_space(attr); 
      int rank = H5Sget_simple_extent_ndims(aspace); 
      if (rank != 1) throw PBException("Attribute " + attr0.getName() + " is not a string array"); 

      hsize_t sdim[1]; 
      herr_t ret = H5Sget_simple_extent_dims(aspace, sdim, NULL); 
      size_t size = H5Tget_size (atype); 
      if (size != sizeof(void*)) 
      { 
       throw PBException("Internal inconsistency. Expected pointer size element"); 
      } 

      // HDF5 only understands vector of char* :-(
      std::vector<char*> arr_c_str(sdim[0]); 

      H5::StrType stringType(H5::PredType::C_S1, H5T_VARIABLE); 
      attr0.read(stringType, arr_c_str.data()); 
      array.resize(sdim[0]); 
      for(int i=0;i<sdim[0];i++) 
      { 
       // std::cout << i << "=" << arr_c_str[i] << std::endl; 
       array[i] = arr_c_str[i]; 
       free(arr_c_str[i]); 
      } 

     } 
     catch (H5::Exception& err) 
     { 
      throw std::runtime_error(string("HDF5 Error in ") 
            + err.getFuncName() 
            + ": " 
            + err.getDetailMsg()); 


     } 

     return attr0; 
    } 
1

Tôi đã có một vấn đề tương tự, với sự báo trước rằng tôi muốn có một vector của chuỗi lưu trữ dưới dạng thuộc tính. Điều khó khăn với các thuộc tính là chúng ta không thể sử dụng các tính năng dataspace ưa thích như hyperslabs (ít nhất là với API C++).

Nhưng trong cả hai trường hợp, có thể hữu ích khi nhập vectơ chuỗi vào một mục nhập trong tập dữ liệu (ví dụ: bạn luôn mong muốn đọc chúng cùng nhau). Trong trường hợp này, tất cả các ma thuật đi kèm với loại, không phải với chính dataspace.

Về cơ bản có 4 bước:

  1. Thực hiện một vector<const char*> mà chỉ vào chuỗi.
  2. Tạo cấu trúc hvl_t trỏ đến vectơ và chứa chiều dài của nó.
  3. Tạo kiểu dữ liệu.Đây là một H5::VarLenType gói một (chiều dài biến đổi) H5::StrType.
  4. Viết loại hvl_t vào tập dữ liệu.

Phần thực sự thú vị của phương pháp này là bạn nhồi toàn bộ mục nhập vào những gì HDF5 xem xét giá trị vô hướng. Điều này có nghĩa rằng làm cho nó một thuộc tính (chứ không phải là một tập dữ liệu) là tầm thường.

Cho dù bạn chọn giải pháp này hoặc chuỗi với mỗi chuỗi trong mục nhập tập dữ liệu của riêng nó cũng có thể là vấn đề của hiệu suất mong muốn: nếu bạn đang tìm kiếm quyền truy cập ngẫu nhiên vào các chuỗi cụ thể, có thể viết chuỗi trong tập dữ liệu để chúng có thể được lập chỉ mục. Nếu bạn luôn đọc tất cả chúng cùng nhau, giải pháp này cũng có thể hoạt động tốt.

Dưới đây là một ví dụ ngắn về cách để làm điều này, sử dụng C++ API và một tập dữ liệu vô hướng đơn giản:

#include <vector> 
#include <string> 
#include "H5Cpp.h" 

int main(int argc, char* argv[]) { 
    // Part 0: make up some data 
    std::vector<std::string> strings; 
    for (int iii = 0; iii < 10; iii++) { 
    strings.push_back("this is " + std::to_string(iii)); 
    } 

    // Part 1: grab pointers to the chars 
    std::vector<const char*> chars; 
    for (const auto& str: strings) { 
    chars.push_back(str.data()); 
    } 

    // Part 2: create the variable length type 
    hvl_t hdf_buffer; 
    hdf_buffer.p = chars.data(); 
    hdf_buffer.len = chars.size(); 

    // Part 3: create the type 
    auto s_type = H5::StrType(H5::PredType::C_S1, H5T_VARIABLE); 
    s_type.setCset(H5T_CSET_UTF8); // just for fun, you don't need this 
    auto svec_type = H5::VarLenType(&s_type); 

    // Part 4: write the output to a scalar dataset 
    H5::H5File out_file("vtest.h5", H5F_ACC_EXCL); 
    H5::DataSet dataset(
    out_file.createDataSet("the_ds", svec_type, H5S_SCALAR)); 
    dataset.write(&hdf_buffer, svec_type); 

    return 0; 
} 
0

Tôi muộn để bên nhưng tôi đã sửa đổi câu trả lời Leo Goodstadt dựa trên các yêu cầu liên quan segfaults. Tôi đang trên Linux, nhưng tôi không có vấn đề như vậy. Tôi đã viết 2 hàm, một để viết một vectơ của std :: string thành một tập dữ liệu của một tên đã cho trong một tệp mở H5File và một hàm khác để đọc lại các tập dữ liệu kết quả thành một vectơ của chuỗi std ::. Lưu ý rằng có thể sao chép không cần thiết giữa các loại một vài lần có thể được tối ưu hóa hơn. Dưới đây là mã làm việc cho văn bản và đọc:

void write_varnames(const std::string& dsetname, const std::vector<std::string>& strings, H5::H5File& f) 
    { 
    H5::Exception::dontPrint(); 

    try 
     { 
     // HDF5 only understands vector of char* :-(
     std::vector<const char*> arr_c_str; 
     for (size_t ii = 0; ii < strings.size(); ++ii) 
     { 
     arr_c_str.push_back(strings[ii].c_str()); 
     } 

     // 
     // one dimension 
     // 
     hsize_t  str_dimsf[1] {arr_c_str.size()}; 
     H5::DataSpace dataspace(1, str_dimsf); 

     // Variable length string 
     H5::StrType datatype(H5::PredType::C_S1, H5T_VARIABLE); 
     H5::DataSet str_dataset = f.createDataSet(dsetname, datatype, dataspace); 

     str_dataset.write(arr_c_str.data(), datatype); 
     } 
    catch (H5::Exception& err) 
     { 
     throw std::runtime_error(std::string("HDF5 Error in ") 
       + err.getFuncName() 
       + ": " 
       + err.getDetailMsg()); 


     } 
    } 

Và để đọc:

std::vector<std::string> read_string_dset(const std::string& dsname, H5::H5File& f) 
    { 
    H5::DataSet cdataset = f.openDataSet(dsname); 


    H5::DataSpace space = cdataset.getSpace(); 

    int rank = space.getSimpleExtentNdims(); 

    hsize_t dims_out[1]; 

    int ndims = space.getSimpleExtentDims(dims_out, NULL); 

    size_t length = dims_out[0]; 

    std::vector<const char*> tmpvect(length, NULL); 

    fprintf(stdout, "In read STRING dataset, got number of strings: [%ld]\n", length); 

    std::vector<std::string> strs(length); 
    H5::StrType datatype(H5::PredType::C_S1, H5T_VARIABLE); 
    cdataset.read(tmpvect.data(), datatype); 

    for(size_t x=0; x<tmpvect.size(); ++x) 
     { 
     fprintf(stdout, "GOT STRING [%s]\n", tmpvect[x]); 
     strs[x] = tmpvect[x]; 
     } 

    return strs; 
    } 
Các vấn đề liên quan