I've been trying to obtain a variable from a text file which I read in within the Matlab workspace.
The file contains the following:
---------------------------------------------------------------
Surface Forces (referred to Sref,Cref,Bref about Xref,Yref,Zref)
Standard axis orientation, X fwd, Z down
Sref = 35.00 Cref = 2.4325 Bref = 14.5000
Xref = 18.5306 Yref = 0.0000 Zref = -0.7092
n Area CL CD Cm CY Cn Cl CDi CDv
1 35.263 0.6972 0.0138 4.8547 0.0040 0.0069 -0.2817 0.0138 0.0000 F27 WING
Surface Forces (referred to Ssurf, Cave about root LE on hinge axis)
n Ssurf Cave cl cd cdv cm_LE
1 35.263 2.432 0.6920 0.0137 0.0000 0.0000 F27 WING
---------------------------------------------------------------
I need the value below CL, in this case its 0.6972. I've tried using fopen and importdata without succes. The importdata just puts the whole file in a cell array with 9 rows and 1 column containing all strings. From there I dont't know how to proceed further.
With the fopen, I've tried to read the file line by line and to check whether he finds the CL string. He does find it but the value its gives is [].
Can anyone give me a tip? Thank you.
Use fgetl() to extract lines you don't need, then use fscanf() to read a line of data into a vector ('dataline'). Then you can access the individual elements of the vector.
Example based on your file:
Open and read file, discarding first 7 lines, including blank lines:
fid = fopen(filename, 'r')
for i = 1:7
oneline = fgetl(fid);
end
read 8th line of file; store in a vector of floats
dataline = fscanf(fid, ['%f' ])
assign third value of vector to 'CL'
CL = dataline(3)
fclose(fid)
CL
ans =
0.6972
If you have the luxury of having one of the newer versions of Matlab, then the following will work.
B = readtable('test.dat'),'Delimiter','\t');
c = regexp(B{9,:}, ' ','split');
CL_vec = c{1,1};
CL_cell = CL_vec(13);
Wing_CL = str2num(CL_cell{1,1});
Related
I have a function that takes data and imports that data into a text file. The issue that I am having is with formatting. I want to be able to set the width of the columns based on the widest array of characters in that column. So, in the code below I have labels and then data. My idea would be to take the length of each individually and find the largest value. Say the second column labels has 15 chars and that is longer than any data array, then I want to set the width of that column to 15 + 3 (white spaces) making it 18. If column 3 had a max of 8 chars for a member of data, then I would like to set the width to 11. I have found plenty of literature on fixed width, and I found that I could do '-*s', *width, colLabels; but I am having difficulty figuring out how to implement that.
Below is my code and it doesn't fail but it takes forever and then won't open because there is not enough memory. I have really tried to work through this to no avail.
Thanks in advance and if there is any other information I can provide, then let me know.
for col = 1:length(this.colLabels) % iterate through columns
colLen = length(this.colLabels{col}); % find the longest string in labels
v = max(this.data(:,col)); % find the longest double in data
n = num2str(v, '%.4f'); % precision of 4 after decimal place
dataLen = length(n);
% find max width for column and add white space
if colLen > dataLen
colWidth = colLen + 3;
else
colWidth = dataLen + 3;
end
% print it
fprintf(fid, '%-*s', this.colWidth, this.colLabels{col}); % write col position i
fprintf(fid, '\n');
fprintf(fid, '%-*s', this.colWidth, this.colUnits{col});% write unit position i
fprintf(fid, '\n');
fprintf(fid, '%-*s', this.colWidth, this.data(:,col)); % write all rows of data in column i
end
There are few places where you are making mistakes:
The size of number is not necessarily related to its size when printed. Consider 1.1234 and 1000, one of these is a larger string and the other is a larger number. This may or may not matter for your data ...
Two, it is best to use the correct format strings when printing to string. %s is for strings, not numbers.
Perhaps most importantly, text appears on multiple lines because of the newline character which ends one line and starts another. This means you essentially have to write one row at a time, not one column at a time.
I tend to prefer creating the text in memory then writing to a file. The following isn't the cleanest implementation but it works.
this.colLabels = {'test' 'cheese' 'variable' 'really long string'};
this.colUnits = {'ml' 'cm' 'C' 'kg'};
n_columns = length(this.colLabels);
%Fake data
this.data = reshape(1:n_columns*5,5,n_columns);
this.data(1) = 1.2345678;
this.data(5) = 1000; %larger number but smaller string
%Format as desired ...
string_data = arrayfun(#(x) sprintf('%g',x),this.data,'un',0);
string_data = [this.colLabels; this.colUnits; string_data];
%Add on newlines ...
%In newer versions you can use newline instead of char(10)
string_data(:,end+1) = {char(10)};
string_lengths = cellfun('length',string_data);
max_col_widths = max(string_lengths,[],1);
%In newer versions you can use singleton expansion, but beware
n_spaces_add = bsxfun(#minus,max_col_widths,string_lengths);
%left justify filling with spaces
final_strings = cellfun(#(x,y) [x blanks(y)],string_data,num2cell(n_spaces_add),'un',0);
%Optional delimiter between columns
%Don't add delimiter for last column or for newline column
final_strings(:,1:end-2) = cellfun(#(x) [x ', '],final_strings(:,1:end-2),'un',0);
%Let's skip last newline
final_strings{end,end} = '';
%transpose for next line so that (:) goes by row first, not column
%Normally (:) linearizes by column first
final_strings = final_strings';
%concatenate all cells together
entire_string = [final_strings{:}];
%Write this to disk fprintf(fid,'%s',entire_string);
The data in the text file is stored one line after the other, so you cannot write column by column. You need first to determine the width of the columns and write the label/unit header, then write all the data. All we need to have is a proper format string for fprintf: fixed width format and fprintf is extremely useful for exporting column delimited data.
The first part of the code is ok in order to determine the width of the columns (assuming the data only has positive samples). You only need to store it in an array.
nCol=length(this.colLabels);
colWidth = zeros(1,nCol);
for col = 1:nCol
colLen = length(this.colLabels{col}); % find the longest string in labels
v = max(this.data(:,col)); % find the longest double in data
n = num2str(v, '%.4f'); % precision of 4 after decimal place
dataLen = length(n);
% find max width for column and add white space
colWidth(col)=max(colLen,dataLen);
end
Now, we need to build format string for the labels and data, to use with sprintf. The format string will look like '%6s %8s %10s\n' for the header and '%6.4f %8.4f %10.4f\n' for the data.
fmtHeader=sprintf('%%%ds ',colWidth);
fmtData=sprintf('%%%d.4f ',colWidth);
%Trim the triple space at the end and add the newline
fmtHeader=[fmtHeader(1:end-3) '\n'];
fmtData =[fmtData(1:end-3) '\n'];
We use the fact that, when sprintf is given an array as input, it will iterate through all the values to produce a long string. We can use the same trick to write the data, but singe we write line by line and Matlab stores data in column major order, a transpose is necessary.
fid=fopen('myFile.txt');
fprintf(fid,fmtHeader,this.colLabels{:});
fprintf(fid,fmtHeader,this.colUnits{:});
fprintf(fid,fmtData,transpose(this.data));
fclose(fid);
For the headers, the cell can be converted to a comma separated list with {:}. This is the same as writing fprintf(fid,fmtHeader,this.colLabels{1},this.colLabels{2},...)
Using the same test data from #Jimbo 's answer and fid=1; to output the fprintf to the screen the code gives:
test cheese variable really long string
ml cm C kg
1.2346 6.0000 11.0000 16.0000
2.0000 7.0000 12.0000 17.0000
3.0000 8.0000 13.0000 18.0000
4.0000 9.0000 14.0000 19.0000
1000.0000 10.0000 15.0000 20.0000
Finally, the most compact version of the code is:
fid=1; %print to screen for test purpose
colWidth =max( cellfun(#length,this.colLabels(:)') , max(1+floor(log10(max(this.data,[],1))) , 1) + 5); %log10 to count digits, +5 for the dot and decimal digits ; works for data >=0 only
fprintf(fid,[sprintf('%%%ds ',colWidth(1:end-1)) sprintf('%%%ds\n',colWidth(end))],this.colLabels{:},this.colUnits{:}); %print header
fprintf(fid,[sprintf('%%%d.4f ',colWidth(1:end-1)) sprintf('%%%d.4f\n',colWidth(end))],this.data'); %print data
I need read values of a txt file with MATLAB, the file is here:
-0.933475 0.358642
-1 6.12323e-17
but I have some troubles with this value 6.12323e-17, in matlab when I read it, the vale is 0.0000
here is the MATLAB code:
close all; clear;
arquivo = fopen('fftOut.txt');
formatSpec = '%f %f';
sizeA = [2 inf];
X = fscanf(arquivo,formatSpec, sizeA);
X'
fclose(arquivo);
and the output is
-0.9335 0.3586
-1.0000 0.0000
How can I handle it 0.0000?
Thanks in advance
You are using %f which is fixed point. Use %e for exponential notation. Check out mathwork’s web site: http://www.mathworks.com/help/matlab/matlab_prog/formatting-strings.html?refresh=true
It's not 0, when matlab prints a matrix, it uses the same notation for all elements, so the printed value is truncated but the stored one isn't.
Try printing just the imaginary part of the second number.
I have a question regarding the importing of .txt files. The file is in the format below, the problem is that matlab does not seem to recognize the "new line" character indicators following every "$", so matlab just sees the 5th line as a continuous stream of data
Data Matlab sees:
01-24-2013 [6:01:53]
Kp (0070.0000)
Ki (0200.0000)
Kd (0009.0000)
$,0045,0044,0000.05,0011.53,0005.64,$,0045,0048,0000.04,0011.55,0005.66,$....etc
01-24-2013 [7:01:48]
Data Wordpad sees:
01-24-2013 [6:01:53]
Kp (0070.0000)
Ki (0200.0000)
Kd (0009.0000)
$,0045,0044,0000.05,0011.53,0005.64,
$,0045,0048,0000.04,0011.55,0005.66,
$, ....
I have no problem importing the format seen by "wordpad (re-saved with)" using "csvread" and skipping column 1, but for the raw .txt file "Data Matlab sees", I cant find a way to tell Matlab how to read. Ideally, I would like to tell Matlab to skip to Row-5, then start reading data and creating a new line in the matrix [nx5] every time it encounters a "$". Is there a way to detect the "$" and reformat the data into a usable matrix form?
Thanks!
I don't know how you managed to read this data as one line, but suppose you did and you want to split it. You can use the almighty regexp to for that:
C = regexp(str, '\$,', 'split');
Then turn the strings into numbers and convert everything into a matrix:
C = cellfun(#str2num, C, 'Uniform', false);
A = vertcat(C{:});
Regarding the second part of the question:
Ideally, I would like to tell Matlab to skip to Row-5, then start reading data...
You can make textread do that by using the 'headerlines' option:
C = textread('file.txt', '%s', 1, 'headerlines', 4, 'delimiter', '\n')
str = C{1};
and then use the code that employs regexp to split the string str.
Note that this will only work if MATLAB indeed "sees" the 5th line like you described. If not, you'll simply get only the first row in your matrix.
Example
str = '$,0045,0044,0000.05,0011.53,0005.64,$,0045,0048,0000.04,0011.55,0005.66';
C = cellfun(#str2num, regexp(str, '\$,', 'split'), 'Uniform', false);
A = vertcat(C{:})
This results in:
A =
45.0000 44.0000 0.0500 11.5300 5.6400
45.0000 48.0000 0.0400 11.5500 5.6600
I am attempting to read some data from a .cnv file. I can open the file with:
TopFolder = 'Name_of_my_file';
SubFolder = dir(fullfile(TopFolder,'*.cnv'));
fid = fopen(fullfile(TopFolder,SubFolder{i}));
All of the data is located following the string END, which is on a separate line to the other headers. I would like to import the data that is stored on the lines following this string. How can this be achieved?
For example, the a section of the .cnv file is as follows:
# datcnv_in = D:\data\110606_000.hex D:\instrument software\Seabird 2010-07\Seabird Con Files\SBE19_2108_ScufaTOBS.con
# datcnv_skipover = 0
# file_type = ascii
*END*
-0.051 0.0312 15.4328 138.1551 0.0000 0.0000 0.0000 0.0000 0.000e+00
-0.033 0.0305 15.4277 138.1551 0.0000 0.0000 0.0000 0.0000 0.000e+00
So, I would like to avoid those lines prior to End
Maybe a first step would be to find the line number of END? How would I do this?
First of all open the file and search through the lines until you find 'END'
fid = fopen('yourfile.cnv') % open file to read
fseek(fid,0,-1); % set read position to beginning of file
while strcmp(fgetl(fid),'*END*') == 0 end % go through lines until '*END*'
Next read data, line by line, into a matrix (data):
n=1;
while 1
tline = fgetl(fid) % read in line
if ~ischar(tline), break, end % if eof, break and finish
data(:,n) = sscanf(tline,'%f') % put numbers in a matrix (in columns)
n=n+1
end
fclose(fid) % close file
When using the import wizard in matlab you can specify the number of header rows.
If you set this to four, it should work in this example.
After using the import wizard you can let it generate code if you want to automate the process for the future.
Try this:
L = '';
while isempty(findstr(L,'*END*')) % if the line doesn't contain *END* => read a new line
L = fgetl(fid); % read next line of file
end
a = fscanf(fid,'%g %g %g %g'); % add as many %g as columns in your data
fclose(fid);
I've added some comments on how this works. Basically, this reads the opened file line by line until it finds a line containing *END*.
You could use strcmp(L,'*END*') if there is more than 1 line that may contain the same characters.
A warning: this code assumes that the file you are reading contains at least one line that has the *END* characters, if not, you'll get an error when attempting to read beyond the EOF.
Hope this helps.
I'm trying to load the following ascii file into MATLAB using load()
% some comment
1 0xc661
2 0xd661
3 0xe661
(This is actually a simplified file. The actual file I'm trying to load contains an undefined number of columns and an undefined number of comment lines at the beginning, which is why the load function was attractive)
For some strange reason, I obtain the following:
K>> data = load('testMixed.txt')
data =
1 50785
2 58977
3 58977
I've observed that the problem occurs anytime there's a "d" in the hexadecimal number.
Direct hex2dec conversion works properly:
K>> hex2dec('d661')
ans =
54881
importdata seems to have the same conversion issue, and so does the ImportWizard:
K>> importdata('testMixed.txt')
ans =
1 50785
2 58977
3 58977
Is that a bug, am I using the load function in some prohibited way, or is there something obvious I'm overlooking?
Are there workarounds around the problem, save from reimplementing the file parsing on my own?
Edited my input file to better reflect my actual file format. I had a bit oversimplified in my original question.
"GOLF" ANSWER:
This starts with the answer from mtrw and shortens it further:
fid = fopen('testMixed.txt','rt');
data = textscan(fid,'%s','Delimiter','\n','MultipleDelimsAsOne','1',...
'CommentStyle','%');
fclose(fid);
data = strcat(data{1},{' '});
data = sscanf([data{:}],'%i',[sum(isspace(data{1})) inf]).';
PREVIOUS ANSWER:
My first thought was to use TEXTSCAN, since it has an option that allows you to ignore certain lines as comments when they start with a given character (like %). However, TEXTSCAN doesn't appear to handle numbers in hexadecimal format well. Here's another option:
fid = fopen('testMixed.txt','r'); % Open file
% First, read all the comment lines (lines that start with '%'):
comments = {};
position = 0;
nextLine = fgetl(fid); % Read the first line
while strcmp(nextLine(1),'%')
comments = [comments; {nextLine}]; % Collect the comments
position = ftell(fid); % Get the file pointer position
nextLine = fgetl(fid); % Read the next line
end
fseek(fid,position,-1); % Rewind to beginning of last line read
% Read numerical data:
nCol = sum(isspace(nextLine))+1; % Get the number of columns
data = fscanf(fid,'%i',[nCol inf]).'; % Note '%i' works for all integer formats
fclose(fid); % Close file
This will work for an arbitrary number of comments at the beginning of the file. The computation to get the number of columns was inspired by Jacob's answer.
New:
This is the best I could come up with. It should work for any number of comment lines and columns. You'll have to do the rest yourself if there are strings, etc.
% Define the characters representing the start of the commented line
% and the delimiter
COMMENT_START = '%%';
DELIMITER = ' ';
% Open the file
fid = fopen('testMixed.txt');
% Read each line till we reach the data
l = COMMENT_START;
while(l(1)==COMMENT_START)
l = fgetl(fid);
end
% Compute the number of columns
cols = sum(l==DELIMITER)+1;
% Split the first line
split_l = regexp(l,' ','split');
% Read all the data
A = textscan(fid,'%s');
% Compute the number of rows
rows = numel(A{:})/cols;
% Close the file
fclose(fid);
% Assemble all the data into a matrix of cell strings
DATA = [split_l ; reshape(A{:},[cols rows])']; %' adding this to make it pretty in SO
% Recognize each column and process accordingly
% by analyzing each element in the first row
numeric_data = zeros(size(DATA));
for i=1:cols
str = DATA(1,i);
% If there is no '0x' present
if isempty(findstr(str{1},'0x')) == true
% This is a number
numeric_data(:,i) = str2num(char(DATA(:,i)));
else
% This is a hexadecimal number
col = char(DATA(:,i));
numeric_data(:,i) = hex2dec(col(:,3:end));
end
end
% Display the data
format short g;
disp(numeric_data)
This works for data like this:
% Comment 1
% Comment 2
1.2 0xc661 10 0xa661
2 0xd661 20 0xb661
3 0xe661 30 0xc661
Output:
1.2 50785 10 42593
2 54881 20 46689
3 58977 30 50785
OLD:
Yeah, I don't think LOAD is the way to go. You could try:
a = char(importdata('testHexa.txt'));
a = hex2dec(a(:,3:end));
This is based on both gnovice's and Jacob's answers, and is a "best of breed"
For files like:
% this is my comment
% this is my other comment
1 0xc661 123
2 0xd661 456
% surprise comment
3 0xe661 789
4 0xb661 1234567
(where the number of columns within the file MUST be the same, but not known ahead of time, and all comments denoted by a '%' character), the following code is fast and easy to read:
f = fopen('hexdata.txt', 'rt');
A = textscan(f, '%s', 'Delimiter', '\n', 'MultipleDelimsAsOne', '1', 'CollectOutput', '1', 'CommentStyle', '%');
fclose(f);
A = A{1};
data = sscanf(A{1}, '%i')';
data = repmat(data, length(A), 1);
for ctr = 2:length(A)
data(ctr,:) = sscanf(A{ctr}, '%i')';
end