Reading Files¶
This page covers opening HDF5 files, navigating their group hierarchy, and reading datasets and attributes back into Rust. The reading API is the same regardless of how a file is opened, so the patterns here apply equally to in-memory, on-disk, streaming, and SWMR reads.
Runnable example
The quickstart example builds a file in memory and reads it back, doubling as a self-check. Run it with:
The groups_and_attributes example walks a nested hierarchy. Run it with cargo run --example groups_and_attributes.
Opening a file¶
File is the entry point for reading. There are four ways to obtain one, all of which produce a value with an identical reading API:
| Constructor | Source | Notes |
|---|---|---|
File::open(path) |
A file on disk | Reads the whole file into memory. Requires the std filesystem. |
File::from_bytes(bytes) |
An in-memory Vec<u8> |
No filesystem needed — the in-memory path, e.g. for WebAssembly. |
File::open_streaming(path) |
A file on disk, read on demand | Fetches metadata and chunks lazily; never buffers the whole file. See Streaming. |
File::open_swmr(path) |
A file being appended to | Re-readable with refresh() to pick up new data. See SWMR. |
use hdf5_pure::File;
let file = File::open("output.h5").unwrap();
let ds = file.dataset("temperature").unwrap();
println!("shape: {:?}", ds.shape().unwrap()); // [3]
println!("data: {:?}", ds.read_f64().unwrap()); // [22.5, 23.1, 21.8]
To read from bytes that were never written to disk, for example the output of FileBuilder::finish (see Writing Files):
use hdf5_pure::{File, FileBuilder};
let mut builder = FileBuilder::new();
builder.create_dataset("x").with_f64_data(&[1.0, 2.0]);
let bytes = builder.finish().unwrap();
let file = File::from_bytes(bytes).unwrap();
let x = file.dataset("x").unwrap().read_f64().unwrap();
Tuning streaming reads
File::open_streaming_with_options, File::open_swmr_with_options, File::open_with_options, and File::from_bytes_with_options accept a FileAccessOptions to bound retained metadata and chunk-cache memory. See Streaming for MetadataCacheConfig and ChunkCacheConfig.
Opening datasets¶
File::dataset(path) resolves a dataset by its full path from the root, returning a Dataset:
use hdf5_pure::File;
let file = File::open("output.h5").unwrap();
let accel = file.dataset("sensors/imu/accel").unwrap();
A dataset can also be opened by name relative to its parent group via Group::dataset(name) (see Navigating groups below). To override the chunk cache for a single dataset, use File::dataset_with_options(path, DatasetAccessOptions).
Inspecting shape and datatype¶
Dataset::shape() returns the dimensions as a Vec<u64>. Two accessors describe the datatype: Dataset::dtype() returns a simplified DType classification, while Dataset::datatype() returns the full Datatype with exact field offsets and layout, which is useful for compound types (see Compound Types).
use hdf5_pure::File;
let file = File::open("output.h5").unwrap();
let ds = file.dataset("temperature").unwrap();
println!("shape: {:?}", ds.shape().unwrap());
println!("dtype: {:?}", ds.dtype().unwrap());
Reading dataset data¶
Typed reads¶
The read_* family delivers a dataset's elements as a flat Vec<T> of the requested type. Each method coerces the stored values to the requested type, so they are about how you want the data delivered rather than an assertion about the stored datatype.
| Method | Result |
|---|---|
read_f64 |
Vec<f64> |
read_f32 |
Vec<f32> |
read_i8 / read_i16 / read_i32 / read_i64 |
signed integers |
read_u8 / read_u16 / read_u32 / read_u64 |
unsigned integers |
read_string |
Vec<String> (fixed- and variable-length) |
read_raw |
Vec<u8> of the complete unfiltered record bytes |
use hdf5_pure::File;
let file = File::open("output.h5").unwrap();
let values = file.dataset("temperature").unwrap().read_f64().unwrap();
Generic reads¶
Dataset::read::<T>() is the generic counterpart to the typed read_* methods, bounded by the sealed H5Element trait (implemented for f32/f64 and the 8/16/32/64-bit signed and unsigned integers). It lets you write code generic over the element type. Like read_f64, it requests delivery as T and coerces, so pick T to match the stored type for a lossless read.
use hdf5_pure::{File, FileBuilder, H5Element, Error};
fn load<T: H5Element>(file: &File, name: &str) -> Result<Vec<T>, Error> {
file.dataset(name)?.read::<T>()
}
let mut fb = FileBuilder::new();
fb.create_dataset("counts").with_data(&[1u32, 2, 3]);
let file = File::from_bytes(fb.finish().unwrap()).unwrap();
let counts: Vec<u32> = load(&file, "counts").unwrap(); // [1, 2, 3]
For the writing side and more detail, see Generic I/O. For N-dimensional reads as ndarray arrays via read_array / read_array_dyn, see ndarray Support (needs the ndarray feature).
String reads¶
Dataset::read_string reads both fixed-length and variable-length HDF5 string datasets into a Vec<String>. When you need to bound variable-length payload allocation before reading, or to consume strings one at a time, use read_vlen_strings(VlenStringReadOptions) or visit_vlen_strings. See Variable-Length Strings.
Raw and compound reads¶
Dataset::read_raw returns the complete unfiltered record bytes, and Dataset::read_compound::<T>() decodes compound (struct-like) records. See Compound Types and the data types reference.
Navigating groups and attributes¶
File::root() returns the root Group, and File::group(path) resolves a subgroup by path. A Group lists its children with groups() and datasets() (each returning Vec<String> of names), opens a child dataset with dataset(name), and opens a child subgroup with group(name).
use hdf5_pure::File;
let file = File::open("output.h5").unwrap();
let sensors = file.group("sensors").unwrap();
println!("child groups: {:?}", sensors.groups().unwrap());
println!("datasets: {:?}", sensors.datasets().unwrap());
let pressure = sensors.dataset("pressure").unwrap();
Attributes are read with attrs(), available on both Group and Dataset. It returns a HashMap<String, AttrValue>:
use hdf5_pure::File;
let file = File::open("output.h5").unwrap();
let root_attrs = file.root().attrs().unwrap();
println!("version: {:?}", root_attrs.get("version")); // Some(I64(2))
let ds = file.dataset("temperature").unwrap();
println!("unit: {:?}", ds.attrs().unwrap().get("unit"));
See Groups and Attributes for the full AttrValue set and writing patterns.
Streaming attribute limits
Reading attributes is not yet supported on the File::open_streaming backend, which also resolves only latest-format (v2) groups along a path. In-memory reads (File::open / File::from_bytes) have neither limit. See Streaming.
Inspecting a file¶
Two free functions check whether bytes or a path look like an HDF5 file without fully opening it:
use hdf5_pure::{is_hdf5, is_hdf5_bytes};
let on_disk: bool = is_hdf5("output.h5").unwrap(); // io::Result<bool>
let in_memory: bool = is_hdf5_bytes(&bytes);
An open File reports its size and the format version it requires:
| Method | Returns |
|---|---|
File::file_size() |
u64 total byte length |
File::libver_bound() |
LibVer low bound implied by the superblock version |
libver_bound() mirrors the low bound of HDF5's H5Fget_libver_bounds: it returns the minimum library version needed to read the file, derived from its superblock version. The LibVer enum names the release boundaries at which the on-disk format changed:
| Variant | HDF5 release |
|---|---|
LibVer::Earliest |
1.0+ (version 0/1 superblock, v1 symbol-table groups) |
LibVer::V18 |
1.8 (version 2 superblock, new-style object headers) |
LibVer::V110 |
1.10 (version 3 superblock, SWMR, extensible/fixed array indices) |
LibVer::V112 |
1.12 |
LibVer::V114 |
1.14 |