std :: vector 컨테이너를 HDF5 데이터 세트에 가장 잘 작성하는 방법은 무엇입니까?
문제
문자열의 벡터가 주어지면 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);
}
최소한 위의 내용을 변경하고 싶습니다.
- 가변 길이 문자열을 사용합니다
- 임시 컨테이너가 필요하지 않습니다
데이터를 저장하는 방법에 대한 제한이 없으므로 예를 들어 화합물 더 나은 방법이있는 경우 데이터 유형.
편집하다: 문제를 좁히기 위해 C ++ 측의 데이터를 사용하는 것에 비교적 익숙합니다. 대부분의 도움이 필요한 HDF5 측입니다.
당신의 도움을 주셔서 감사합니다.
해결책
많은 감사합니다 더러워졌습니다 이에 대답하는 데 도움을주기 위해.
HDF5에서 가변 길이 문자열을 작성하려면 다음을 사용하십시오.
// 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 );
컨테이너를 작성하는 한 가지 해결책은 각 요소를 개별적으로 작성하는 것입니다. 이것은 사용하여 달성 할 수 있습니다 hyperslabs.
예를 들어:
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);
}
다른 팁
다음은 HDF5 C ++ API를 사용하여 가변 길이 문자열의 벡터를 작성하기위한 작업 코드입니다.
다른 게시물에 몇 가지 제안을 통합합니다.
- h5t_c_s1 및 h5t_variable을 사용하십시오
- 사용
string::c_str()
문자열에 대한 포인터를 얻습니다 - 포인터를 a에 넣으십시오
vector
의char*
HDF5 API로 전달합니다
그것은이다 필요하지 않습니다 비싼 문자열 사본을 만들려면 (예 : strdup()
). c_str()
기본 문자열의 NULL 종료 데이터에 대한 포인터를 반환합니다. 이것이 바로 기능이 의도 한 것입니다. 물론, 널이 포함 된 문자열은 이것과 함께 작동하지 않습니다 ...
std::vector
인접한 기본 저장소가 있으므로 사용하십시오. vector
그리고 vector::data()
원시 어레이를 사용하는 것과 동일하지만 물론 일을하는 구식 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());
}
}
클리너 코드를보고있는 경우 : 문자열을 가져 와서 HDF5 컨테이너 (원하는 모드)에 저장하는 functor를 작성하는 것이 좋습니다. Richard, 나는 잘못된 알고리즘을 사용했습니다. 다시 확인하십시오!
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() ?
}
};
그게 시작하는 데 도움이됩니까?
나는 비슷한 문제가 있었는데, 나는 문자열의 벡터를 기인하다. 속성이있는 까다로운 것은 hyperslabs (적어도 C ++ API)와 같은 멋진 데이터 스페이스 기능을 사용할 수 없다는 것입니다.
그러나 두 경우 모두 데이터 세트의 단일 항목에 문자열 벡터를 입력하는 것이 유용 할 수 있습니다 (예를 들어, 항상 함께 읽을 것으로 예상). 이 경우 모든 마법은 다음과 함께 제공됩니다 유형, 데이터 스페이스 자체가 아닙니다.
기본적으로 4 단계가 있습니다.
- 을 만들다
vector<const char*>
줄을 가리 킵니다. - a
hvl_t
벡터를 가리키고 길이를 포함하는 구조. - 데이터 유형을 만듭니다. 이것은
H5::VarLenType
래핑 (가변 길이)H5::StrType
. - 쓰기
hvl_t
데이터 세트에 입력하십시오.
이 방법의 정말 좋은 부분은 HDF5가 스칼라 값을 고려하는 내용으로 전체 항목을 채우는 것입니다. 이것은 데이터 세트가 아닌 속성으로 만드는 것이 사소한 것임을 의미합니다.
이 솔루션을 선택하든 자체 데이터 세트 항목에서 각 문자열이있는 솔루션을 선택하든 원하는 성능의 문제 일 것입니다. 특정 문자열에 대한 임의의 액세스를 찾고 있다면 데이터 세트에 문자열을 작성하는 것이 좋습니다. 그들은 색인화 될 수 있습니다. 항상 함께 읽으려면이 솔루션도 잘 작동 할 수 있습니다.
다음은 C ++ API 및 간단한 스칼라 데이터 세트를 사용 하여이 작업을 수행하는 방법에 대한 짧은 예입니다.
#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;
}
TempContainer 대신 간단한 std :: 벡터를 사용할 수 있습니다 (t-> basic_string과 일치하도록 템플릿을 만들 수도 있습니다.
#include <algorithm>
#include <vector>
#include <string>
#include <functional>
class StringToVector
: std::unary_function<std::vector<char>, std::string> {
public:
std::vector<char> operator()(const std::string &s) const {
// assumes you want a NUL-terminated string
const char* str = s.c_str();
std::size_t size = 1 + std::strlen(str);
// s.size() != strlen(s.c_str())
std::vector<char> buf(&str[0], &str[size]);
return buf;
}
};
void conv(const std::vector<std::string> &vi,
std::vector<std::vector<char> > &vo)
{
// assert vo.size() == vi.size()
std::transform(vi.begin(), vi.end(),
vo.begin(),
StringToVector());
}
능력을 갖기 위해 읽다 std::vector<std::string>
여기에 Leo의 힌트를 기반으로 솔루션을 게시하고 있습니다. https://stackoverflow.com/a/15220532/364818.
C와 C ++ API를 혼합했습니다. 자유롭게 편집하고 더 간단하게 만드십시오.
HDF5 API는 목록을 반환합니다 char*
읽기를 호출 할 때 포인터. 이것들 char*
사용 후 포인터를 풀어야합니다. 그렇지 않으면 메모리 누출이 있습니다.
사용 예제
H5::Attribute Foo = file.openAttribute("Foo");
std::vector<std::string> foos
Foo >> foos;
코드는 다음과 같습니다
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;
}
나는 파티에 늦었지만 segfaults에 관한 의견을 바탕으로 Leo Goodstadt의 답변을 수정했습니다. 나는 리눅스에 있지만 그런 문제가 없습니다. 나는 열린 h5file에서 주어진 이름의 데이터 세트에 std :: string의 벡터를 작성하고 다른 하나는 결과 데이터 세트를 std :: string의 벡터로 다시 읽기 위해 두 가지 함수를 썼습니다. 참고 유형간에 불필요한 복사가 몇 번 더 최적화 될 수 있습니다. 다음은 작성 및 읽기를위한 작업 코드입니다.
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());
}
}
그리고 읽기 :
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;
}
HDF5에 대해 잘 모르겠지만 사용할 수 있습니다.
struct TempContainer {
char* string;
};
그런 다음 문자열을이 방법으로 복사하십시오.
TempContainer t;
t.string = strdup(i->c_str());
tc.push_back (t);
이렇게하면 정확한 크기의 문자열을 할당하고 컨테이너에서 삽입하거나 읽을 때도 많은 개선이 개선됩니다 (예에서는 배열이 복사되어 있습니다.이 경우 포인터 만 있습니다). std :: 벡터를 사용할 수도 있습니다.
std::vector<char *> tc;
...
tc.push_back(strdup(i->c_str());