3. CTD数据文件结构解析
CTD(温盐深)观测数据在归档、交换与再分析时,最常见的三种文件形态是 CNV、EDF 和 NetCDF。它们承载的物理量相同——电导率、温度、压力及其派生的盐度、密度、深度——但组织方式差异很大:CNV 是“头部元数据 + 列式数据表”的纯文本,EDF 是带固定字段说明的文本/二进制混合体,NetCDF 则是自描述的多维二进制容器。能否快速读懂一个文件,取决于你是否能在打开它的头几十行内定位到三件事:列的顺序与含义、每列的单位、缺失值如何标记。本节围绕这三条主线展开,并给出用文本编辑器与基础命令行工具查看结构的可操作方法。
一、CNV:头部键值对 + 列定义 + 数据区
CNV 文件(Sea-Bird 体系常用)是纯 ASCII 文本,结构上分为三段。第一段是头部,以 * 开头的注释行和 # 开头的系统行组成,其中 # name、# units、# bad_flag 等键值对最为关键。第二段是列定义区,由 # name 0 = prDM: Pressure 这类逐行声明构成,行号即列序号(从 0 开始)。第三段是数据区,以 *END* 行分隔,其后每行是一组以空格或逗号分隔的数值,列顺序严格对应列定义区。
查看 CNV 时,用文本编辑器打开后先跳到文件头部,重点抓取以下信息:# nquan(列数)、# nvalues(每列样本数)、# bad_flag(缺失值哨兵,常见为 -9.990e-29 或 -999)、# interval(采样间隔,秒)。列定义区里 # name 0 = prDM: Pressure [db] 同时给出了短名、长名和单位,这是判断列含义最直接的依据。若头部缺少 # units 行,则单位只能从列定义行的方括号中读取,二者需交叉核对。
# nquan = 4
# nvalues = 1200
# bad_flag = -9.990e-29
# interval = 0.0417
# name 0 = prDM: Pressure [db]
# name 1 = t090C: Temperature [ITS-90, deg C]
# name 2 = c0S/m: Conductivity [S/m]
# name 3 = sal00: Salinity [PSU]
*END*
1.02 12.345 3.210 31.02
1.05 12.341 3.208 31.01
实操上,不必逐行肉眼扫描。在 Linux/macOS 终端可用 head -n 40 file.cnv 看头部,用 grep -n "bad_flag\|name [0-9]" file.cnv 一次性抽出列定义与缺失值标记;在 Windows 下可用 PowerShell 的 Get-Content file.cnv -TotalCount 40。判断数据区是否完整,可对比 # nvalues 与实际数据行数(wc -l 减去头部行数),若不一致说明文件被截断或存在空行。
二、EDF:字段说明块与固定列宽
EDF(Export Data Format)常见于海洋观测数据交换,头部由若干以 // 或 # 引导的说明行组成,随后是列标题行,再往下是数据。与 CNV 不同,EDF 的列含义往往直接写在列标题中(如 Pressure(dbar) Temperature(degC) Salinity(PSU)),单位内嵌在括号里,缺失值通常用 NaN、-999 或空字段表示。查看时先确认列标题行位置,再检查是否存在多行表头或单位行与数据行混排的情况;若字段以固定宽度对齐,用文本编辑器的“显示空白字符”功能可避免把空格误判为分隔符。
三、NetCDF:自描述容器与变量属性
NetCDF 是二进制自描述格式,结构信息不在“文本行”里,而在变量属性中。用 ncdump -h file.nc 可只输出头部(维度、变量、属性),这是查看结构最快的方式。重点看三类属性:units(单位)、_FillValue 或 missing_value(缺失值)、long_name/standard_name(变量语义)。维度上要确认 time、depth 或 pressure 的记录长度,以及是否存在 scan 等辅助维度。若没有 ncdump,可用 Python 的 netCDF4.Dataset 或 xarray.open_dataset 打印 ds 概览,同样能读到变量名、单位和缺失值。
四、三要素的交叉核对与常见陷阱
三种格式的“列顺序—单位—缺失值”来源不同,误读往往发生在交叉处。CNV 的列序号从 0 开始,若把 # name 0 误当作第 1 列,后续所有列都会错位一列;EDF 的列标题若含空格(如 Temperature (degC)),按空格切分会把单位拆成独立字段;NetCDF 的 _FillValue 是属性而非数据值,若直接对原始数组求均值而不做掩码,缺失值会污染统计结果。一个稳妥的做法是:先打印每列的前 5 个值,与头部声明的单位做量级核对——压力应在 0~6000 dbar 量级,温度在 -2~35 ℃ 量级,盐度在 0~40 PSU 量级,电导率在 0~6 S/m 量级。若某列出现 -9.99e-29 或 NaN,即确认缺失值标记生效。
head、ncdump -h),避免把整个大文件载入编辑器导致卡顿;对 CNV 与 EDF,先用 grep 抽出列定义与缺失值行,再决定是否读取数据区。bad_flag 相同。Sea-Bird 不同采集配置下缺失值哨兵可能变化,混用文件前应逐文件核对头部,否则缺失值会被当作真实观测参与平均。把上述检查固化为一个可复用的动作序列:打开文件 → 定位头部 → 抽取列定义与单位 → 记录缺失值哨兵 → 抽样核对量级 → 与 nvalues 或维度长度比对。完成这一步,文件才真正“可读”,后续的盐度计算、深度换算与质量控制才有可靠起点。