Matlab fwrite precision conversion - matlab

I want to write some data to a binary file in single precision. The data is originally in double precision. Is there any difference between converting the data to single by calling the single command before fwrite and just letting Matlab do the conversion in the fwrite call?
Case 1
data1 % double precision
fwrite(fid,data1,'single');
Case 2
data2=single(data1);
fwrite(fid,data2,'single');
In the 2nd case, is Matlab doing any modifications to data2 before writing it since it is already in single format ? Will there be any difference in data written to the two files ?

Let's try this:
data1 = 1.555555555555555555;
data2 = single(data1);
fid = fopen('C:\Some\Address\data1.bin', 'w');
fwrite(fid, data1, 'single');
fclose(fid);
fid = fopen('C:\Some\Address\data2.bin', 'w');
fwrite(fid, data2, 'single');
fclose(fid);
% Lets read them back (note that MATLAB stores them in a double-precision variable by default)
fid = fopen('C:\Some\Address\data1.bin', 'r');
data1 = fread(fid, 'single');
fclose(fid);
fid = fopen('C:\Some\Address\data2.bin', 'r');
data2 = fread(fid, 'single');
fclose(fid);
format long;
[data1 data2] % or use fprintf to see the values
ans =
1.555555582046509 1.555555582046509
To your questions:
In the 2nd case, is Matlab doing any modifications to data2 before
writing it since it is already in single format ?
I don't think so but cannot be confident without knowing what is going on under the hood of fwrite.
Will there be any difference in data written to the two files ?
According to the test above I don't believe so,

You should use formatSpec to specify the format:
Like this
A = [6.6,1.11111];
formatSpec = '%4.5f'; % modify it accordingly.
fprintf(formatSpec ,A);

Related

fread and fwrite in matlab not exact

I would like to save float numbers in a binary file and read them afterwards for further processing. Unfortunately, fwrite and afterwards fread does change the number.
Following simple example:
% Number to store
A = 0.123456789101112
% Generate and open txt file
fid = fopen('test_fread.txt','w','b');
% write A into test_fread.txt
fwrite(fid,A,'float32');
% close file
fclose(fid)
% open txt file
fid = fopen('test_fread.txt','r','b');
% read the file
fread(fid,'float32')
ans = 0.123456791043282
The answer is different than the input. How can I fix this? What should I search for? Is it rounding, precision or another problem?
Floating point numbers are never exact. single precision floats (float32) only have 6-9 decimals of precision. For the pure decimal case this translates to the issue that you're seeing. The effect is more exaggerated if you also have an integer component:
% Sample number
A = 123456789.123456789;
% Write, rewind, and read back in
fID = fopen('test_fread.txt', 'w+', 'b');
fwrite(fID, A, 'float32');
frewind(fID);
B = fread(fID,'float32');
fclose(fID);
fprintf('A: %15.15f\nB: %15.15f\n', A, B);
Which returns:
A: 123456789.123456790000000
B: 123456792.000000000000000
Note that MATLAB casts B as a double here.
MATLAB's default data type, double (float64) has double the bits available, which will give you 15-17 significant decimal digits. Using the previous example we can try:
% Sample number
A = 123456789.123456789;
% Write, rewind, and read back in
fID = fopen('test_fread.txt', 'w+', 'b');
fwrite(fID, A, 'float64');
frewind(fID);
B = fread(fID,'float64');
fclose(fID);
fprintf('A: %15.15f\nB: %15.15f\n', A, B);
Which returns:
A: 123456789.123456790000000
B: 123456789.123456790000000
Yay.
If you need better precision in MATLAB than double you will need to look into using vpa, which is part of the Symbolic Math Toolbox, or a similar package that offers higher/variable precision.

How to read a single character in file using MATLAB?

In my file data.txt, I have a string abcdefgh. Now I want to take just 1 character without read whole string. How can I do this in MATLAB?
For example, I want to take the first character, I use c = fscanf(data.txt, '%c'); and c = textscan(data.txt, '%c'); but it read whole line in data.txt. I know that c(1) is my answer but I don't want to do that.
You can limit the number of characters that are read in using the third input to either fscanf or textscan.
fid = fopen('data.txt', 'r');
c = fscanf(fid, '%c', 1);
c = textscan(fid, '%c', 1);
You could also just use a lower-level function such as fread to do this.
fid = fopen('data.txt', 'r');
c = fread(fid, 1, '*char');

Modifying PDF file with Matlab by fopen

Is it possible to use matlab to fopen a PDF file, manually replace a string ('Helvetica') with a new string ('Arial')? Probably due to the fact that the file is part binary and part ascii, if I
fid = fopen(filename, 'r');
str = fread(fid, '*char')';
fclose(fid);
newStr = strrep(str, 'Helvetica', 'Arial');
fid = fopen(filename, 'w');
fprintf(fid, '%s', newStr);
fclose(fid);
The PDF will be unusable at all. Is there a way to avoid this?
PS: 1) The PDF file may have very different sizes, so skipping a certain amount of binary data may be difficult;
2) I know how to do it in python, but I'd really like to see whether it could be done by pure MATLAB...
Thanks!
One way of doing this is to read the pdf as uint8 instead of char and write out with fwrite
fid = fopen(filename, 'r');
bytes = fread(fid, 'uint8')';
fclose(fid);
% Do the replacement
% NB: strrep complains about the byte array but works anyway
% You could do replacement without using string function
% but this works.
output = strrep(bytes,'Helvetica','Arial');
% Write out the modified pdf
fid = fopen(filename, 'w');
fwrite(fid, output);
fclose(fid);

Reading CSV with mixed type data

I need to read the following csv file in MATLAB:
2009-04-29 01:01:42.000;16271.1;16271.1
2009-04-29 02:01:42.000;2.5;16273.6
2009-04-29 03:01:42.000;2.599609;16276.2
2009-04-29 04:01:42.000;2.5;16278.7
...
I'd like to have three columns:
timestamp;value1;value2
I tried the approaches described here:
Reading date and time from CSV file in MATLAB
modified as:
filename = 'prova.csv';
fid = fopen(filename, 'rt');
a = textscan(fid, '%s %f %f', ...
'Delimiter',';', 'CollectOutput',1);
fclose(fid);
But it returs a 1x2 cell, whose first element is a{1}='ÿþ2', the other are empty.
I had also tried to adapt to my case the answers to these questions:
importing data with time in MATLAB
Read data files with specific format in matlab and convert date to matal serial time
but I didn't succeed.
How can I import that csv file?
EDIT After the answer of #macduff i try to copy-paste in a new file the data reported above and use:
a = textscan(fid, '%s %f %f','Delimiter',';');
and it works.
Unfortunately that didn't solve the problem because I have to process csv files generated automatically, which seems to be the cause of the strange MATLAB behavior.
What about trying:
a = textscan(fid, '%s %f %f','Delimiter',';');
For me I get:
a =
{4x1 cell} [4x1 double] [4x1 double]
So each element of a corresponds to a column in your csv file. Is this what you need?
Thanks!
Seems you're going about it the right way. The example you provide poses no problems here, I get the output you desire. What's in the 1x2 cell?
If I were you I'd try again with a smaller subset of the file, say 10 lines, and see if the output changes. If yes, then try 100 lines, etc., until you find where the 4x1 cell + 4x2 array breaks down into the 1x2 cell. It might be that there's an empty line or a single empty field or whatever, which forces textscan to collect data in an additional level of cells.
Note that 'CollectOutput',1 will collect the last two columns into a single array, so you'll end up with 1 cell array of 4x1 containing strings, and 1 array of 4x2 containing doubles. Is that indeed what you want? Otherwise, see #macduff's post.
I've had to parse large files like this, and I found I didn't like textscan for this job. I just use a basic while loop to parse the file, and I use datevec to extract the timestamp components into a 6-element time vector.
%% Optional: initialize for speed if you have large files
n = 1000 %% <# of rows in file - if known>
timestamp = zeros(n,6);
value1 = zeros(n,1);
value2 = zeros(n,1);
fid = fopen(fname, 'rt');
if fid < 0
error('Error opening file %s\n', fname); % exit point
end
cntr = 0
while true
tline = fgetl(fid); %% get one line
if ~ischar(tline), break; end; % break out of loop at end of file
cntr = cntr + 1;
splitLine = strsplit(tline, ';'); %% split the line on ; delimiters
timestamp(cntr,:) = datevec(splitLine{1}, 'yyyy-mm-dd HH:MM:SS.FFF'); %% using datevec to parse time gives you a standard timestamp vector
value1(cntr) = splitLine{2};
value2(cntr) = splitLine{3};
end
%% Concatenate at the end if you like
result = [timestamp value1 value2];

Reading a text file and plotting it in 3D

I want to read in a text file that contains some strings but mostly numbers. I want to be able to ignore the strings and only look at the numbers. I want to plot those values on a 3D plane. The data looks like this:
Tech4:<152.266724,173.189377,27.995975>
<117.880638,156.116531,27.999983>
<129.849899,59.195660,27.999983>
<249.321121,60.605404,27.999983>
<224.120361,139.072739,28.000668>
<171.188950,143.490921,56.933430>
<171.188950,143.490921,83.548088>
<171.188950,143.490921,27.999985>
I believe to read in a file is just:
File = textread('testFile.txt');
How can I only look at those values and then plot it.
Thanks!
fid = fopen([pathname,filename]);
tline = fgetl(fid);
CX = [];
CY = [];
CZ = [];
while ischar(tline)
% skip < and >
tline = substr(tline, 1, length(tline)-2)
% extract numbers
temp = textscan(tline,'%n%n%n', 'delimiter',',');
CX(end+1,:) = [temp(1)];
CY(end+1,:) = [temp(2)];
CZ(end+1,:) = [temp(3)];
tline = fgetl(fid);
end
fclose(fid);
and then plot it using
plot3(CX, CY, CZ)
function call.
Add the check for "Tech4:" at the beginning however...
I think you can also directly use textscan in a one-liner:
fid = fopen('testFile.txt');
data = textscan(fid,'%*s%f,%f,%f');
fclose(fid);
this loads the values from all rows with the specified format into the variable data.
no matlab around to test it out though.
fscanf is an option to, the same kind of parameters as textscan.
EDIT: typo, you want to detect floats (%f) of course, and not integers (%d)
EDIT2: got matlab and tested it out, this works here for your sample input ^^
fid = fopen('testFile.txt');
data = textscan(fid,'%*s%f%f%f','Delimiter',',<>')
fclose(fid);