I am cycling through various cells in Matlab by hand in a script (let's call it foo.m):
%%
%Code for cell 1
%%
%Code for cell 2
From the command line of Matlab, I would like to be able to selectively run the code in cell 2. The documentation only has instructions for how to do it interactively (e.g., place the cursor in the appropriate cell, then blah blah). I would like something for the command line so I can do something like foo.runCell(1) to run the code in cell 1 above.
If there is no way to do it, I will just split the cells up into separate scripts/functions. This is less convenient, as I am in a 'grind out prototype very quickly' mode of coding, so want everything in one file for now.
Dev-iL has provided a good answer using java etc... I will provide an alternative here which does not use java or the editor, but reads the file and evals the statements when requested.
For this to work it has a pre-requirement that the file has been saved (which I appreciate is not a pre-requisite of running cells/codeBlocks interactively).
Anyway I thought this was a "quirky" question and thought I'd add have a go at an answer.
Here is my script (cellScript.m) which contains code blocks (Note I have given each block a name after the "%%":
%Note: for this method your NOT allowed to put comments at the end of lines
%% cellA
disp ( 'running cell A' );
disp ( 'finished cell A' );
%% cellB
disp ( 'running cell B' );
disp ( 'finished cell B' );
%% cellC
disp ( 'running cell C' );
for ii=1:100
aa(ii) = randi(1);
end
% cells can have comments
disp ( 'past 1st coment' );
% cells comments can be indented
disp ( 'past indented comment' );
%{
block
comment
%}
disp ( 'past block comment' );
% cells can have comments
% multiple lines of comments
% not lined up
%
%and empty
disp ( 'past multiple lines of comments' );
disp ( 'finished cell C' );
%% cellD
disp ( 'running cell D' );
disp ( 'finished cell D' );
%% cellE
disp ( 'running cell E' );
disp ( 'finished cell E' );
I have created a class which does the job requested (I called it cellRunner.m )
classdef cellRunner < handle
properties ( SetAccess = private )
fileName
fileInfo
cellInfo
cellNames = {};
end
methods
function obj = cellRunner ( file ) % constructor
if nargin == 0
obj.fileName = 'cellScript.m'; % default file for testing
else
obj.fileName = file; % store user file
end
obj.parseFile(); % read the file into memory
end
function obj = parseFile ( obj )
if ~isempty ( obj.fileInfo ) % on parsing check to see if its been parsed before
if isequal ( obj.fileInfo, dir ( obj.fileName ) ) % Check date stamp (has cell file been modified
% disp ( 'file not changed - reading skipped' ); % if not skip
% reading
return
end
end
obj.fileInfo = dir ( obj.fileName ); % store file info
fid = fopen ( obj.fileName ); % open file for reading
if fid ~= -1
index = 0; % this is the index of each cell
inCell = false; % has it found a cell to start reading
lines = cell(0);
while ( true )
line = fgetl ( fid ); % read the line in the file
if line == -1; break; end % check for the end of the file
sLine = strtrim ( line ); % trim any white space
if length ( sLine ) > 2 && strcmp ( sLine(1:2), '%%' ) % check to see if its the start of a cell
if index > 0 % Store the last cell data
obj.cellInfo{index} = lines; % in class to run when required
end
index = index + 1; % increment the index
obj.cellNames{index} = strtrim ( sLine(3:end) ); % save the name of the cell
lines = cell(0); % re-initialise the lines var
inCell = true; % the start of the cells have been found
elseif inCell % if reading a cell array
lines{end+1} = line; % add each line to the lines var
end
end
if index > 0 % make sure and save the last cell when finished reading
obj.cellInfo{index} = lines;
end
fclose ( fid );
else
error ( 'cellRunner:fileError', 'unable to read file' );
end
end
function obj = runCell ( obj, arg )
% obj.runCell ( 'cellName' );
% obj.runCell ( index );
obj.parseFile(); % check that the file hasn't been changed
if ischar ( arg ) % if user provided a char then search for it
index = strcmp ( arg, obj.cellNames ); % find the index
if ~any ( index ) % check it was found
error ( 'cellRunner:notFound', '%s not found', arg );
end
else
index = arg; % if index is an integer (not checked - assumed if not char)
if index < 1 || index > length ( obj.cellInfo ) % check integer is valid
error ( 'cellRunner:notFound', 'Index %d not found', arg );
end
end
commands = obj.cellInfo{index}{1}; % start to build the command to execute.
inBlock = false;
for ii=2:length(obj.cellInfo{index}) % loop around - ignoring any commented lines.
nextLine = strtrim ( obj.cellInfo{index}{ii} );
if inBlock
if length ( nextLine ) == 2 && strcmp ( nextLine, '%}' );
inBlock = false;
end
continue
end
if length ( nextLine ) == 2 && strcmp ( nextLine, '%{' );
inBlock = true;
continue
end
if length ( nextLine ) >= 1 && strcmp ( nextLine(1), '%' )
continue;
end
commands = sprintf ( '%s;%s', commands, obj.cellInfo{index}{ii} ); % build a parge string to eval
end
evalin('base',commands); % eval the expression in the base workspace.
end
end
end
The code is then used as follows:
obj.cellRunner();
% Individual cells can be run in two ways:
% By providing the name of the cell (the string after the %%)
obj.runCell ( 'cellC' );
% By providing the index
obj.runCell ( 3 );
Note Recall the file must be saved for this to work.
Sample run:
whos
obj = cellRunner ( 'cellScript.m' );
obj.runCell ( 'cellC' );
running cell C
past 1st coment
past indented comment
past block comment
past multiple lines of comments
finished cell C
whos
Name Size Bytes Class Attributes
aa 1x100 800 double
ans 1x1 112 cellRunner
ii 1x1 8 double
obj 1x1 112 cellRunner
Note 1 - Why handle class? I inherit from the handle class because I only want one copy of my file data that has been read - see answer 1 in this question for an excellent overview of when to use value/handle classes.
This is my ugly code that does what you asked for.
Despite my best attempts, I was unable to find the way to make it work "properly" (i.e. without simulating key presses). It is my hope that this will facilitate somebody else's attempts to find the way to do it right. Anyway, here goes:
runCodeSectionInFile.m:
function runCodeSectionInFile(scriptFullPath,sectionNum)
%// TIP: You can use "which('scriptName')" to obtain the full path of a
%// script on your MATLAB path.
%%% // Temporary Workaround
import java.awt.Robot; %// part of temporary workaround
import java.awt.event.*; %// part of temporary workaround
RoboKey = Robot; %// part of temporary workaround
RoboKey.setAutoDelay(10);%// part of temporary workaround
%% // Test if the needed components are available (optional)
if ~matlab.desktop.editor.isEditorAvailable || ...
~com.mathworks.mde.editor.codepad.Codepad.isCodepadEnabled
error('MATLAB editor is N\A');
end
%% // Open and\or switch to the script file
%// Test if script is opened:
if ~matlab.desktop.editor.isOpen(scriptFullPath)
scriptDoc = matlab.desktop.editor.openDocument(scriptFullPath);
else %// in case the script is open, get a handle to it, and save it:
scriptDoc = matlab.desktop.editor.findOpenDocument(scriptFullPath);
%// Save the script before running (optional):
scriptDoc.save;
end
scriptDoc.goToLine(0); %// Position the cursor at the beginning of the file
%// NOTE1: - uses zero based indexing!
jEd = com.mathworks.mlservices.MLEditorServices.getEditorApplication ...
.openEditorForExistingFile(java.io.File(scriptFullPath));
jEd.getTextComponent.grabFocus; drawnow; %// part of temp fix
%// NOTE2: most of the above can be replaced with:
%// EDITOROBJ = matlab.desktop.editor.openAndGoToLine(FILENAME,LINENUM);
%% // Get the Codepad and the LineManager handles:
jCm = com.mathworks.mde.editor.codepad.CodepadActionManager ...
.getCodepadActionManager(jEd);
jCp = jEd.getProperty('Codepad');
jLm = jCp.getLineManager(jEd.getTextComponent,jCm);
%% // Advance to the desired section
jAc = com.mathworks.mde.editor.codepad.CodepadAction.CODEPAD_NEXT_CELL;
%// 'next-cell' Action
for ind1=1:sectionNum-1 %// click "advance" several times
%// <somehowExecute(jAc) OR jCp.nextCell() >
RoboKey.keyPress(KeyEvent.VK_CONTROL); %// part of temporary workaround
RoboKey.keyPress(KeyEvent.VK_DOWN); %// part of temporary workaround
end
RoboKey.keyRelease(KeyEvent.VK_DOWN); %// part of temporary workaround
RoboKey.keyRelease(KeyEvent.VK_CONTROL); %// part of temporary workaround
%% Execute section - equivalent to clicking "Run Section" once
jAc = com.mathworks.mde.editor.codepad.CodepadAction.CODEPAD_EVALUATE_CELL;
%// 'eval-cell' Action
%// <somehowExecute(jAc); OR jCp.evalCurrentCell(true) >
RoboKey.keyPress(KeyEvent.VK_CONTROL); %// part of temporary workaround
RoboKey.keyPress(KeyEvent.VK_ENTER); %// part of temporary workaround
RoboKey.keyRelease(KeyEvent.VK_CONTROL); %// part of temporary workaround
%% // Close the file (optional)
jEd.close;
%% // Return focus to the command line:
com.mathworks.mde.cmdwin.CmdWin.getInstance.grabFocus;
testScript.m:
%% // This is code block one
disp('This is section one');
%% // This is code block two
disp('This is section two');
%% // This is code block three
disp('This is section three');
To run the demo, simply put both files in the same folder, make sure it's on your path (or cd), and do runCodeSectionInFile(which('testScript.m'),1). This results in the following:
>> runCodeSectionInFile(which('testScript.m'),1)
This is section one
>> runCodeSectionInFile(which('testScript.m'),2)
This is section two
>> runCodeSectionInFile(which('testScript.m'),3)
This is section three
You may also need to adjust the RoboKey.setAutoDelay(10); to a larger number if you get NPE.
Sources:
A long-gone MATLAB blog post.
Some old question I posted and its answers.
P.S.
I wrote this on 2014b. Other (older) versions may need adjustments...
Edit1:
I have managed to uncover another piece of this puzzle by finding handles to the Codepad object and its TaggedLineManager. The current problem is the fact that most Codepad methods must be run on the EDT (something like a "UI thread", i.e. originate from interactions with the interface).
In order to run a particular section from a file using command line, you can use
echodemo(filename, index of section)
MATLAB documentation
Related
I have a script (actually a test script used for script based unit tests) which is divided into sections of the type
%% test foo
code
%% test bar
more code
How do I retrieve the name of a section (in any form, e.g. foo, test foo, TestFoo or whatever Matlab makes out of a section name) from inside my script (i.e. the place where I have written code).
Sorry, I have no Matlab on this computer. So the code is untested. But I guess with the code snippet below you should be able to do what you are trying.
It's a little bit hacky, but it's Matlab.
function section_name = get_current_section_name()
ds = dbstack();
execution_file = ds(2).file;
execution_line = ds(2).line;
fid = fopen(execution_file);
section_name = "";
current_line_number = 1;
tline = fgetl(fid);
while ischar(tline)
if startsWith(tline, "%%")
section_name = tline;
end
if execution_line == current_line_number
return
end
tline = fgetl(fid);
current_line = current_line + 1;
end
fclose(fid)
% case: not found
section_name = "";
end
And on your desired position you should be fine if you just call
section_name = get_current_section_name()
Here's a possible solution.
Given the following code:
%% test foo
code
%% test bar
more code
%% test baz
other code
You can obtain the comment of the active live script as follows:
>> X = matlab.desktop.editor.getActive; % Get MATLAB editor
>> editorText = X.Text; % Get editor text
>> n = X.Selection(1); % Get line number on which cursor is
>> all_lines = regexp(editorText,'\n','split'); % Get all lines of editor
>> for line = all_lines(n:-1:1) % Iterate backwards from starting lne
>> if contains(line,'%%') % If the line contains a comment, extract string and exit loop
>> match = regexp(line,'%% (.+)$','tokens','once')
>> break
>> end
>> end
match is a cell with the desired content. If your cursor is in the first code section, match{1} contains test foo. If the cursor is in the second block, test bar and finally test baz for the last block.
How it works:
First we get the active MATLAB editor and the position of the cursor. Then, we split on newline \n to obtain all of the lines in the editor. From the current line, we iterate backwards to identify the first line which has the comment, which contains the desired string.
In short, I'm having a headache in multiple languages to read a txt file (linked below). My most familiar language is MATLAB so for that reason I'm using that in this example. I've found a way to read this file in ~ 5 minutes, but given I'll have tons and tons of data from my instrument shortly as it measures all day every 30 seconds this just isn't feasible.
I'm looking for a way to quickly read these irregular text files so that going forward I can knock these out with less of a time burden.
You can find my exact data at this link:
http://lb3.pandonia.net/BostonMA/Pandora107s1/L0/Pandora107s1_BostonMA_20190814_L0.txt.bz2
I've been using the "readtable" function in matlab and I have achieved a final product I want but I'm looking to increase the speed
Below is my code!
clearvars -except pan day1; % Clearing all variables except for the day and instrument variables.
close all;
clc;
pan_mat = [107 139 155 153]; % Matrix of pandora numbers for file-choosing
reasons.
pan = pan_mat(pan); % pandora number I'm choosing
pan = num2str(pan); % Turning Pandora number into a string.
%pan = '107'
pandora = strcat('C:\Users\tadams15\Desktop\Folders\Counts\Pandora_Dta\',pan)
% string that designates file location
%date = '90919'
month = '09'; % Month
day2 = strcat('0',num2str(day1)) % Creating a day name for the figure I ultimately produce
cd(pandora)
d2 = strcat('2019',num2str(month),num2str(day2)); % The final date variable
for the figure I produce
%file_pan = 'Pandora107s1_BostonMA_20190909_L0';
file_pan = strcat('Pandora',pan,'s1_BostonMA_',d2,'_L0'); % File name string
%Try reading it in line by line?
% Load in as a string and then convert the lines you want as numbers into
% number.
delimiterIn = '\t';
headerlinesIn = 41;
A = readtable(file_pan,'HeaderLines', 41, 'Delimiter', '\t'); %Reading the
file as a table
A = table2cell(A); % Converting file to a cell
A = regexp(A, ' ', 'split'); % converting cell to a structure matrix.
%%
A= array2table(A); % Converting Structure matrix back to table
row_num = 0;
pan_mat_2 = zeros(2359,4126);
datetime_mat = zeros(2359,2);
blank = 0;
%% Converting data to proper matrices
[length width] = size(A);
% The matrix below is going through "A" and writing from it to a new
% matrix, "pan_mat_2" which is my final product as well as singling out the
% rows that contain non-number variables I'd like to keep and adding them
% later.
tic
%flag1
for i = 1:length; % Make second number the length of the table, A
blank = 0;
b = table2array(A{i,1});
[rows, columns] = size(b);
if columns > 4120 && columns < 4140
row_num = row_num + 1;
blank = regexp(b(2), 'T', 'split');
blank2 = regexp(blank{1,1}(2), 'Z', 'split');
datetime_mat(row_num,1) = str2double(blank{1,1}(1));
datetime_mat(row_num,2) = str2double(blank2{1,1}(1));
for j = 1:4126;
pan_mat_2(row_num,j) = str2double(b(j));
end
end
end
toc
%flag2
In short, I'm already getting the result I want but the part of the code where I'm writing to a new array "flag 1" to "flag 2" is taking roughly 222 seconds while the entire code only takes about 248 seconds. I'd like to find a better way to create the data there than to write it to a new array and take a whole bunch of time.
Any suggestions?
Note:
There are a quite a few improvments you can make for speed but there are also corrections. You preallocate you final output variable with hard coded values:
pan_mat_2 = zeros(2359,4126);
But later you populate it in a loop which run for i = 1:length.
length is the full number of lines picked from the file. In your example file there are only 784 lines. So even if all your line were valid (ok to be parsed), you would only ever fill the first 784 lines of the total 2359 lines you allocated in your pan_mat_2. In practice, this file has only 400 valid data lines, so your pan_mat_2 could definitely be smaller.
I know you couldn't know you had only 400 line parsed before you parsed them, but you knew from the beginning that you had only 784 line to parse (you had the info in the variable length). So in case like these pre-allocate to 784 and only later discard the empty lines.
Fortunately, the solution I propose does not need to pre-allocate larger then discard. The matrices will end up the right size from the start.
The code:
%%
file_pan = 'Pandora107s1_BostonMA_20190814_L0.txt' ;
delimiterIn = '\t';
headerlinesIn = 41;
A = readtable(file_pan,'HeaderLines', 41, 'Delimiter', '\t'); %Reading the file as a table
A = table2cell(A); % Converting file to a cell
A = regexp(A, ' ', 'split'); % converting cell to a structure matrix.
%% Remove lines which won't be parsed
% Count the number of elements in each line
nelem = cell2mat( cellfun( #size , A ,'UniformOutput',0) ) ;
nelem(:,1) = [] ;
% find which lines does not have enough elements to be parsed
idxLine2Remove = ~(nelem > 4120 & nelem < 4140) ;
% remove them from the data set
A(idxLine2Remove) = [] ;
%% Remove nesting in cell array
nLinesToParse = size(A,1) ;
A = reshape( [A{:}] , [], nLinesToParse ).' ;
% now you have a cell array of size [400x4126] cells
%% Now separate the columns with different data type
% Column 1 => [String] identifier
% Column 2 => Timestamp
% Column 3 to 4125 => Numeric values
% Column 4126 => empty cell created during the 'split' operation above
% because of a trailing space character.
LineIDs = A(:,1) ;
TimeStamps = A(:,2) ;
Data = A(:,3:end-1) ; % fetch to "end-1" to discard last empty column
%% now extract the values
% You could do that directly:
% pan_mat = str2double(Data) ;
% but this takes a long time. A much computationnaly faster way (even if it
% uses more complex code) would be:
dat = strjoin(Data) ; % create a single long string made of all the strings in all the cells
nums = textscan( dat , '%f' , Inf ) ; % call textscan on it (way faster than str2double() )
pan_mat = reshape( cell2mat( nums ) , nLinesToParse ,[] ) ; % reshape to original dimensions
%% timestamps
% convert to character array
strTimeStamps = char(TimeStamps) ;
% convert to matlab own datetime numbering. This will be a lot faster if
% you have operations to do on the time stamps later
ts = datenum(strTimeStamps,'yyyymmddTHHMMSSZ') ;
%% If you really want them the way you had it in your example
strTimeStamps(:,9) = ' ' ; % replace 'T' with ' '
strTimeStamps(:,end) = ' ' ; % replace 'Z' characters with ' '
%then same again, merge into a long string, parse then reshape accordingly
strdate = reshape(strTimeStamps.',1,[]) ;
tmp = textscan( strdate , '%d' , Inf ) ;
datetime_mat = reshape( double(cell2mat(tmp)),2,[]).' ;
The performance:
As you can see on my machine your original code takes ~102 seconds to execute, with 80% of that (81s) spent on calling the function str2double() 3,302,400 times!
My solution, run on the same input file, takes ~5.5 seconds, with half of the time spent on calling strjoin() 3 times.
When you read the code above, try to understand how I limited the repetition of function call in lengthy loops by trying to keep everything as vectorised as possible.
Using the profiler, you can see that you call str2double 3302400 times in a run which takes about 80% of the total time on my pc. Now thats suboptimal, as each time you only translate 1 value and as far as your code goes you dont need the values as string again. I added this under you original code:
row_num = 0;
pan_mat_2_b = cell(2359,4126);
datetime_mat_b = cell(2359,2);%not zeros
blank = 0;
tic
%flag1
for i = 1:length % Make second number the length of the table, A
blank = 0;
b = table2array(A{i,1});
[rows, columns] = size(b);
if columns > 4120 && columns < 4140
row_num = row_num + 1;
blank = regexp(b(2), 'T', 'split');
blank2 = regexp(blank{1,1}(2), 'Z', 'split');
%datetime_mat(row_num,1) = str2double(blank{1,1}(1));
%datetime_mat(row_num,2) = str2double(blank2{1,1}(1));
datetime_mat_b(row_num,1) = blank{1,1}(1);
datetime_mat_b(row_num,2) = blank2{1,1}(1);
pan_mat_2_b(row_num,:) = b;
% for j = 1:4126
% pan_mat_2(row_num,j) = str2double(b(j));
% end
end
end
datetime_mat_b = datetime_mat_b(~all(cellfun('isempty',datetime_mat_b),2),:);
pan_mat_2_b=pan_mat_2_b(~all(cellfun('isempty',pan_mat_2_b),2),:);
datetime_mat_b=str2double(string(datetime_mat_b));
pan_mat_2_b=str2double(pan_mat_2_b);
toc
Still not great, but better. If you want to speed this up further i recommend you take a closer look at the readtable part. As you can save up quite some time if you start with reading in the format as doubles right from the beginning
Is there an elegant way to tell matlab to perform a predefined action after the execution of every line in a certain script? By elegant I mean no calling of the action after every line, but rather something like a simple command given at the start of the script.
Example:
Action --> disp('Performing Action');
script:
a = 1;
b = 2;
c = 3;
So the desirable outcome is that after each assignment (of a, b and c), the disp() command would be performed.
You can automatically create a modifed file that has the desired action included at the end of each line:
action = 'disp(''Performing Action'');'; %// action to be added at the end of each line
file = 'script.m'; %// original script
file_out = 'script_out.m'; %// modified script with action added
x = importdata(file); %// cell array of strings. Each line of the
%// original file is a string
x = strcat(x, {' ; '}, action); %// add action at the end of each string,
%// preceded with `;` in case the original line
%// didn't include that
fid = fopen(file_out, 'w');
fprintf(fid, '%s\n', x{:}); %// write output file
fclose(fid);
a = 1;
disp('Performing Action');
b = 2;
disp('Performing Action');
c = 3;
disp('Performing Action');
Or, if you can do this in a loop
for ii = 1:3
a(ii) = ii;
disp('Performing Action');
end
Actually making it output something after every line is not very matlab, but you could of course just loose all the semicolons and thus make it display all variables if you want to track where in the script you are.
I'd suggest a verbose switch in your code. Set it to 0 for no output and 1 for output (or use multiple levels if so desired)
if verbose > 0
disp('Performing Action');
end
This way you can easily switch the output on or off, depending on need.
For the code-reading and appending piece, see Louis Mendo's answer at https://stackoverflow.com/a/32137053/5211833
Here is my attempt. You can:
read the function line by line
execute each line, followed by your custom function.
NOTE
This won't work with functions containing for, if, etc..
You can eventually improve the code by passing a function handler with your custom action to runEachLine (and feval with that).
Here a code sample (partly based on this):
foo.m
function foo(args)
a = args{1};
b = 2;
c = a + b;
end
runEachLine.m
function runEachLine( mfile, args )
if nargin < 1
error('No script m-file specified.');
end
if ~strcmp(mfile(end-1:end),'.m')
mfile = [mfile '.m'];
end
if ~exist(mfile,'file')
error(['Cannot access ' mfile])
end
% Read function file
M = textread(mfile,'%s','delimiter','\n');
% Remove empty lines
M = M(~cellfun('isempty',M));
% Input arguments
assignin('base', 'args', args);
% Skipping first line: function [...] = func_name(...)
% Skipping last line : end
for k=2:length(M)-1
try
% Execute each line
evalin('base',M{k})
% Execute your custom function
disp(['Performing Action: ' M{k}]);
catch ME
error('RunFromTo:ScriptError',...
[ME.message '\n\nError in ==> ' mfile ' at ' num2str(k) '\n\t' M{k}]);
end
end
end
Usage:
>>runEachLine('foo.m', {4});
Result:
>> runEachLine('foo.m', {4})
Performing Action: a = args{1};
Performing Action: b = 2;
Performing Action: c = a + b;
>>
No, there is not.
What you present in your original question is just a simple usage of the = operator.
If you want to overload the default behaviour of MATLAB then you should consider creating a class and define the desired behaviour for each operator or function needed.
I have a text file with two columns of data. I want to split this file and save it as two individual strings in matlab, but I also need to stop copying the data when I meet an identifier in the data then stat two new strings.
For example
H 3
7 F
B B
T Y
SPLIT
<>
Where SPLIT <> is where I want to end the current string.
I'm trying to use fopen and fscanf, but struggling to get it to do what I want it to.
I tried the following script on the example you provided and it works. I believe the comments are very self explanatory.
% Open text file.
fid = fopen('test.txt');
% Read the first line.
tline = fgetl(fid);
% Initialize counter.
ii = 1;
% Check for end string.
while ~strcmp(tline, 'SPLIT')
% Analyze line only if it is not an empty one.
if ~strcmp(tline, '')
% Read the current line and split it into column 1 and column 2.
[column1(ii), column2(ii)] = strread(tline, ['%c %c']);
% Advance counter.
ii = ii + 1;
end
% Read the next line.
tline = fgetl(fid);
end
% Display results in console.
column1
column2
% Close text file.
fclose(fid);
The key functions here are fgetl and strread. Take a look at their documentation, it has some very nice examples as well. Hope it helps.
Given a file name, how can I programmatically distinguish between scripts and functions in MATLAB?
If I attempt to pass an argument to a script, I get Attempt to execute SCRIPT somescript as a function:. Is there a way to detect this without attempting to execute it?
Update: As #craq pointed out, shortly after this question was posted, there was an article about this on MATLAB Central: http://blogs.mathworks.com/loren/2013/08/26/what-kind-of-matlab-file-is-this/
Didn't find a clean solution, but you can probably use try-catch (as #Ilya suggested) and nargin
EDIT - Use function to avoid some naming conflict; use exist to further classify the input (e.g. MEX-file)
function is_script = is_a_script( varargin )
% is_a_script( varargin ) returns one of the following:
% 1: if the input is a script
% 0: if the input is a function
% -1: if the input is neither a function nor a script.
is_script = 0;
switch( exist(varargin{1}) )
case 2
% If the input is not a MEX or DLL or MDL or build-in or P-file or variable or class or folder,
% then exist() returns 2
try
nargin(varargin{1});
catch err
% If nargin throws an error and the error message does not match the specific one for script, then the input is neither script nor function.
if( strcmp( err.message, sprintf('%s is a script.',varargin{1}) ) )
is_script = 1;
else
is_script = -1;
end
end
case {3, 4, 5, 6} % MEX or DLL-file, MDL-file, Built-in, P-file
% I am not familiar with DLL-file/MDL-file/P-file. I assume they are all considered as functions.
is_script = 0;
otherwise % Variable, Folder, Class, or other cases
is_script = -1;
end
If you are willing to use semi-documented features, here is something to try:
function tf = isfunction(fName)
t = mtree(fName, '-file');
tf = strcmp(t.root.kind, 'FUNCTION');
end
This is the same function used in MATLAB Cody and Contests to measure code length.
This is a bit of a hack, but... here is a function that will return true if the argument is a function, and false if it's not. It is possible that there are exceptions where this won't work - I look forward to comments.
EDIT - catching the case where the function is in a mex file...
function b = isFunction(fName)
% tries to determine whether the entity called 'fName'
% is a function or a script
% by looking at the file, and seeing if the first line starts with
% the key word "function"
try
w = which(fName);
% test for mex file:
mx = regexp(w, [mexext '$']);
if numel(mx)>0, b = true; return; end
% the correct thing to do... as shown by YYC
% if nargin(fName) >=0, b = true; return; end
% my original alternative:
fid = fopen(w,'r'); % open read only
while(~feof(fid))
l = fgetl(fid);
% strip everything after comment
f = strtok(l, '%');
g = strtok(f, ' ');
if strcmpi(g, 'function'), b=true; break; end
if strlen(g)>0, b=false; break; end
end
fclose(fid);
catch
fprintf(1, '%s not found!\n');
b = false;
end