classdef UnifiedDataReader
% UNIFIEDDATAREADER 统一数据读取器,支持自动识别Excel/TXT文件格式
% 支持格式: .xlsx, .xls, .txt, .csv
% 使用示例:
% reader = UnifiedDataReader();
% data = reader.readFile(‘data.xlsx’, ‘Sheet1’, ‘A’); % Excel文件
% curves = reader.readFile(‘data.txt’, true); % 文本文件(带校验和)
methods (Static) function data = readFile(filename, varargin) % READFILE 自动识别文件类型并读取数据 % data = readFile(filename, ...) % % 参数说明: % Excel文件: readFile(filename, sheet, column, [Name-Value参数]) % sheet - 工作表名称/索引 % column - 列标识符(如'A'/'down_1_1')或列索引 % 'HeaderLines' - 跳过表头行数(默认0) % 'TrimBlanks' - 截断空白数据(默认true) % % 文本文件: readFile(filename, hasChecksum) % hasChecksum - 是否包含校验和(true/false) % 验证文件存在性 if ~isfile(filename) error('文件未找到: %s', filename); end % 获取文件扩展名并转为小写 [~, ~, ext] = fileparts(filename); ext = lower(ext); % 根据扩展名调用相应读取方法 switch ext case {'.xlsx', '.xls', '.csv'} % Excel格式处理 if nargin < 3 error('Excel文件需要提供sheet和column参数'); end sheet = varargin{1}; column = varargin{2}; data = UnifiedDataReader.readExcel(filename, sheet, column, varargin{3:end}); case {'.txt', '.dat'} % 文本格式处理 if nargin < 2 error('文本文件需要提供hasChecksum参数'); end hasChecksum = varargin{1}; data = UnifiedDataReader.readText(filename, hasChecksum); otherwise error('不支持的文件类型: %s', ext); end end function columnData = readExcel(filename, sheet, column, varargin) % READEXCEL 读取Excel文件(静态方法) % 继承原My_readExcelColumn功能,输出数值数组 % 解析可选参数 p = inputParser; addParameter(p, 'HeaderLines', 0, @isnumeric); addParameter(p, 'TrimBlanks', true, @islogical); parse(p, varargin{:}); % 创建导入选项 opts = detectImportOptions(filename, 'Sheet', sheet); % 列标识转换 if isnumeric(column) colIndex = column; else colIndex = find(strcmpi(opts.VariableNames, column), 1); if isempty(colIndex) colIndex = UnifiedDataReader.colLetter2num(column); end end % 验证列索引 if colIndex < 1 || colIndex > numel(opts.VariableNames) error('列索引 %d 超出范围', colIndex); end % 设置导入选项 columnName = opts.VariableNames{colIndex}; opts.SelectedVariableNames = {columnName}; if p.Results.HeaderLines > 0 opts.VariableNamesRange = ''; opts.DataRange = p.Results.HeaderLines + 1; end % 读取数据 dataTable = readtable(filename, opts); columnData = table2array(dataTable(:, 1)); % 截断空白数据 if p.Results.TrimBlanks columnData = UnifiedDataReader.trimBlanks(columnData); end end function curveData = readText(filename, hasChecksum) % READTEXT 读取文本文件(静态方法) % 基于优化版的parse_serial_data % 读取文件所有行 fid = fopen(filename, 'r'); if fid == -1 error('文件打开失败: %s', filename); end lines = textscan(fid, '%s', 'Delimiter', '\n', 'WhiteSpace', ''); fclose(fid); lines = lines{1}; numLines = numel(lines); % 初始化变量 firstLineCnt = []; curveData = {}; validLines = false(numLines, 1); % 第一遍扫描:确定曲线数量 for i = 1:numLines rawLine = strtrim(lines{i}); if isempty(rawLine), continue; end tokens = regexp(rawLine, '\s+', 'split'); if numel(tokens) < 3 || ~strcmpi(tokens{1}, 'AA') || ~strcmpi(tokens{2}, '55') continue; end validLines(i) = true; if isempty(firstLineCnt) firstLineCnt = hex2dec(tokens{3}); end end % 预分配内存 maxValidLines = sum(validLines); if isempty(firstLineCnt) || maxValidLines == 0 error('未找到有效数据行'); end curveData = cell(1, firstLineCnt); for j = 1:firstLineCnt curveData{j} = zeros(maxValidLines, 1); end % 第二遍扫描:处理数据 lineCount = 0; for i = 1:numLines if ~validLines(i), continue; end rawLine = strtrim(lines{i}); tokens = regexp(rawLine, '\s+', 'split'); tokenCount = numel(tokens); currentCnt = hex2dec(tokens{3}); lineCount = lineCount + 1; % 验证数据 expectedTokens = 3 + currentCnt*2 + double(hasChecksum); if tokenCount ~= expectedTokens warning('行 %d: 预期 %d token, 实际 %d', i, expectedTokens, tokenCount); continue; end % 处理数据段 for curveIdx = 1:currentCnt byteIdx = (curveIdx-1)*2 + 4; highByte = sscanf(tokens{byteIdx}, '%2x'); lowByte = sscanf(tokens{byteIdx+1}, '%2x'); % 组合16位值 value = bitor(bitshift(highByte, 8), lowByte); % 转换为有符号整数 if value > 32767 value = value - 65536; end curveData{curveIdx}(lineCount) = value; end % 校验和验证 if hasChecksum checksum = sscanf(tokens{end}, '%2x'); calcChecksum = 0; for j = 1:(numel(tokens)-1) byteVal = sscanf(tokens{j}, '%2x'); calcChecksum = mod(calcChecksum + byteVal, 256); end if checksum ~= calcChecksum warning('行 %d: 校验和错误 (计算: %d, 实际: %d)', i, calcChecksum, checksum); end end end % 修剪结果 for j = 1:length(curveData) curveData{j} = curveData{j}(1:lineCount); end end function colNum = colLetter2num(colLetters) % 列字母转数字索引 colLetters = upper(colLetters); colNum = 0; for i = 1:length(colLetters) charCode = colLetters(i); colNum = colNum * 26 + (charCode - 'A' + 1); end end function trimmedData = trimBlanks(dataArray) % 截断空白数据 (数值数组版本) nonNanIdx = ~isnan(dataArray); firstValid = find(nonNanIdx, 1, 'first'); lastValid = find(nonNanIdx, 1, 'last'); if isempty(firstValid) trimmedData = []; else trimmedData = dataArray(firstValid:lastValid); end end end
end
增加功能:
在读txt文件后,对比数据线的数量和帧头包含的数据线的数量是否可以对上,同时返回读取数据线的数量