MATLAB 读取大型文本数据文件的方法
很多测试数据存储在大型文本文件(如 CSV)中。考虑采样率为 1kHz 的情况,持续采集几分钟就能产生百万行数据文件。
本文考虑 csv 文件,其特征在于:
- 具有标题行,有的场景下标题行数目不固定
- 每行数据列数固定,分隔符固定(比如逗号或分号)
本文介绍提升 MATLAB 读取大型文本数据文件性能的方法。
读取大文件:使用 textscan 替代 readtable
考虑如下 csv 文件:
1 | # Data acquisition time, Epoch time (ms), Distance1 (μm), Distance2 (μm), Distance3 (μm), Distance4 (μm) |
初始代码使用 readtable 读取数据:
1 | %% 设置导入选项并导入数据 |
历时 1.919952 秒。
textscan 是底层 C 实现,比 readtable 快 2-3倍。
1 | % 打开文件 |
历时 0.650829 秒。(用时减少 66%)
历时 0.168380 秒。(考虑数据解析的总用时减少 57%)
优化文件打开参数
数据基本上只由 ASCII 字符组成(数字、逗号、换行等),我们可以优化 fopen 的参数,这里显式指定机器格式为 'native',避免做不必要的字节序转换;同时将字符编码固定为 US-ASCII,跳过自动编码检测,从而进一步提升性能:
1 | fid = fopen(filePath, 'r', 'n', 'US-ASCII'); |
| 编码方案 | 耗时 | 相对性能 |
|---|---|---|
| 不指定(自动检测) | ~1.0s | 基准 |
| GB18030 | 0.75s | +25% |
| UTF-8 | 0.70s | +30% |
| US-ASCII | 0.65s | +35% ⭐ |
处理变长标题行
有时标题行位置不固定,比如随测量通道数变化:
1 | # softwareVersion: 1.2.3.4 |
这时我们当然希望能动态定位标题行,而不是手动数行。注意到“真正的”标题行是:
1 | # Data acquisition time, Epoch time (ms), Distance1 (μm), Distance2 (μm), Distance3 (μm), Distance4 (μm) |
容易想到的做法是逐行读取,直到找到该行:
1 | fid = fopen(filePath, 'r', 'n', 'US-ASCII'); |
Found header line.
历时 0.786832 秒。
更快的方法是使用 fread 一次性读取整个文件内容,然后使用 strfind 在内存中搜索标题行:
1 | fid = fopen(filePath, 'r', 'n', 'US-ASCII'); |
历时 0.052721 秒。
其他
-
如果数据中包含空数据(即连续分隔符),需要在
textscan中指定'EmptyValue', NaN以正确处理。 -
如果可能重复读取同一文件,考虑将数据缓存到 MAT 文件中以加快后续读取速度。
1
2
3
4
5
6
7
8
9
10[fileDir, fileBase] = fileparts(filePath);
matPath = fullfile(fileDir, [fileBase '.mat']);
if exist(matPath, 'file')
cache = load(matPath, 'data', 'vars');
data = cache.data;
vars = cache.vars;
else
[data, vars] = loaderFunc(filePath);
save(matPath, 'data', 'vars');
end