读取文件的最低级别函数是(如Import Text Data Files with Low-Level I/O中所述):
在您的情况下,输入文件是ascii,而不是二进制文件,因此我们可以立即删除最后一个选项 ( fread
)。
剩下的是fgetl/fgets
(用于逐行读取文件,然后解析每一行)和fscanf
.
您已经使用逐行方法获得了两个答案,所以我不会详细说明这个答案,而是向您展示如何使用fscanf
(因为您的数据是合适的,它们确实是以格式化的模式组织的)。
使用 , 的好处fscanf
是,只要您使用正确的formatSpec
参数,该函数将能够一次读取整个文件,而不是逐行迭代。对于文件中的所有数字数据都是如此。我们将不得不对最后一列中的文本元素进行第二次传递。
定义格式说明符:
首先让我们定义您的格式规范。我们将为每个通道使用不同的格式。第一遍将读取所有数字数据,但会跳过文本字段,而第二遍则相反,忽略所有数字数据并仅读取文本字段。在定义格式说明符时,该'*'
字符非常有用:
DataFormatSpec = repmat('%d,',1,8) ;
DataFormatSpec = [DataFormatSpec '%*s'] ; % yield: '%d,%d,%d,%d,%d,%d,%d,%d,%*s'
TextFormatSpec = repmat('%*d,',1,8) ;
TextFormatSpec = [TextFormatSpec '%s'] ; % yield: '%*d,%*d,%*d,%*d,%*d,%*d,%*d,%*d,%s'
我已经使用%d
了所有列,因为在您的示例数据中我没有看到您提到的各种数字类型。如果数据需要,您可以轻松地替换%f
它,并且您可以在同一个说明符中毫无问题地混合两种类型(只要它们都是数字的)。只需使用对每列有意义的内容。
有了这些,让我们来看看文件。数据在标题行之后是格式化的模式,所以我们首先需要在调用之前通过标题行fscanf
。我们会像你一样做:
%% Open file and retrieve header line
fid = fopen( filein,'r') ;
hdr = fgetl(fid) ; % Retrieve the header line
DataStartIndex = ftell(fid) ; % save the starting index of data section
调用ftell
允许我们保存文件指针的位置。在我们读取标题之后,指针位于数据段的开头。我们保存它以便能够将文件指针倒回到第二遍读取的同一点。
读取数值数据:
这可以通过fscanf
一个简单的调用非常快速地完成:
%% First pass, read the "numeric values"
dataArray = fscanf( fid , DataFormatSpec , [8 Inf]).' ;
请注意行尾的转置运算符.'
。这是因为fscanf
填充它以列主要顺序读取的值,但文本文件中的数据以行主要顺序读取。最后的转置操作只是使输出数组的维度与文本文件中的相同。
现在dataArray
包含所有数字数据:
>> dataArray
dataArray =
18 8 318 150 3436 11 70 1
16 8 304 150 3433 12 70 1
读取文本数据:
这是它变得稍微复杂一些。fscanf
自动将文本字符转换为其 ascii 值。将其转换回实际字符很容易(使用函数char()
)。最大的障碍是,如果我们一口气读完所有文本字段,它们都将显示为连续的数字,但无法知道每个字符串在哪里停止以及下一个从哪里开始。为了克服这个问题,我们将逐行阅读,但仍然使用fscanf
:
%% Second pass, read the "text" values
fseek(fid,DataStartIndex,'bof') ; % Rewind file pointer to the start of data section
nRows = size(dataArray,1) ; % How many lines we'll need to read
textArray = cell(nRows,1) ; % pre allocate a cell array to receive the text column elements
for iline=1:nRows
textArray{iline,1} = char( fscanf(fid,TextFormatSpec,1).' ) ;
end
fclose(fid) ; % Close file
再次注意转置运算符.'
的使用,以及char()
. 现在textArray
是一个包含所有文本字段的单元格数组:
>> textArray
textArray =
'sampletext'
'sampletext2'
重组数据集:
就个人而言,我会将这两个数组分开,因为它们是每种数据类型(double
数字数据cell
的数组和字符串数组的数组)最优化的容器。但是,如果您需要将它们重新组合到一个数据结构中,您可以使用元胞数组:
%% Optional, merge data into cell array
FullArray = [num2cell(dataArray) textArray]
FullArray =
[18] [8] [318] [150] [3436] [11] [70] [1] 'sampletext'
[16] [8] [304] [150] [3433] [12] [70] [1] 'sampletext2'
或者你可以使用table
:
%% Optional, merge data into a table
T = array2table(dataArray) ;
T.text = textArray ;
T.Properties.VariableNames = [cellstr(reshape(sprintf('v%d',1:8),2,[]).') ; {'text'}] ;
这使:
T =
v1 v2 v3 v4 v5 v6 v7 v8 text
__ __ ___ ___ ____ __ __ __ _____________
18 8 318 150 3436 11 70 1 'sampletext'
16 8 304 150 3433 12 70 1 'sampletext2'
显然,如果您选择表格版本,请使用从标题中解析的变量名称,而不是我在本示例中使用的自动生成的变量名称。