MATLAB Coder

Read a Text File

This example shows how to generate a standalone C library from MATLAB code that reads a file from disk using the standard C functions fopen/fread/fclose. To call these C functions, the MATLAB code uses the 'coder.ceval' function.

Prerequisites

There are no prerequisites for this example.

Create a New Folder and Copy Relevant Files

The following code will create a folder in your current working folder (pwd). The new folder will only contain the files that are relevant for this example. If you do not want to affect the current folder (or if you cannot generate files in this folder), you should change your working folder.

Run Command: Create a New Folder and Copy Relevant Files

coderdemo_setup('coderdemo_readfile');

About the 'readfile' Function

The readfile.mreadfile.m function takes a file name (or path) as input and returns a string containing the contents of the file.

type readfile
% y = readfile(filename)
% Read file 'filename' and return a MATLAB string with the contents
% of the file. Internally C functions fopen/fread are used to read
% data from the file.
function y = readfile(filename) %#codegen

% The 'fprintf' function will not be compiled and instead passed
% to the MATLAB runtime. If we choose to generate code for this example,
% all calls to extrinsic functions are automatically eliminated.
coder.extrinsic('fprintf');

% Put class and size constraints on function input.
assert(isa(filename, 'char'));
assert(size(filename, 1) == 1);
assert(size(filename, 2) <= 1024);

% Define a new opaque variable 'f' which will be of type 'FILE *'
% in the generated C code initially with the value NULL.
f = coder.opaque('FILE *', 'NULL');

% Call fopen(filename 'r'), but we need to convert the MATLAB
% string into a C type string (which is the same string with the
% NUL (\0) string terminator).
f = coder.ceval('fopen', c_string(filename), c_string('r'));

% Call fseek(f, 0, SEEK_END) to set file position to the end of
% the file.
coder.ceval('fseek', f, int32(0), coder.opaque('int', 'SEEK_END'));

% We need to initialize the variable 'filelen' to the proper type
% as custom C functions are not analyzed.
filelen = int32(0);

% Call ftell(f) which will return the length of the file in bytes
% (as current file position is at the end of the file).
filelen = coder.ceval('ftell', f);

% Reset current file position
coder.ceval('fseek', f, int32(0), coder.opaque('int', 'SEEK_SET'));

% Initialize a buffer
buffer = zeros(1,65536,'uint8');

% Remaining is the number of bytes to read (from the file)
remaining = filelen;

% Index is the current position to read into the buffer
index = int32(1);

while remaining > 0
    % Buffer overflow?
    if remaining + index > size(buffer,2)
        fprintf('Attempt to read file which is bigger than internal buffer.\n');
        fprintf('Current buffer size is %d bytes and file size is %d bytes.\n', size(buffer,2), filelen);
        break
    end
    % Read as much as possible from the file into internal buffer
    nread = coder.opaque('size_t');
    nread = coder.ceval('fread', coder.ref(buffer(index)), int32(1), remaining, f);
    n = int32(0);
    n = coder.ceval('(int)',nread);
    if n == 0
        % Nothing more to read
        break;
    end
    % Did something went wrong when reading?
    if n < 0
        fprintf('Could not read from file: %d.\n', n);
        break;
    end
    % Update state variables
    remaining = remaining - n;
    index = index + n;
end

% Close file
coder.ceval('fclose', f);

y = char(buffer(1:index));

% Create a NUL terminated C string given a MATLAB string
function y = c_string(s)
y = [s 0];

The 'readfile' function uses the following functions: 'coder.opaque': to declare a variable in the generated code 'coder.extrinsic': to instruct 'codegen' not to generate code for the specified function but instead to dispatch it to MATLAB for execution 'coder.ceval': to call external C functions

Generate the MEX Function for Testing

Generate a MEX function using the 'codegen' command.

codegen readfile

Before generating C code, you should first test the MEX function in MATLAB to ensure that it is functionally equivalent to the original MATLAB code and that no run-time errors occur. By default, 'codegen' generates a MEX function named 'readfile_mex' in the current folder. This allows you to test the MATLAB code and MEX function and compare the results.

Run the MEX Function

Call the generated MEX function and display the size of the returned string and its first 100 characters.

y = readfile_mex('readfile.m');
size(y)
y(1:100)
ans =

           1        2719


ans =

% y = readfile(filename)
% Read file 'filename' and return a MATLAB string with the contents
% of th

Generate C Code

cfg = coder.config('lib');
cfg.CustomSourceCode = '#include <stdio.h>';
codegen -config cfg readfile

Using 'codegen' with the specified '-config cfg' option produces a standalone C library using the STDIO standard header file.

Inspect the Generated Code

By default, the code generated for the library is in the folder codegen/lib/readfile/

The files are:

dir codegen/lib/readfile/
.                      readfile_emxAPI.h      readfile_types.h       
..                     readfile_emxAPI.o      rtGetInf.c             
buildInfo.mat          readfile_emxutil.c     rtGetInf.h             
char.c                 readfile_emxutil.h     rtGetInf.o             
char.h                 readfile_emxutil.o     rtGetNaN.c             
char.o                 readfile_initialize.c  rtGetNaN.h             
codeInfo.mat           readfile_initialize.h  rtGetNaN.o             
interface              readfile_initialize.o  rt_nonfinite.c         
readfile.a             readfile_ref.rsp       rt_nonfinite.h         
readfile.c             readfile_rtw.mk        rt_nonfinite.o         
readfile.h             readfile_terminate.c   rtw_proj.tmw           
readfile.o             readfile_terminate.h   rtwtypes.h             
readfile_emxAPI.c      readfile_terminate.o   

Inspect the C Code for the 'readfile.c' Function

type codegen/lib/readfile/readfile.c
/*
 * File: readfile.c
 *
 * MATLAB Coder version            : 2.7
 * C/C++ source code generated on  : 04-Sep-2014 03:02:37
 */

/* Include Files */
#include "rt_nonfinite.h"
#include "readfile.h"
#include "readfile_emxutil.h"
#include "char.h"

/* Custom Source Code */
#include <stdio.h>

/* Function Definitions */

/*
 * The 'fprintf' function will not be compiled and instead passed
 *  to the MATLAB runtime. If we choose to generate code for this example,
 *  all calls to extrinsic functions are automatically eliminated.
 * Arguments    : const char filename_data[]
 *                const int filename_size[2]
 *                emxArray_char_T *y
 * Return Type  : void
 */
void readfile(const char filename_data[], const int filename_size[2],
              emxArray_char_T *y)
{
  int filelen;
  int n;
  char tmp_data[1025];
  char cv0[2];
  static const char cv1[2] = { 'r', '\x00' };

  FILE * f;
  unsigned char buffer[65536];
  int b_index;
  boolean_T exitg1;
  long i0;
  size_t nread;
  emxArray_uint8_T *b_buffer;

  /*  y = readfile(filename) */
  /*  Read file 'filename' and return a MATLAB string with the contents */
  /*  of the file. Internally C functions fopen/fread are used to read */
  /*  data from the file. */
  /*  Put class and size constraints on function input. */
  /*  Define a new opaque variable 'f' which will be of type 'FILE *' */
  /*  in the generated C code initially with the value NULL. */
  /*  Call fopen(filename 'r'), but we need to convert the MATLAB */
  /*  string into a C type string (which is the same string with the */
  /*  NUL (\0) string terminator). */
  /*  Create a NUL terminated C string given a MATLAB string */
  filelen = filename_size[1];
  for (n = 0; n < filelen; n++) {
    tmp_data[n] = filename_data[filename_size[0] * n];
  }

  tmp_data[filename_size[1]] = '\x00';
  for (n = 0; n < 2; n++) {
    cv0[n] = cv1[n];
  }

  f = fopen(&tmp_data[0], cv0);

  /*  Call fseek(f, 0, SEEK_END) to set file position to the end of */
  /*  the file. */
  fseek(f, 0, SEEK_END);

  /*  We need to initialize the variable 'filelen' to the proper type */
  /*  as custom C functions are not analyzed. */
  /*  Call ftell(f) which will return the length of the file in bytes */
  /*  (as current file position is at the end of the file). */
  filelen = ftell(f);

  /*  Reset current file position */
  fseek(f, 0, SEEK_SET);

  /*  Initialize a buffer */
  memset(&buffer[0], 0, sizeof(unsigned char) << 16);

  /*  Remaining is the number of bytes to read (from the file) */
  /*  Index is the current position to read into the buffer */
  b_index = 1;
  exitg1 = false;
  while ((!exitg1) && (filelen > 0)) {
    /*  Buffer overflow? */
    i0 = (long)filelen + b_index;
    if (i0 > 2147483647L) {
      i0 = 2147483647L;
    } else {
      if (i0 < -2147483648L) {
        i0 = -2147483648L;
      }
    }

    if ((int)i0 > 65536) {
      exitg1 = true;
    } else {
      /*  Read as much as possible from the file into internal buffer */
      nread = fread(&buffer[b_index - 1], 1, filelen, f);
      n = (int)(nread);
      if ((n == 0) || (n < 0)) {
        /*  Nothing more to read */
        exitg1 = true;
      } else {
        /*  Did something went wrong when reading? */
        /*  Update state variables */
        filelen -= n;
        i0 = (long)b_index + n;
        if (i0 > 2147483647L) {
          i0 = 2147483647L;
        } else {
          if (i0 < -2147483648L) {
            i0 = -2147483648L;
          }
        }

        b_index = (int)i0;
      }
    }
  }

  emxInit_uint8_T(&b_buffer, 2);

  /*  Close file */
  fclose(f);
  n = b_buffer->size[0] * b_buffer->size[1];
  b_buffer->size[0] = 1;
  b_buffer->size[1] = b_index;
  emxEnsureCapacity((emxArray__common *)b_buffer, n, (int)sizeof(unsigned char));
  for (n = 0; n < b_index; n++) {
    b_buffer->data[b_buffer->size[0] * n] = buffer[n];
  }

  b_char(b_buffer, y);
  emxFree_uint8_T(&b_buffer);
}

/*
 * File trailer for readfile.c
 *
 * [EOF]
 */

Cleanup

Remove files and return to original folder

Run Command: Cleanup

cleanup