简介:HDF(Hierarchical Data Format)是气象、遥感、空间科学等领域广泛使用的科学数据存储格式,Matlab中常通过hdfread函数从HDF或HDF-EOS文件中读取并提取数据。这份文档面向需要处理HDF文件的科研人员与工程师,系统整理了Matlab读取HDF文件的核心函数,帮助解决从HDF文件中读取数据时遇到的语法与参数选择难题。包内为一份doc文档,共1个文件,压缩包仅37KB,轻量便携,便于随时查阅。目前已有352人学习,可见其参考价值。文档详细说明了hdfread的基本语法DATA = HDFREAD(FILENAME,DATASETNAME),并详解Index、Rows、Cols、Bands、Interleave等参数选项的用途,覆盖子集提取、图像数据读取、SDS数据读取等多种场景;同时介绍如何与hdfinfo函数配合,先获取文件结构信息,再直接读取指定的数据集,并给出了读取8位栅格图像时返回图像数据与colormap的用法([DATA,MAP] = HDFREAD(...))。此外,文档还总结了hdfread高效、灵活、应用广泛等优点,并列举了气象、遥感、空间科学等典型应用案例,帮助读者快速掌握HDF数据读取的核心技巧。1. MATLAB读取HDF:先搞清楚你手里是HDF4还是HDF5拿到一个.hdf或.h5文件,第一反应当然是上网搜“matlab 读取hdf的函数”,然后照着代码敲,结果要么报错要么读出来一堆看不懂的数字。我最早用 MATLAB 碰 HDF 是在处理某次气象数据交换时,对方发来一批.hdf文件,我用h5read去读,直接报“文件无法识别”,后来才发现人家给的是 HDF4,函数家族都不一样。这个经历说明,读 HDF 的第一件事不是找函数,而是确定格式版本——HDF4 和 HDF5 在 MATLAB 里走的是两套完全不同的函数,连文件后缀都不能完全区分(HDF4 也可能是.hdf,HDF5 也可能是.h5或.hdf5)。本文就按这个思路展开:先讲怎么识别版本,再分别给出 HDF5 和 HDF4 的完整读取方案,最后补上批处理封装和高频踩坑点,让新手能照做,老手能直接拿去排查问题。2. HDF5读取:h5info与h5read是主力组合2.1 先用h5info看清文件结构:一键摸清数据布局HDF5 文件内部是一个层级结构,类似文件夹套文件。顶层是/,下面可以分 group,每个 group 里再挂 dataset(实际数据)和 attribute(数据属性)。直接用h5read去读,不给路径,MATLAB 根本不知道你要哪个数据集,所以第一步永远是h5info:% 查看HDF5文件完整结构 info = h5info('example.h5'); % 打印根组信息 disp(info);这段代码运行后,info里会列出Groups、Datasets、Attributes等字段。你看到的Datasets是所有根目录下的数据集名称,Groups是下一级分组。数据组织复杂时,建议递归查看:% 递归查看所有层级下的Dataset和Group function listH5Group(group, level) fprintf('%sGroup: %s\n', repmat(' ', 1, level), group.Name); if ~isempty(group.Datasets) for i = 1:length(group.Datasets) fprintf('%sDataset: %s\n', repmat(' ', 1, level+1), group.Datasets(i).Name); end end if ~isempty(group.Groups) for j = 1:length(group.Groups) listH5Group(group.Groups(j), level+1); end end end % 调用:从根组开始递归 listH5Group(info, 0);这里的repmat(' ', 1, level)用于缩进显示层级,看得更清晰。group.Datasets(i).Name给出的是数据集名称,而不是完整路径。实际读取时要在前面拼接父路径,比如'/group1/data1'。这一步的价值在于:你永远不会在h5read阶段因为路径写错而翻车。它的输出会告诉你一个文件里到底有几个数据集、叫什么名字、在哪个分组下面。2.2 h5read读取数据集:路径与切片一个都不能少确定路径后,用h5read读数据:% 读取根目录下的数据集 data = h5read('example.h5', '/temperature'); % 读取group1下的数据集,并按起始位置和步长切片 data_sub = h5read('example.h5', '/group1/precipitation', [1 1], [100 200], [1 2]);第二个参数是数据集完整路径,必须在h5info的输出中能直接对应。第三个参数[1 1]表示从每个维度的第一个元素开始读,第四个参数[100 200]表示每个维度读取的长度(按这个长度切块),第五个参数[1 2]是每个维度的步长,1和1表示第一维逐点,1和2表示第二维隔一个点取一个。如果只想读整块数据,第三个、第四个参数可以省略,直接调h5read('example.h5', '/dataset')。这里有个细节,HDF5 的维度顺序和 MATLAB 的数组维度顺序是不一致的——HDF5 按 C 语言的行优先存储,MATLAB 按列优先。比如 HDF5 里一个数据集的维度是[rows, cols],在 MATLAB 中读取后你会发现它的维度是[cols, rows]的转置关系。处理这类数据时,读取后直接做一次转置是常见做法:data = h5read('file.h5', '/image'); data = data'; % 转置为MATLAB习惯的行列这一步不做,后面做图像显示或矩阵运算时,图会翻转,矩阵逻辑也会对不上,是新手最容易忽略的一环。2.3 读取属性与子数据集:数据之外的元信息不能丢HDF5 的数据集往往带有属性,比如单位、时间戳、比例因子。这些信息不会跟数据一起读出来,需要单独用h5readatt:% 读取根组属性 attr1 = h5readatt('example.h5', '/', 'Units'); % 读取group1下数据集的属性 attr2 = h5readatt('example.h5', '/group1/precipitation', 'scale_factor');属性名大小写敏感,必须先通过h5info看准。属性值可能是字符数组,也可能是数值,读取后直接作为普通 MATLAB 变量使用即可。有些文件会存复合数据类型(类似结构体),读取时 MATLAB 会自动转为 cell 或 struct,不需要额外处理。需要说明的是,h5read不支持直接读取某个 group 下面所有数据集,必须逐个指定路径。当数据集特别多时,写一个循环读取所有路径是最高效的办法,这个我在第 4 章会给出完整封装方案。3. HDF4读取:hdfread与老函数的正确打开方式3.1 hdfinfo先识别:HDF4文件长什么样HDF4 是上世纪 90 年代到 2000 年代常用的格式,很多早期遥感数据、气象数据都长这样。MATLAB 中处理 HDF4 的函数族和 HDF5 完全不同,主入口同样是 info 函数:% 查看HDF4文件信息 info = hdfinfo('old_data.hdf'); % 查看根级信息 disp(info);hdfinfo返回的结构里会区分SDS(科学数据集)、Vdata(属性数据)和Vgroup(数据分组)。SDS 是核心的栅格数据,Vdata 通常是属性表或点数据,Vgroup 用于组织层级关系。你要找的核心数据一般都在 SDS 里。注意,hdfinfo和h5info返回结构完全不一样,别混用。有经验的用户会先用hdfinfo拿到SDS列表,然后逐个读取。处理 HDF4 的常见误区是拿h5read去读——HDF4 文件不是 HDF5,h5read 对它无能为力,报错信息往往是“不是有效的 HDF5 文件”。3.2 hdfread读取SDS与Vdata:沙场老将的参数细节HDF4 数据的读取函数是hdfread,它的用法比 h5read 更灵活,但参数也更绕:% 读取第一个SDS数据 SDS_info = info.SDS(1); data = hdfread(SDS_info); % 指定读取某个SDS data = hdfread('old_data.hdf', '/SDS_name');第一种用法直接传入hdfinfo返回的 SDS 结构体,最稳妥。第二种用法需要完整路径,路径中的名称必须和hdfinfo完全一致,大小写不同都会取不到数据。如果你想把数据读成切片,可以用hdfread的Index参数:% 只读取SDS中的部分数据:从第1行第1列开始,读取50行100列 data_partial = hdfread('old_data.hdf', '/SDS_name', 'Index', [1 1 50 100]);Index的四个值依次是起始行、起始列、读取行数、读取列数。相比h5read的切片参数,它更接近 MATLAB 数组索引的习惯。但有个坑,这个索引是按 HDF4 原始存储顺序来的,读取后同样要确认维度顺序是否和 MATLAB 一致。Vdata 的读取方式类似:% 读取Vdata信息 Vdata_info = info.Vdata(1); data = hdfread(Vdata_info);读出来的 Vdata 通常是一个元胞数组或结构体,每个字段对应对应的属性列。HDF4 的 Vdata 存属性表时,可能一个字段是时间,一个字段是观测值,读取后按列拆分即可。3.3 hdfreadatt读取属性与HDF-EOS数据HDF4 文件的属性信息用hdfreadatt读取:% 读取SDS属性 attr = hdfreadatt('old_data.hdf', '/SDS_name', 'Units'); % 读取文件全局属性 attr_global = hdfreadatt('old_data.hdf', '/', 'Title');这里的路径和名称同样大小写敏感,先hdfinfo看准再读。如果拿到的 HDF4 文件是 HDF-EOS 格式(很多遥感产品用这个),hdfinfo的 SDS 会带有EOS结构体,实际读取时依然用hdfread,不需要额外处理。HDF-EOS 数据的核心坑在于:它把常规的 SDS 名称包装成了类似/HDFEOS/GRIDS/...的长路径。很多人在这一步卡住,因为hdfinfo显示的名称和hdfread能识别的名称对不上。我的经验是:直接按hdfread返回的SDS_info.Name去读,别自己拼接路径,能绕开一半的玄学问题。4. 批处理与数据提取:把自己的读取流程封装成工具函数4.1 批量读取多个文件:循环、暂存与内存管理实际业务中很少只读一个文件,往往是几十个甚至几百个. hdf 文件要做批量处理。一个比较稳的批量读取框架是逐文件循环读取,数据量大时考虑分块处理:% 获取某个目录下所有HDF5文件,循环读取指定数据集 fileList = dir('data_dir/*.h5'); allData = cell(length(fileList), 1); for i = 1:length(fileList) filename = fullfile(fileList(i).folder, fileList(i).name); try allData{i} = h5read(filename, '/temperature'); fprintf('已读取 %s\n', filename); catch ME fprintf('读取失败 %s:%s\n', filename, ME.message); end end这里的try-catch很重要。批量读取时,某一个文件的数据集路径缺失或类型异常会导致整个脚本中断,有了try-catch就能把失败文件打出来,最后一次处理。对于遥感影像这类超大文件,一次读入可能内存不足,这时先读属性或小切片,确认数据结构后再按需读取关键区域。如果你面对的是 HDF4 老文件,批处理思路完全一致,只需要把h5read换成hdfread,把*.h5换成*.hdf。更好的做法是做一个自动识别版本的包装函数,这个放到第 6 章。4.2 从HDF中提取特定子数据集:两步过滤法有些文件里塞了几百个数据集,我们只关心其中几个。先看命名规律,再从h5info的结果里过滤目标名称,生成待读取路径清单,避免手写一堆路径:% 获取所有数据集路径 info = h5info('big_file.h5'); paths = listAllDatasetPaths(info); % 过滤出名字里带temperature的数据集 targetPaths = paths(contains(lower(paths), 'temperature')); % 按路径逐个读取 for i = 1:length(targetPaths) data = h5read('big_file.h5', targetPaths{i}); % 这里做后续处理 end % 定义递归获取所有数据集完整路径的函数 function paths = listAllDatasetPaths(group, prefix) if nargin < 2 prefix = ''; end paths = {}; for i = 1:length(group.Datasets) paths{end+1} = [prefix '/' group.Datasets(i).Name]; end for j = 1:length(group.Groups) subPaths = listAllDatasetPaths(group.Groups(j), [prefix '/' group.Groups(j).Name]); paths = [paths, subPaths]; end endcontains对路径大小写的处理比较宽松,但 HDF5 的数据集命名大小写敏感,如果在 Windows 上处理 Linux 生成的 HDF5 文件,会影响过滤结果。建议先把路径统一转成小写再匹配。这个方式在数据集的名称规律清晰时效率极高,几百个数据集只花几秒钟就能把所有目标数据批量读进来。4.3 数据集名称与路径的隐藏坑:空格与特殊字符HDF5 的数据集名称可以包含空格、中文、括号等特殊字符,这会让路径拼接出错。读取前先打印路径,直接复制h5info返回的完整路径名,尽量不要手工拼接。下面的写法能避免 90% 的路径错误:% 从info结构里直接拿路径,不手工拼接 datasetName = info.Datasets(3).Name; fullPath = ['/' datasetName]; data = h5read('file.h5', fullPath);如果路径中包含'/'以外的特殊字符,直接读没问题;如果路径中包含中文字符,要确认 MATLAB 的默认字符编码是 UTF-8,否则读出来可能是乱码。在较新版本的 MATLAB 中,h5read已经能较好处理中文路径,但老版本会有兼容性问题。曾有同事在处理某批中文命名的数据集时,代码一直报错,最后发现是编码问题,改用英文别名路径后一切正常。5. 读取HDF的6个高频坑:现象、原因与解决办法5.1 报错“无法识别文件”或“不是有效的HDF5文件”现象:用h5read读取.hdf后缀文件时,MATLAB 直接报错,说文件不是有效的 HDF5 文件。原因:文件实际是 HDF4 格式,函数家族用错了。解决:先用hdfinfo试试,能返回 SDS 信息就是 HDF4;或直接用hdfread读取。也可以先执行fileID = fopen('file.hdf', 'r'); fread(fileID, 8, 'uint8')'查看文件头,HDF5 的头 8 个字节是十六进制的89 48 44 46 0D 0A 1A 0A,HDF4 则完全不同。判断格式后再选函数,省掉一半排查时间。5.2 读出来的数据维度方向不对,图像是翻转的现象:读 HDF5 中的二维数组,用imagesc显示时发现图像上下颠倒或左右翻转。原因:HDF5 按 C 语言行优先存储,MATLAB 默认列优先,读取后维度顺序发生了变化。解决:读取后对数据进行转置操作,如data = data'。三维或更高维度数据转置时要permute(data, [2 1 3])这种写法,具体维度顺序按原始数据的 shape 来调整。HDF4 的读取同样存在此问题,但表现不如 HDF5 明显,建议读取后打印size(data)确认。5.3 数据集能读但全是0或者NaN,数值类型不对味现象:h5read读取后数据全是 0,或者出现大量 NaN。原因:文件中存储的是缩放后的整型数据,需要乘以比例因子并加偏移量才能还原真实值。很多遥感数据为了节省空间,把浮点数据量化成 uint16 或 int16,把真实比例因子存在属性里。解决:读取属性中的scale_factor和add_offset,读取数据后做还原计算:data = h5read('file.h5', '/reflectance'); scale = h5readatt('file.h5', '/reflectance', 'scale_factor'); offset = h5readatt('file.h5', '/reflectance', 'add_offset'); realData = double(data) * scale + offset;见过有人读出来全是 0 后直接放弃这个文件,是亏的。先查属性,此类问题十有八九能解决。5.4 中文字符串属性读出来是乱码现象:读取 HDF5 属性时,中文字符串出现乱码,英文字符串正常。原因:文件中的字符串编码是 UTF-8,而 MATLAB 老版本默认按本地编码(比如 GBK)解释字符串。解决:读取后统一转码:attr = h5readatt('file.h5', '/', 'Description'); % 若出现乱码,尝试转码 attr = native2unicode(uint8(attr), 'UTF-8');在较新版本 MATLAB 中,这个问题已经自动处理。老版本用户如果转码后发现还是不对,检查uint8(attr)的长度和字节内容,再尝试用unicode2native反向验证。5.5 用hdfread读HDF4总是提示找不到数据集现象:hdfread('file.hdf', '/SDS_name')报错无法找到数据集,但hdfinfo明明列出来了。原因:SDS 的完整路径不是简单的/SDS_name,而是嵌套在 Vgroup 下面,或名称里带了额外的层级信息。解决:读取hdfinfo返回的 SDS 结构体,然后直接把它传给hdfread:info = hdfinfo('file.hdf'); data = hdfread(info.SDS(1));不要手工拼路径,用结构体传参,这个处理能避开大多数路径坑。HDF-EOS 数据尤其如此,路径深层嵌套非常普遍。5.6 读取超大HDF5文件时内存溢出现象:一个文件几个 GB,h5read读完直接内存不足,MATLAB 无响应。原因:整块读入,内存吃不住。解决:分块读取。先读数据集的尺寸,再循环读取分片,按需处理:info = h5info('large_file.h5'); dims = info.Datasets(1).ChunkSize; % 查看chunk大小 data = h5read('large_file.h5', '/dataset', [1 1], [5000 5000], [1 1]);分批读完再拼接或直接在各分片上做统计。做遥感影像处理时,这是绕不开的环节。分块读的步长设置可以参考数据集本身的 chunk 大小,读起来内存和速度都比较均衡。6. 落地技巧:写一个自动识别格式的统一读取入口最后一章给你一个可以直接抄的实用技巧:封装一个统一入口,自动判断 HDF4 / HDF5,返回数据及元信息。以后换文件格式、换机器、换 MATLAB 版本,都不用改外层脚本。function [data, meta] = readHDFAuto(filename, datasetPath) % READHDFAUTO 自动识别HDF4/HDF5并读取数据 % 输入: % filename - HDF文件名 % datasetPath - 数据集路径(如 '/data') % 输出: % data - 读取的数据数组 % meta - 数据集属性结构体 % 检查扩展名和文件头 fileID = fopen(filename, 'r'); header = fread(fileID, 8, 'uint8')'; fclose(fileID); if isequal(header, [137 72 68 70 13 10 26 10]) % 是HDF5 data = h5read(filename, datasetPath); meta = struct(); info = h5info(filename, datasetPath); for i = 1:length(info.Attributes) attName = info.Attributes(i).Name; attVal = h5readatt(filename, datasetPath, attName); meta.(attName) = attVal; end else % 假定是HDF4 info = hdfinfo(filename); targetSDS = findSDSByName(info, datasetPath); if isempty(targetSDS) error('未找到匹配的SDS: %s', datasetPath); end data = hdfread(targetSDS); meta = targetSDS.Attributes; end end function sds = findSDSByName(info, name) % 按名称查找SDS,支持模糊匹配 sds = []; for i = 1:length(info.SDS) if contains(info.SDS(i).Name, name) sds = info.SDS(i); return; end end end这个工具的要点有三个:文件头判断格式、属性一并读出、HDF4 的 SDS 用模糊匹配找。实际用下来,能覆盖日常 80% 的场景。文件头判断比扩展名判断可靠得多,这一步做完就不会因为后缀名不规范而走弯路。属性一并读出可以后续直接做数据还原,不用再单独调一次属性读取。验证封装是否可靠,建议拿一个已知标准样本测一遍:HDF5 文件读出来的数据和直接用h5read读的全等(用isequal判断),HDF4 文件读出来的数据和直接用hdfread读的全等。自测通过后,再遇到新文件基本可以放心交给这个入口。我的习惯是把这套函数存成一个readHDFAuto.m,放在 MATLAB 的搜索路径下,任何脚本都能直接调用。以后换项目、换数据源,只改数据集路径,逻辑层完全不动。希望这些方法能在你拿到 HDF 文件时少绕几个弯,直接落到数据处理本身,而不是跟格式较劲。希望帮到你。本文还有配套的精品资源,点击获取
简介:HDF(Hierarchical Data Format)是气象、遥感、空间科学等领域广泛使用的科学数据存储格式,Matlab中常通过hdfread函数从HDF或HDF-EOS文件中读取并提取数据。这份文档面向需要处理HDF文件的科研人员与工程师,系统整理了Matlab读取HDF文件的核心函数,帮助解决从HDF文件中读取数据时遇到的语法与参数选择难题。包内为一份doc文档,共1个文件,压缩包仅37KB,轻量便携,便于随时查阅。目前已有352人学习,可见其参考价值。文档详细说明了hdfread的基本语法DATA = HDFREAD(FILENAME,DATASETNAME),并详解Index、Rows、Cols、Bands、Interleave等参数选项的用途,覆盖子集提取、图像数据读取、SDS数据读取等多种场景;同时介绍如何与hdfinfo函数配合,先获取文件结构信息,再直接读取指定的数据集,并给出了读取8位栅格图像时返回图像数据与colormap的用法([DATA,MAP] = HDFREAD(...))。此外,文档还总结了hdfread高效、灵活、应用广泛等优点,并列举了气象、遥感、空间科学等典型应用案例,帮助读者快速掌握HDF数据读取的核心技巧。1. MATLAB读取HDF:先搞清楚你手里是HDF4还是HDF5拿到一个.hdf或.h5文件,第一反应当然是上网搜“matlab 读取hdf的函数”,然后照着代码敲,结果要么报错要么读出来一堆看不懂的数字。我最早用 MATLAB 碰 HDF 是在处理某次气象数据交换时,对方发来一批.hdf文件,我用h5read去读,直接报“文件无法识别”,后来才发现人家给的是 HDF4,函数家族都不一样。这个经历说明,读 HDF 的第一件事不是找函数,而是确定格式版本——HDF4 和 HDF5 在 MATLAB 里走的是两套完全不同的函数,连文件后缀都不能完全区分(HDF4 也可能是.hdf,HDF5 也可能是.h5或.hdf5)。本文就按这个思路展开:先讲怎么识别版本,再分别给出 HDF5 和 HDF4 的完整读取方案,最后补上批处理封装和高频踩坑点,让新手能照做,老手能直接拿去排查问题。2. HDF5读取:h5info与h5read是主力组合2.1 先用h5info看清文件结构:一键摸清数据布局HDF5 文件内部是一个层级结构,类似文件夹套文件。顶层是/,下面可以分 group,每个 group 里再挂 dataset(实际数据)和 attribute(数据属性)。直接用h5read去读,不给路径,MATLAB 根本不知道你要哪个数据集,所以第一步永远是h5info:% 查看HDF5文件完整结构 info = h5info('example.h5'); % 打印根组信息 disp(info);这段代码运行后,info里会列出Groups、Datasets、Attributes等字段。你看到的Datasets是所有根目录下的数据集名称,Groups是下一级分组。数据组织复杂时,建议递归查看:% 递归查看所有层级下的Dataset和Group function listH5Group(group, level) fprintf('%sGroup: %s\n', repmat(' ', 1, level), group.Name); if ~isempty(group.Datasets) for i = 1:length(group.Datasets) fprintf('%sDataset: %s\n', repmat(' ', 1, level+1), group.Datasets(i).Name); end end if ~isempty(group.Groups) for j = 1:length(group.Groups) listH5Group(group.Groups(j), level+1); end end end % 调用:从根组开始递归 listH5Group(info, 0);这里的repmat(' ', 1, level)用于缩进显示层级,看得更清晰。group.Datasets(i).Name给出的是数据集名称,而不是完整路径。实际读取时要在前面拼接父路径,比如'/group1/data1'。这一步的价值在于:你永远不会在h5read阶段因为路径写错而翻车。它的输出会告诉你一个文件里到底有几个数据集、叫什么名字、在哪个分组下面。2.2 h5read读取数据集:路径与切片一个都不能少确定路径后,用h5read读数据:% 读取根目录下的数据集 data = h5read('example.h5', '/temperature'); % 读取group1下的数据集,并按起始位置和步长切片 data_sub = h5read('example.h5', '/group1/precipitation', [1 1], [100 200], [1 2]);第二个参数是数据集完整路径,必须在h5info的输出中能直接对应。第三个参数[1 1]表示从每个维度的第一个元素开始读,第四个参数[100 200]表示每个维度读取的长度(按这个长度切块),第五个参数[1 2]是每个维度的步长,1和1表示第一维逐点,1和2表示第二维隔一个点取一个。如果只想读整块数据,第三个、第四个参数可以省略,直接调h5read('example.h5', '/dataset')。这里有个细节,HDF5 的维度顺序和 MATLAB 的数组维度顺序是不一致的——HDF5 按 C 语言的行优先存储,MATLAB 按列优先。比如 HDF5 里一个数据集的维度是[rows, cols],在 MATLAB 中读取后你会发现它的维度是[cols, rows]的转置关系。处理这类数据时,读取后直接做一次转置是常见做法:data = h5read('file.h5', '/image'); data = data'; % 转置为MATLAB习惯的行列这一步不做,后面做图像显示或矩阵运算时,图会翻转,矩阵逻辑也会对不上,是新手最容易忽略的一环。2.3 读取属性与子数据集:数据之外的元信息不能丢HDF5 的数据集往往带有属性,比如单位、时间戳、比例因子。这些信息不会跟数据一起读出来,需要单独用h5readatt:% 读取根组属性 attr1 = h5readatt('example.h5', '/', 'Units'); % 读取group1下数据集的属性 attr2 = h5readatt('example.h5', '/group1/precipitation', 'scale_factor');属性名大小写敏感,必须先通过h5info看准。属性值可能是字符数组,也可能是数值,读取后直接作为普通 MATLAB 变量使用即可。有些文件会存复合数据类型(类似结构体),读取时 MATLAB 会自动转为 cell 或 struct,不需要额外处理。需要说明的是,h5read不支持直接读取某个 group 下面所有数据集,必须逐个指定路径。当数据集特别多时,写一个循环读取所有路径是最高效的办法,这个我在第 4 章会给出完整封装方案。3. HDF4读取:hdfread与老函数的正确打开方式3.1 hdfinfo先识别:HDF4文件长什么样HDF4 是上世纪 90 年代到 2000 年代常用的格式,很多早期遥感数据、气象数据都长这样。MATLAB 中处理 HDF4 的函数族和 HDF5 完全不同,主入口同样是 info 函数:% 查看HDF4文件信息 info = hdfinfo('old_data.hdf'); % 查看根级信息 disp(info);hdfinfo返回的结构里会区分SDS(科学数据集)、Vdata(属性数据)和Vgroup(数据分组)。SDS 是核心的栅格数据,Vdata 通常是属性表或点数据,Vgroup 用于组织层级关系。你要找的核心数据一般都在 SDS 里。注意,hdfinfo和h5info返回结构完全不一样,别混用。有经验的用户会先用hdfinfo拿到SDS列表,然后逐个读取。处理 HDF4 的常见误区是拿h5read去读——HDF4 文件不是 HDF5,h5read 对它无能为力,报错信息往往是“不是有效的 HDF5 文件”。3.2 hdfread读取SDS与Vdata:沙场老将的参数细节HDF4 数据的读取函数是hdfread,它的用法比 h5read 更灵活,但参数也更绕:% 读取第一个SDS数据 SDS_info = info.SDS(1); data = hdfread(SDS_info); % 指定读取某个SDS data = hdfread('old_data.hdf', '/SDS_name');第一种用法直接传入hdfinfo返回的 SDS 结构体,最稳妥。第二种用法需要完整路径,路径中的名称必须和hdfinfo完全一致,大小写不同都会取不到数据。如果你想把数据读成切片,可以用hdfread的Index参数:% 只读取SDS中的部分数据:从第1行第1列开始,读取50行100列 data_partial = hdfread('old_data.hdf', '/SDS_name', 'Index', [1 1 50 100]);Index的四个值依次是起始行、起始列、读取行数、读取列数。相比h5read的切片参数,它更接近 MATLAB 数组索引的习惯。但有个坑,这个索引是按 HDF4 原始存储顺序来的,读取后同样要确认维度顺序是否和 MATLAB 一致。Vdata 的读取方式类似:% 读取Vdata信息 Vdata_info = info.Vdata(1); data = hdfread(Vdata_info);读出来的 Vdata 通常是一个元胞数组或结构体,每个字段对应对应的属性列。HDF4 的 Vdata 存属性表时,可能一个字段是时间,一个字段是观测值,读取后按列拆分即可。3.3 hdfreadatt读取属性与HDF-EOS数据HDF4 文件的属性信息用hdfreadatt读取:% 读取SDS属性 attr = hdfreadatt('old_data.hdf', '/SDS_name', 'Units'); % 读取文件全局属性 attr_global = hdfreadatt('old_data.hdf', '/', 'Title');这里的路径和名称同样大小写敏感,先hdfinfo看准再读。如果拿到的 HDF4 文件是 HDF-EOS 格式(很多遥感产品用这个),hdfinfo的 SDS 会带有EOS结构体,实际读取时依然用hdfread,不需要额外处理。HDF-EOS 数据的核心坑在于:它把常规的 SDS 名称包装成了类似/HDFEOS/GRIDS/...的长路径。很多人在这一步卡住,因为hdfinfo显示的名称和hdfread能识别的名称对不上。我的经验是:直接按hdfread返回的SDS_info.Name去读,别自己拼接路径,能绕开一半的玄学问题。4. 批处理与数据提取:把自己的读取流程封装成工具函数4.1 批量读取多个文件:循环、暂存与内存管理实际业务中很少只读一个文件,往往是几十个甚至几百个. hdf 文件要做批量处理。一个比较稳的批量读取框架是逐文件循环读取,数据量大时考虑分块处理:% 获取某个目录下所有HDF5文件,循环读取指定数据集 fileList = dir('data_dir/*.h5'); allData = cell(length(fileList), 1); for i = 1:length(fileList) filename = fullfile(fileList(i).folder, fileList(i).name); try allData{i} = h5read(filename, '/temperature'); fprintf('已读取 %s\n', filename); catch ME fprintf('读取失败 %s:%s\n', filename, ME.message); end end这里的try-catch很重要。批量读取时,某一个文件的数据集路径缺失或类型异常会导致整个脚本中断,有了try-catch就能把失败文件打出来,最后一次处理。对于遥感影像这类超大文件,一次读入可能内存不足,这时先读属性或小切片,确认数据结构后再按需读取关键区域。如果你面对的是 HDF4 老文件,批处理思路完全一致,只需要把h5read换成hdfread,把*.h5换成*.hdf。更好的做法是做一个自动识别版本的包装函数,这个放到第 6 章。4.2 从HDF中提取特定子数据集:两步过滤法有些文件里塞了几百个数据集,我们只关心其中几个。先看命名规律,再从h5info的结果里过滤目标名称,生成待读取路径清单,避免手写一堆路径:% 获取所有数据集路径 info = h5info('big_file.h5'); paths = listAllDatasetPaths(info); % 过滤出名字里带temperature的数据集 targetPaths = paths(contains(lower(paths), 'temperature')); % 按路径逐个读取 for i = 1:length(targetPaths) data = h5read('big_file.h5', targetPaths{i}); % 这里做后续处理 end % 定义递归获取所有数据集完整路径的函数 function paths = listAllDatasetPaths(group, prefix) if nargin < 2 prefix = ''; end paths = {}; for i = 1:length(group.Datasets) paths{end+1} = [prefix '/' group.Datasets(i).Name]; end for j = 1:length(group.Groups) subPaths = listAllDatasetPaths(group.Groups(j), [prefix '/' group.Groups(j).Name]); paths = [paths, subPaths]; end endcontains对路径大小写的处理比较宽松,但 HDF5 的数据集命名大小写敏感,如果在 Windows 上处理 Linux 生成的 HDF5 文件,会影响过滤结果。建议先把路径统一转成小写再匹配。这个方式在数据集的名称规律清晰时效率极高,几百个数据集只花几秒钟就能把所有目标数据批量读进来。4.3 数据集名称与路径的隐藏坑:空格与特殊字符HDF5 的数据集名称可以包含空格、中文、括号等特殊字符,这会让路径拼接出错。读取前先打印路径,直接复制h5info返回的完整路径名,尽量不要手工拼接。下面的写法能避免 90% 的路径错误:% 从info结构里直接拿路径,不手工拼接 datasetName = info.Datasets(3).Name; fullPath = ['/' datasetName]; data = h5read('file.h5', fullPath);如果路径中包含'/'以外的特殊字符,直接读没问题;如果路径中包含中文字符,要确认 MATLAB 的默认字符编码是 UTF-8,否则读出来可能是乱码。在较新版本的 MATLAB 中,h5read已经能较好处理中文路径,但老版本会有兼容性问题。曾有同事在处理某批中文命名的数据集时,代码一直报错,最后发现是编码问题,改用英文别名路径后一切正常。5. 读取HDF的6个高频坑:现象、原因与解决办法5.1 报错“无法识别文件”或“不是有效的HDF5文件”现象:用h5read读取.hdf后缀文件时,MATLAB 直接报错,说文件不是有效的 HDF5 文件。原因:文件实际是 HDF4 格式,函数家族用错了。解决:先用hdfinfo试试,能返回 SDS 信息就是 HDF4;或直接用hdfread读取。也可以先执行fileID = fopen('file.hdf', 'r'); fread(fileID, 8, 'uint8')'查看文件头,HDF5 的头 8 个字节是十六进制的89 48 44 46 0D 0A 1A 0A,HDF4 则完全不同。判断格式后再选函数,省掉一半排查时间。5.2 读出来的数据维度方向不对,图像是翻转的现象:读 HDF5 中的二维数组,用imagesc显示时发现图像上下颠倒或左右翻转。原因:HDF5 按 C 语言行优先存储,MATLAB 默认列优先,读取后维度顺序发生了变化。解决:读取后对数据进行转置操作,如data = data'。三维或更高维度数据转置时要permute(data, [2 1 3])这种写法,具体维度顺序按原始数据的 shape 来调整。HDF4 的读取同样存在此问题,但表现不如 HDF5 明显,建议读取后打印size(data)确认。5.3 数据集能读但全是0或者NaN,数值类型不对味现象:h5read读取后数据全是 0,或者出现大量 NaN。原因:文件中存储的是缩放后的整型数据,需要乘以比例因子并加偏移量才能还原真实值。很多遥感数据为了节省空间,把浮点数据量化成 uint16 或 int16,把真实比例因子存在属性里。解决:读取属性中的scale_factor和add_offset,读取数据后做还原计算:data = h5read('file.h5', '/reflectance'); scale = h5readatt('file.h5', '/reflectance', 'scale_factor'); offset = h5readatt('file.h5', '/reflectance', 'add_offset'); realData = double(data) * scale + offset;见过有人读出来全是 0 后直接放弃这个文件,是亏的。先查属性,此类问题十有八九能解决。5.4 中文字符串属性读出来是乱码现象:读取 HDF5 属性时,中文字符串出现乱码,英文字符串正常。原因:文件中的字符串编码是 UTF-8,而 MATLAB 老版本默认按本地编码(比如 GBK)解释字符串。解决:读取后统一转码:attr = h5readatt('file.h5', '/', 'Description'); % 若出现乱码,尝试转码 attr = native2unicode(uint8(attr), 'UTF-8');在较新版本 MATLAB 中,这个问题已经自动处理。老版本用户如果转码后发现还是不对,检查uint8(attr)的长度和字节内容,再尝试用unicode2native反向验证。5.5 用hdfread读HDF4总是提示找不到数据集现象:hdfread('file.hdf', '/SDS_name')报错无法找到数据集,但hdfinfo明明列出来了。原因:SDS 的完整路径不是简单的/SDS_name,而是嵌套在 Vgroup 下面,或名称里带了额外的层级信息。解决:读取hdfinfo返回的 SDS 结构体,然后直接把它传给hdfread:info = hdfinfo('file.hdf'); data = hdfread(info.SDS(1));不要手工拼路径,用结构体传参,这个处理能避开大多数路径坑。HDF-EOS 数据尤其如此,路径深层嵌套非常普遍。5.6 读取超大HDF5文件时内存溢出现象:一个文件几个 GB,h5read读完直接内存不足,MATLAB 无响应。原因:整块读入,内存吃不住。解决:分块读取。先读数据集的尺寸,再循环读取分片,按需处理:info = h5info('large_file.h5'); dims = info.Datasets(1).ChunkSize; % 查看chunk大小 data = h5read('large_file.h5', '/dataset', [1 1], [5000 5000], [1 1]);分批读完再拼接或直接在各分片上做统计。做遥感影像处理时,这是绕不开的环节。分块读的步长设置可以参考数据集本身的 chunk 大小,读起来内存和速度都比较均衡。6. 落地技巧:写一个自动识别格式的统一读取入口最后一章给你一个可以直接抄的实用技巧:封装一个统一入口,自动判断 HDF4 / HDF5,返回数据及元信息。以后换文件格式、换机器、换 MATLAB 版本,都不用改外层脚本。function [data, meta] = readHDFAuto(filename, datasetPath) % READHDFAUTO 自动识别HDF4/HDF5并读取数据 % 输入: % filename - HDF文件名 % datasetPath - 数据集路径(如 '/data') % 输出: % data - 读取的数据数组 % meta - 数据集属性结构体 % 检查扩展名和文件头 fileID = fopen(filename, 'r'); header = fread(fileID, 8, 'uint8')'; fclose(fileID); if isequal(header, [137 72 68 70 13 10 26 10]) % 是HDF5 data = h5read(filename, datasetPath); meta = struct(); info = h5info(filename, datasetPath); for i = 1:length(info.Attributes) attName = info.Attributes(i).Name; attVal = h5readatt(filename, datasetPath, attName); meta.(attName) = attVal; end else % 假定是HDF4 info = hdfinfo(filename); targetSDS = findSDSByName(info, datasetPath); if isempty(targetSDS) error('未找到匹配的SDS: %s', datasetPath); end data = hdfread(targetSDS); meta = targetSDS.Attributes; end end function sds = findSDSByName(info, name) % 按名称查找SDS,支持模糊匹配 sds = []; for i = 1:length(info.SDS) if contains(info.SDS(i).Name, name) sds = info.SDS(i); return; end end end这个工具的要点有三个:文件头判断格式、属性一并读出、HDF4 的 SDS 用模糊匹配找。实际用下来,能覆盖日常 80% 的场景。文件头判断比扩展名判断可靠得多,这一步做完就不会因为后缀名不规范而走弯路。属性一并读出可以后续直接做数据还原,不用再单独调一次属性读取。验证封装是否可靠,建议拿一个已知标准样本测一遍:HDF5 文件读出来的数据和直接用h5read读的全等(用isequal判断),HDF4 文件读出来的数据和直接用hdfread读的全等。自测通过后,再遇到新文件基本可以放心交给这个入口。我的习惯是把这套函数存成一个readHDFAuto.m,放在 MATLAB 的搜索路径下,任何脚本都能直接调用。以后换项目、换数据源,只改数据集路径,逻辑层完全不动。希望这些方法能在你拿到 HDF 文件时少绕几个弯,直接落到数据处理本身,而不是跟格式较劲。希望帮到你。本文还有配套的精品资源,点击获取