【问题标题】:Save Struct with Armadillo Vec to hdf5 file将带有犰狳 Vec 的结构保存到 hdf5 文件
【发布时间】:2022-02-18 04:02:54
【问题描述】:

我正在尝试保存一个包含一些 arma::vec 的结构,但我不确定如何将它们写入 hdf5 文件。

#define ARMA_DONT_USE_WRAPPER
#include <armadillo>
#include <string>
#include <iostream>
#include "H5Cpp.h"
#define ARMA_ALLOW_FAKE_GCC  //allows the use of GCC compiled armadillo with ICC compiler

using namespace std;
using namespace arma;

struct info_struct{
    double frequency_MHz;
    vec beam_centres_deg;
    vec beam_crossover_deg;
    double task_centre_azimuth_deg;
    double WRF_Hz;
    double bandwidth_kHz;
    double beam_spacing_dB;
    int direct_wave_range_bin;
    double fs;
    vec rx_rng_twoway_vec_km;
    string date;
};

int main(void)
{
    info_struct header;

    header.frequency_MHz=7;
    header.beam_centres_deg=8*ones<vec>(10);
    header.beam_crossover_deg=9*ones<vec>(10);
    header.task_centre_azimuth_deg=10;
    header.WRF_Hz = 11;
    header.bandwidth_kHz=12;
    header.beam_spacing_dB=13;
    header.direct_wave_range_bin=14;
    header.fs=15e3;
    header.rx_rng_twoway_vec_km=16*ones<vec>(10);
    header.date="14-Oct-2020 17:02:30";

    // the array of each length of multidimentional data.
    hsize_t dim_header[1];

    dim_header[0] = sizeof(header) / sizeof(info_struct);

    // the length of dim
    int rank_header = sizeof(dim_header) / sizeof(hsize_t);

    // defining the datatype to pass HDF55
    H5::CompType h5_header(sizeof(info_struct));
    h5_header.insertMember("frequency_MHz", HOFFSET(info_struct, frequency_MHz), H5::PredType::NATIVE_DOUBLE);
    h5_header.insertMember("beam_centres_deg", HOFFSET(info_struct, beam_centres_deg), H5::PredType::NATIVE_DOUBLE);
    h5_header.insertMember("beam_crossover_deg", HOFFSET(info_struct, beam_crossover_deg), H5::PredType::NATIVE_DOUBLE);
    h5_header.insertMember("task_centre_azimuth_deg", HOFFSET(info_struct, task_centre_azimuth_deg), H5::PredType::NATIVE_DOUBLE);
    h5_header.insertMember("WRF_Hz", HOFFSET(info_struct, WRF_Hz), H5::PredType::NATIVE_DOUBLE);
    h5_header.insertMember("bandwidth_kHz", HOFFSET(info_struct, bandwidth_kHz), H5::PredType::NATIVE_DOUBLE);
    h5_header.insertMember("beam_spacing_dB", HOFFSET(info_struct, beam_spacing_dB), H5::PredType::NATIVE_DOUBLE);
    h5_header.insertMember("direct_wave_range_bin", HOFFSET(info_struct, direct_wave_range_bin), H5::PredType::NATIVE_INT);
    h5_header.insertMember("fs", HOFFSET(info_struct, fs), H5::PredType::NATIVE_DOUBLE);
    h5_header.insertMember("rx_rng_twoway_vec_km", HOFFSET(info_struct, rx_rng_twoway_vec_km), H5::PredType::NATIVE_DOUBLE);
    h5_header.insertMember("date", HOFFSET(info_struct, date), H5::StrType(H5::PredType::C_S1, H5T_VARIABLE));
    
    // preparation of a dataset and a file.
    H5::DataSpace space_header(rank_header, dim_header);
    H5::H5File *file = new H5::H5File("data_test.h5", H5F_ACC_TRUNC);
    H5::DataSet *dataset_header = new H5::DataSet(file->createDataSet("MOTHR_header", h5_header, space_header));
    // Write
    dataset_header->write(&header, h5_header);
    
    delete dataset_header;
    delete file;
    return 0;
}

如果我注释掉与向量有关的所有内容,它会很好地写入文件,但是当我不注释它们时,我会在编译时收到此警告:

In file included from /usr/include/unistd.h:226,
                 from /usr/include/armadillo:62,
                 from save_data.cpp:2:
save_data.cpp: In function ‘int main()’:
save_data.cpp:65:53: warning: offsetof within non-standard-layout type ‘info_struct’ is conditionally-supported [-Winvalid-offsetof]
     h5_header.insertMember("frequency_MHz", HOFFSET(info_struct, frequency_MHz), H5::PredType::NATIVE_DOUBLE);
save_data.cpp:65:45: note: in expansion of macro ‘HOFFSET’
     h5_header.insertMember("frequency_MHz", HOFFSET(info_struct, frequency_MHz), H5::PredType::NATIVE_DOUBLE);
                                             ^~~~~~~
save_data.cpp:67:56: warning: offsetof within non-standard-layout type ‘info_struct’ is conditionally-supported [-Winvalid-offsetof]
     h5_header.insertMember("beam_centres_deg", HOFFSET(info_struct, beam_centres_deg), H5::PredType::NATIVE_DOUBLE);
save_data.cpp:67:48: note: in expansion of macro ‘HOFFSET’
     h5_header.insertMember("beam_centres_deg", HOFFSET(info_struct, beam_centres_deg), H5::PredType::NATIVE_DOUBLE);
                                                ^~~~~~~
save_data.cpp:68:58: warning: offsetof within non-standard-layout type ‘info_struct’ is conditionally-supported [-Winvalid-offsetof]
     h5_header.insertMember("beam_crossover_deg", HOFFSET(info_struct, beam_crossover_deg), H5::PredType::NATIVE_DOUBLE);
save_data.cpp:68:50: note: in expansion of macro ‘HOFFSET’
     h5_header.insertMember("beam_crossover_deg", HOFFSET(info_struct, beam_crossover_deg), H5::PredType::NATIVE_DOUBLE);
                                                  ^~~~~~~
save_data.cpp:70:63: warning: offsetof within non-standard-layout type ‘info_struct’ is conditionally-supported [-Winvalid-offsetof]
     h5_header.insertMember("task_centre_azimuth_deg", HOFFSET(info_struct, task_centre_azimuth_deg), H5::PredType::NATIVE_DOUBLE);
save_data.cpp:70:55: note: in expansion of macro ‘HOFFSET’
     h5_header.insertMember("task_centre_azimuth_deg", HOFFSET(info_struct, task_centre_azimuth_deg), H5::PredType::NATIVE_DOUBLE);
                                                       ^~~~~~~
save_data.cpp:71:46: warning: offsetof within non-standard-layout type ‘info_struct’ is conditionally-supported [-Winvalid-offsetof]
     h5_header.insertMember("WRF_Hz", HOFFSET(info_struct, WRF_Hz), H5::PredType::NATIVE_DOUBLE);
save_data.cpp:71:38: note: in expansion of macro ‘HOFFSET’
     h5_header.insertMember("WRF_Hz", HOFFSET(info_struct, WRF_Hz), H5::PredType::NATIVE_DOUBLE);
                                      ^~~~~~~
save_data.cpp:72:53: warning: offsetof within non-standard-layout type ‘info_struct’ is conditionally-supported [-Winvalid-offsetof]
     h5_header.insertMember("bandwidth_kHz", HOFFSET(info_struct, bandwidth_kHz), H5::PredType::NATIVE_DOUBLE);
save_data.cpp:72:45: note: in expansion of macro ‘HOFFSET’
     h5_header.insertMember("bandwidth_kHz", HOFFSET(info_struct, bandwidth_kHz), H5::PredType::NATIVE_DOUBLE);
                                             ^~~~~~~
save_data.cpp:73:55: warning: offsetof within non-standard-layout type ‘info_struct’ is conditionally-supported [-Winvalid-offsetof]
     h5_header.insertMember("beam_spacing_dB", HOFFSET(info_struct, beam_spacing_dB), H5::PredType::NATIVE_DOUBLE);
save_data.cpp:73:47: note: in expansion of macro ‘HOFFSET’
     h5_header.insertMember("beam_spacing_dB", HOFFSET(info_struct, beam_spacing_dB), H5::PredType::NATIVE_DOUBLE);
                                               ^~~~~~~
save_data.cpp:74:61: warning: offsetof within non-standard-layout type ‘info_struct’ is conditionally-supported [-Winvalid-offsetof]
     h5_header.insertMember("direct_wave_range_bin", HOFFSET(info_struct, direct_wave_range_bin), H5::PredType::NATIVE_INT);
save_data.cpp:74:53: note: in expansion of macro ‘HOFFSET’
     h5_header.insertMember("direct_wave_range_bin", HOFFSET(info_struct, direct_wave_range_bin), H5::PredType::NATIVE_INT);
                                                     ^~~~~~~
save_data.cpp:75:42: warning: offsetof within non-standard-layout type ‘info_struct’ is conditionally-supported [-Winvalid-offsetof]
     h5_header.insertMember("fs", HOFFSET(info_struct, fs), H5::PredType::NATIVE_DOUBLE);
save_data.cpp:75:34: note: in expansion of macro ‘HOFFSET’
     h5_header.insertMember("fs", HOFFSET(info_struct, fs), H5::PredType::NATIVE_DOUBLE);
                                  ^~~~~~~
save_data.cpp:79:44: warning: offsetof within non-standard-layout type ‘info_struct’ is conditionally-supported [-Winvalid-offsetof]
     h5_header.insertMember("date", HOFFSET(info_struct, date), H5::StrType(H5::PredType::C_S1, H5T_VARIABLE));
save_data.cpp:79:36: note: in expansion of macro ‘HOFFSET’
     h5_header.insertMember("date", HOFFSET(info_struct, date), H5::StrType(H5::PredType::C_S1, H5T_VARIABLE));

而且该功能似乎运行良好,但是当我在生成的文件上尝试 h5dump 时,我得到:

HDF5 "data_test.h5" {
GROUP "/" {
   DATASET "MOTHR_header" {
      DATATYPE  H5T_COMPOUND {
         H5T_IEEE_F64LE "frequency_MHz";
         H5T_IEEE_F64LE "beam_centres_deg";
         H5T_IEEE_F64LE "beam_crossover_deg";
         H5T_IEEE_F64LE "task_centre_azimuth_deg";
         H5T_IEEE_F64LE "WRF_Hz";
         H5T_IEEE_F64LE "bandwidth_kHz";
         H5T_IEEE_F64LE "beam_spacing_dB";
         H5T_STD_I32LE "direct_wave_range_bin";
         H5T_IEEE_F64LE "fs";
         H5T_IEEE_F64LE "rx_rng_twoway_vec_km";
         H5T_STRING {
            STRSIZE H5T_VARIABLE;
            STRPAD H5T_STR_NULLTERM;
            CSET H5T_CSET_ASCII;
            CTYPE H5T_C_S1;
         } "date";
      }
      DATASPACE  SIMPLE { ( 1 ) / ( 1 ) }
      DATA {
      (0): {
            7,
            4.94066e-323,
            4.94066e-323,
            10,
            11,
            12,
            13,
            14,
            15000,
            4.94066e-323,
            "14-Oct-2020 17:02:30"
         }
      }
   }
}
}


我对 hdf5 很陌生,所以我完全不知道如何保存这些向量,任何帮助都将不胜感激。我还有另一个带有 arma:cx_mat 的结构。我知道在阅读它们时我必须单独阅读它们然后将它们组合起来,所以可以假设我必须为编写它们做同样的事情吗?

更新:

在阅读了有关复合数据类型的更多信息后,我对代码进行了修改:

#define ARMA_DONT_USE_WRAPPER
#include <armadillo>
#include <string>
#include <iostream>
#include "H5Cpp.h"
#define ARMA_ALLOW_FAKE_GCC  //allows the use of GCC compiled armadillo with ICC compiler

using namespace std;
using namespace arma;

typedef std::vector<double> stdvec;

struct info_struct{
    double frequency_MHz;
    vec beam_centres_deg;
    stdvec beam_centres_deg_c;
    vec beam_crossover_deg;
    stdvec beam_crossover_deg_c;
    double task_centre_azimuth_deg;
    double WRF_Hz;
    double bandwidth_kHz;
    double beam_spacing_dB;
    int direct_wave_range_bin;
    double fs;
    vec rx_rng_twoway_vec_km;
    stdvec rx_rng_twoway_vec_km_c;
    string date;
};

struct info_struct_hdf5{
    double frequency_MHz;
    double* beam_centres_deg;
    hvl_t beam_centres_deg_handle;
    double* beam_crossover_deg;
    hvl_t beam_crossover_deg_handle;
    double task_centre_azimuth_deg;
    double WRF_Hz;
    double bandwidth_kHz;
    double beam_spacing_dB;
    int direct_wave_range_bin;
    double fs;
    double* rx_rng_twoway_vec_km;
    hvl_t rx_rng_twoway_vec_km_handle;
    string date;
};



int main(void)
{
    info_struct header;
    info_struct_hdf5 header_hdf5;

    header.frequency_MHz=7;
    header.beam_centres_deg=8*ones<vec>(10);
    header.beam_crossover_deg=9*ones<vec>(10);
    header.task_centre_azimuth_deg=10;
    header.WRF_Hz = 11;
    header.bandwidth_kHz=12;
    header.beam_spacing_dB=13;
    header.direct_wave_range_bin=14;
    header.fs=15e3;
    header.rx_rng_twoway_vec_km=16*ones<vec>(10);
    header.date="14-Oct-2020 17:02:30";

    header_hdf5.frequency_MHz=header.frequency_MHz;
    header_hdf5.beam_centres_deg=header.beam_centres_deg.memptr();
    //header_hdf5.beam_crossover_deg=eader.beam_crossover_deg;
    header_hdf5.task_centre_azimuth_deg=header.task_centre_azimuth_deg;
    header_hdf5.WRF_Hz = header.WRF_Hz;
    header_hdf5.bandwidth_kHz=header.bandwidth_kHz;
    header_hdf5.beam_spacing_dB=header.beam_spacing_dB;
    header_hdf5.direct_wave_range_bin=header.direct_wave_range_bin;
    header_hdf5.fs=header.fs;
    //header_hdf5.rx_rng_twoway_vec_km=header.rx_rng_twoway_vec_km;
    header_hdf5.date=header.date;

    // the array of each length of multidimentional data.
    hsize_t dim_header[1];
    dim_header[0] = sizeof(header_hdf5) / sizeof(info_struct_hdf5);
    // the length of dim
    int rank_header = sizeof(dim_header) / sizeof(hsize_t);

    // defining the datatype to pass HDF55
    H5::CompType h5_header(sizeof(info_struct_hdf5));    
    h5_header.insertMember("frequency_MHz", HOFFSET(info_struct_hdf5, frequency_MHz), H5::PredType::NATIVE_DOUBLE);
    h5_header.insertMember("beam_centres_deg", HOFFSET(info_struct_hdf5, beam_centres_deg_handle), H5::VarLenType(H5::PredType::NATIVE_DOUBLE));
    h5_header.insertMember("beam_crossover_deg", HOFFSET(info_struct_hdf5, beam_crossover_deg_handle), H5::VarLenType(H5::PredType::NATIVE_DOUBLE));
    h5_header.insertMember("task_centre_azimuth_deg", HOFFSET(info_struct_hdf5, task_centre_azimuth_deg), H5::PredType::NATIVE_DOUBLE);
    h5_header.insertMember("WRF_Hz", HOFFSET(info_struct_hdf5, WRF_Hz), H5::PredType::NATIVE_DOUBLE);
    h5_header.insertMember("bandwidth_kHz", HOFFSET(info_struct_hdf5, bandwidth_kHz), H5::PredType::NATIVE_DOUBLE);
    h5_header.insertMember("beam_spacing_dB", HOFFSET(info_struct_hdf5, beam_spacing_dB), H5::PredType::NATIVE_DOUBLE);
    h5_header.insertMember("direct_wave_range_bin", HOFFSET(info_struct_hdf5, direct_wave_range_bin), H5::PredType::NATIVE_INT);
    h5_header.insertMember("fs", HOFFSET(info_struct_hdf5, fs), H5::PredType::NATIVE_DOUBLE);
    h5_header.insertMember("rx_rng_twoway_vec_km", HOFFSET(info_struct_hdf5, rx_rng_twoway_vec_km_handle), H5::VarLenType(H5::PredType::NATIVE_DOUBLE));
    h5_header.insertMember("date", HOFFSET(info_struct_hdf5, date), H5::StrType(H5::PredType::C_S1, H5T_VARIABLE));
    
    // preparation of a dataset and a file.
    H5::DataSpace space_header(rank_header, dim_header);
    H5::H5File *file = new H5::H5File("data_test.h5", H5F_ACC_TRUNC);
    H5::DataSet *dataset_header = new H5::DataSet(file->createDataSet("MOTHR_header", h5_header, space_header));
    // Write
    dataset_header->write(&header_hdf5, h5_header);
    
    delete dataset_header;
    delete file;
    return 0;
}

现在编译时出现警告或错误,所以我认为这至少是朝着正确方向迈出的一步,但是当我运行代码时,我得到:

HDF5-DIAG: Error detected in HDF5 (1.10.4) thread 140528006936384:
  #000: ../../../src/H5Dio.c line 336 in H5Dwrite(): can't write data
    major: Dataset
    minor: Write failed
  #001: ../../../src/H5Dio.c line 828 in H5D__write(): can't write data
    major: Dataset
    minor: Write failed
  #002: ../../../src/H5Dcontig.c line 633 in H5D__contig_write(): contiguous write failed
    major: Dataset
    minor: Write failed
  #003: ../../../src/H5Dscatgath.c line 704 in H5D__scatgath_write(): datatype conversion failed
    major: Dataset
    minor: Can't convert datatypes
  #004: ../../../src/H5T.c line 5025 in H5T_convert(): datatype conversion failed
    major: Datatype
    minor: Can't convert datatypes
  #005: ../../../src/H5Tconv.c line 2512 in H5T__conv_struct_opt(): unable to convert compound datatype member
    major: Datatype
    minor: Unable to initialize object
  #006: ../../../src/H5T.c line 5025 in H5T_convert(): datatype conversion failed
    major: Datatype
    minor: Can't convert datatypes
  #007: ../../../src/H5Tconv.c line 3272 in H5T__conv_vlen(): can't write VL data
    major: Datatype
    minor: Write failed
  #008: ../../../src/H5Tvlen.c line 913 in H5T_vlen_disk_write(): Unable to write VL information
    major: Datatype
    minor: Write failed
  #009: ../../../src/H5HG.c line 543 in H5HG_insert(): unable to allocate a global heap collection
    major: Heap
    minor: Unable to initialize object
  #010: ../../../src/H5HG.c line 163 in H5HG__create(): memory allocation failed
    major: Resource unavailable
    minor: No space available for allocation
  #011: ../../../src/H5FL.c line 922 in H5FL_blk_malloc(): memory allocation failed for chunk
    major: Resource unavailable
    minor: No space available for allocation
  #012: ../../../src/H5FL.c line 250 in H5FL_malloc(): memory allocation failed for chunk
    major: Resource unavailable
    minor: No space available for allocation
terminate called after throwing an instance of 'H5::DataSetIException'
Aborted (core dumped)

【问题讨论】:

  • 这只是错误/警告的一部分。请在问题中包含完整的编译器消息
  • 问题很可能是因为您告诉 HDF5 库犰狳向量“只是一个双倍”(insertMember 的最后一个参数)。您可能需要首先为犰狳向量定义一个新的复合数据类型。只有在那之后,您才能定义info_struct 的复合数据类型,因为您需要将犰狳矢量的相应复合数据类型指定为作为insertMember 的最后一个参数传递给vec 成员的“HDF5 类型” .
  • @darcamo 我看了一下这个post 并试图从那个开始工作(它关于阅读而不是写作)并且它现在编译现在的问题但是当我运行它时得到memory allocation error我将使用新代码和错误更新我的原始帖子
  • 这可能会变得很棘手,因为 HDF5 基本上是一个 C 库。一种解决方法是创建一个不同的结构,使用更简单的类型(使用 C 数组来存储原始犰狳矢量)并使用这个结构而不是原始结构来保存/加载到 HDF5。
  • @darcamo 所以这就是我正在尝试的。我使用double * 而不是arma::vec 创建了一个副本结构。 malloc() 对于 vec 的大小,然后是 memcpy(),这似乎有效,但是当我尝试保存它时,前面评论中提到的方式编译并运行时没有警告或错误,但是当我使用 h5dump查看文件,该条目是空白的。我想我又回到了insertmember() 的问题上,但我对 HDF5 非常陌生,所以我不确定下一步该去哪里......

标签: c++ hdf5 armadillo


【解决方案1】:

我终于明白了!

首先,两个非常相似的结构:

struct info_struct{
    double frequency_MHz;
    vec beam_centres_deg;
    vec beam_crossover_deg;
    double task_centre_azimuth_deg;
    double WRF_Hz;
    double bandwidth_kHz;
    double beam_spacing_dB;
    int direct_wave_range_bin;
    double fs;
    vec rx_rng_twoway_vec_km;
    string date;
};

struct info_struct_hdf5{
    double frequency_MHz;
    hvl_t beam_centres_deg_handle;
    hvl_t beam_crossover_deg_handle;
    double task_centre_azimuth_deg;
    double WRF_Hz;
    double bandwidth_kHz;
    double beam_spacing_dB;
    int direct_wave_range_bin;
    double fs;
    hvl_t rx_rng_twoway_vec_km_handle;
    string date;
};

接下来将数据从原始结构传输到这个新的缓冲区结构:

    header_hdf5.frequency_MHz=header.frequency_MHz;
    header_hdf5.beam_centres_deg_handle.p=header.beam_centres_deg.memptr();
    header_hdf5.beam_centres_deg_handle.len = header.beam_centres_deg.n_elem;
    header_hdf5.beam_crossover_deg_handle.p=header.beam_crossover_deg.memptr();
    header_hdf5.beam_crossover_deg_handle.len = header.beam_crossover_deg.n_elem;
    header_hdf5.task_centre_azimuth_deg=header.task_centre_azimuth_deg;
    header_hdf5.WRF_Hz = header.WRF_Hz;
    header_hdf5.bandwidth_kHz=header.bandwidth_kHz;
    header_hdf5.beam_spacing_dB=header.beam_spacing_dB;
    header_hdf5.direct_wave_range_bin=header.direct_wave_range_bin;
    header_hdf5.fs=header.fs;
    header_hdf5.rx_rng_twoway_vec_km_handle.p=header.rx_rng_twoway_vec_km.memptr();
    header_hdf5.rx_rng_twoway_vec_km_handle.len = header.rx_rng_twoway_vec_km.n_elem;
    header_hdf5.date=header.date;

创建一个新类型:

hid_t vlen_tid = H5Tvlen_create(H5T_NATIVE_DOUBLE);

然后使用缓冲区插入成员:

h5_header.insertMember("frequency_MHz", HOFFSET(info_struct_hdf5, frequency_MHz), H5::PredType::NATIVE_DOUBLE);
    h5_header.insertMember("beam_centres_deg", HOFFSET(info_struct_hdf5, beam_centres_deg_handle), vlen_tid);
    h5_header.insertMember("beam_crossover_deg", HOFFSET(info_struct_hdf5, beam_crossover_deg_handle), vlen_tid);
    h5_header.insertMember("task_centre_azimuth_deg", HOFFSET(info_struct_hdf5, task_centre_azimuth_deg), H5::PredType::NATIVE_DOUBLE);
    h5_header.insertMember("WRF_Hz", HOFFSET(info_struct_hdf5, WRF_Hz), H5::PredType::NATIVE_DOUBLE);
    h5_header.insertMember("bandwidth_kHz", HOFFSET(info_struct_hdf5, bandwidth_kHz), H5::PredType::NATIVE_DOUBLE);
    h5_header.insertMember("beam_spacing_dB", HOFFSET(info_struct_hdf5, beam_spacing_dB), H5::PredType::NATIVE_DOUBLE);
    h5_header.insertMember("direct_wave_range_bin", HOFFSET(info_struct_hdf5, direct_wave_range_bin), H5::PredType::NATIVE_INT);
    h5_header.insertMember("fs", HOFFSET(info_struct_hdf5, fs), H5::PredType::NATIVE_DOUBLE);
    h5_header.insertMember("rx_rng_twoway_vec_km", HOFFSET(info_struct_hdf5, rx_rng_twoway_vec_km_handle), vlen_tid);
    h5_header.insertMember("date", HOFFSET(info_struct_hdf5, date), H5::StrType(H5::PredType::C_S1, H5T_VARIABLE));

它对我很有效!

【讨论】:

    猜你喜欢
    • 2018-03-18
    • 1970-01-01
    • 1970-01-01
    • 2017-09-28
    • 2019-01-17
    • 2015-10-16
    • 1970-01-01
    • 2018-06-18
    • 1970-01-01
    相关资源
    最近更新 更多