Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

strange behavior of the fscanf function

Tags:

matlab

scanf

I am trying to read the information contained in a small configuration file with Matlab's fscanf function. The content of the file is;

YAcex: 1.000000
YOx: 1.000000
KAce: 1.000000

The matlab code used to parse the file is;

fh = fopen('parameters', 'r');
fscanf(fh, 'YAcex: %f\n')
fscanf(fh, 'YOx: %f\n')
fscanf(fh, 'KAce: %f\n')
fclose(fh);

When this script is invoked, only the "YAcex" line is read correctly; fscanf returns [] for the two other lines. If the YOx and KAce lines are switched (KAce before YOx), all lines are read correctly by fscanf.

Can someone explain this behavior?

supplementary information: The linefeeds in the input file are simple linefeed (\n character, without \r character).

like image 862
magva Avatar asked Jan 03 '23 14:01

magva


2 Answers

Your problem is that you only want to read one value per call to fscanf, but by default it tries to read as many values as possible. Note this excerpt from the documentation:

The fscanf function reapplies the format throughout the entire file and positions the file pointer at the end-of-file marker. If fscanf cannot match formatSpec to the data, it reads only the portion that matches and stops processing.

This means the first call correctly reads the first line of the file, but then tries to read the next line as well, finding no exact match to its format specifier. It finds a partial match for the next line, where the first Y of YOx: matches the beginning of YAcex: in the format specifier. This partial match places the file pointer directly after the Y in YOx:, causing the next call to fscanf to fail since it is starting at the Ox: .... We can illustrate this with ftell:

fh = fopen('parameters', 'r');
fscanf(fh, 'YAcex: %f\n');
ftell(fh)

ans =

    18    % The "O" is the 18th character in the file

When you switch the YOx: and KAce: lines, a partial match of the next line doesn't happen any more, so the file pointer ends up at the beginning of the next line every time and all the reads are successful.

So, how can you get around this? One option is to always specify the size argument so fscanf doesn't reapply the format specifier unnecessarily:

fh = fopen('parameters', 'r');
fscanf(fh, 'YAcex: %f\n', 1);
fscanf(fh, 'YOx: %f\n', 1);
fscanf(fh, 'KAce: %f\n', 1);
fclose(fh);

Another option is to do this all in one line:

fh = fopen('parameters', 'r');
values = fscanf(fh, 'YAcex: %f\n YOx: %f\n KAce: %f\n');
fclose(fh);

And values will be a 3-by-1 array containing the 3 values from the file.

like image 181
gnovice Avatar answered Jan 12 '23 01:01

gnovice


As you already realized, \r or \r\n could cause this kind of behavior. The likely reason is similar to that, for example, there is some invisible characters like space somewhere. You can debug this by reading all as uint8, and take a look at the location where problem occurs:

u8 = fread(fh, inf, '*uint8')';

One stupid way to avoid this kind of issue is to read all as char, and search each keyword:

fh = fopen('parameters');
ch = fread(fh, inf, '*char')'; % read all as char
fclose(fh);

YAcex = regexp(ch, '(?<=YAcex:\s?)[\d\.]+', 'match', 'once'); % parse YAcex 

You can parse others accordingly. The advantage of this is that it is less sensitive to a space somewhere, and the order of parameters does not matter.

like image 26
Xiangrui Li Avatar answered Jan 12 '23 01:01

Xiangrui Li