2

I have written a script that saves me to manually import in Matlab the data recorded during tests.

Each test run saves around 2600 variables in .csv files, each with 2 header lines, two columns of data and ; is the separator.

The filenames come from an internal C-struct used by the monitoring program and are thus of this kind: foo.bar.another.foo.bar.local_varname#VALUE.csv and I want to use this to re-create the struct in Matlab in order to save only that in a test_name.mat file.

Many times the local_varname is more than 63 char long, so I have some substitution rules in order to shorten the names without having Matlab truncating the names (and so trying to avoid naming conflicts).

This is the code

clear all
clc

% Main names
path_self         = pwd;
backslash_indices = strfind(path_self,'\');
test_name         = path_self(backslash_indices(end)+1:end); % the directory name gives me the test_name

% Preallocation
filenames = cell(1,2600);
addresses = cell(1,2600);
i=0;

% Full list
MyFiles = dir(path_self); 

% Discard subdirectories and non interesting files
for k=1:length(MyFiles)
    if ~MyFiles(k).isdir,
        if ~isempty(strfind(MyFiles(k).name,'#VALUE.csv'))
            i=i+1;
            % Too many files
            if i > length(filenames)
                filenames = [filenames cell(1,100)];
                addresses = [addresses cell(1,100)];
            end
            % Naming Substitution Rules

            %%% INSERT HERE BUNCH OF RULES

            % Addresses and names
            filenames{i} = strrep(filename,'#VALUE.csv','');
            addresses{i} = fullfile(path_self, MyFiles(k).name);
        end
    end
end
filenames = filenames(1:i);
addresses = addresses(1:i);

% Check that no conflicts are created
if length(filenames) ~= length(unique(filenames))
    error('filenames not unique')
end

% Housekeeping #1
clear MyFiles backslash_indices i k path_self

% Import data
for j=1:length(filenames)
    % Read data
    Data = importdata(addresses{j}, ';', 2);
    % Assign data
    eval([filenames{j}, '.time   = Data.data(:,1)./1000000;']); % Converted in seconds
    eval([filenames{j}, '.values = Data.data(:,2);']);
    % Let's avoid data corruption
    clear Data
end

% Housekeeping #2
clear filenames addresses j 

% Save data struct
save(test_name, '-regexp', '^((?!name).)*$')

Now my question While researching information and help to write the above code I often found people frowning upon the use of eval(): why is it so? In the above situation, can I avoid it?

Thanks

EDIT As suggested by @wakjah, I tested a containers.Map() approach. Unfortunately is not suited to our needs, since at that point a list of keys is needed and accessing the data is not exactly friendly (remember that I have ~2600 variables, meaning at least the same number of keys)

As for what has been asked by @Dennis Jaheruddin, the data struct is usable and does not creates any kind of conflict, even with these long varnames (given that each name* between two consecutive . is less than 63 chars long)

*my apologies for not using a better technical term

Federico
  • 1,092
  • 3
  • 16
  • 36

3 Answers3

4

From this page on Mathworks:

Although the eval function is very powerful and flexible, it not always the best solution to a programming problem. Code that calls eval is often less efficient and more difficult to read and debug than code that uses other functions or language constructs. ...

You can easily use the parentheses notation to accomplish your task. A simple example:

s = struct();
myFieldName = 'test';
s.(myFieldName) = myFieldValue;

This will set the test field in the struct s to myFieldValue.

There is also this blog post by Loren on this very subject.

Edit: As your requirement is that field names be longer than 63 characters, another method is to use the containers.Map object. Here's a little example:

>> m = containers.Map();
>> myFieldName = repmat('abcdefg', [1 10]); % 70 chars long
>> m(myFieldName) = 12345; 
>> m(myFieldName)

ans =

       12345
wakjah
  • 4,541
  • 1
  • 18
  • 23
  • Unfortunately this solution creates a different kind of problem: the limit of 63 chars is imposed on the full `filenames{j}` (that goes up to 180 chars in certain cases) while with `eval()` I have to care only about the `local_varname`. Additionally, it returns an error if `filenames{j}` contains a `.` (always the case for me) – Federico Mar 28 '13 at 12:45
  • How is this avoided with `eval`? I just tried it - MATLAB throws a warning whether you do it using `eval` or not when your field name is longer than 63 chars. Perhaps I'm misunderstanding you... Anyway, if you really need field names that long, have you considered using [`containers.Map`](http://www.mathworks.co.uk/help/matlab/map-containers.html)? – wakjah Mar 28 '13 at 12:49
  • Example: `filenames{1} = ABCDE.ABC0.ABCDE.ABC_AA_AA_AAA_00.BBB_BBB_00_BBB_CCCC_ABCDE_ABCDEFGHIJKLMNO_AAAA0.A_ABCDE_CCC_DDDD_EEEEE_AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDD00_DDDD0` (153 char long) With `eval()`, if I shrink the last portion (now 71 char long) to less than 63 chars, I do not have problems, with you code 1) I have an error for the `.`s and 2) it complains that `filenames{1}` is too long – Federico Mar 28 '13 at 13:00
  • Ah okay. Is there any particular reason you need these to be variables in the local workspace? If not, I'd definitely say a Map is a more efficient and (perhaps more importantly) readable solution. – wakjah Mar 28 '13 at 13:03
  • Never heard of Maps, I will have a look at them. In general I will have to do data post-processing later on (e.g., frequency analysis), is it possible to do it with Maps? – Federico Mar 28 '13 at 13:05
  • Well, you may run into some speed troubles if there is, e.g., only a single data point associated with each key in the map, but if each key maps to a vector that you want to do an FFT on, you should be fine. – wakjah Mar 28 '13 at 13:08
  • Thanks. I will verify if it is possible to adjust the rest of the post-processing accordingly. – Federico Mar 28 '13 at 13:21
3

After looking at what you are trying to do my first thought is that you are doing strange.

Having variablenames of more than 63 characters is basically asking for trouble. Instead I would recommend the following simple solution:

Use a struct with two fields:

  • Filename
  • Value

This way your data is structured more naturally and you will only need one variable for all your files.


Note that value can be a structure or array and is not limited to a single string or number.

The only real disadvantage I can think of here is that autocomplete works on variable names but not on their contents, but that should be a small price to pay.

Dennis Jaheruddin
  • 21,208
  • 8
  • 66
  • 122
0

Have you considered using the function genvarname to generate valid unique variable names from local_varname?

Sam Roberts
  • 23,951
  • 1
  • 40
  • 64
  • yes, but I have to maintain the same struct as in the original program, so I prefer hand-written rules so that I know what corresponds to what. – Federico Mar 28 '13 at 13:03