3

How to extract the first line of a simple matlab file and the following lines just after that begin with the character % ? The number of lines to extract is unknown. Then I want to use listingsutf8 to display the code extracted.

Example of matlab file :

function myfunction(args)
% Comments
% about the function
% with an unknown number of lines
command1(); % another comment
command2();
% another comment
command3();
end

lines to extract :

function myfunction(args)
% Comments
% about the function
% with an unknown number of lines
4

3 Answers 3

6

You can do that with a combination of relatively arcane listings internal macros; no need to write to an external file. For convenience, I've defined a new boolean key called onlyheader. If that key is set, any output after the first contiguous block of comment lines (i.e. the function header) is dropped.

Edit: This feature has been implemented in version 0.2 of matlab-prettifier via a key called mlonlyheader.

mlpr output

\documentclass{article}

\usepackage[T1]{fontenc}

% --- write to file ---
\usepackage{filecontents}
\begin{filecontents*}{code.txt}
function myfunction(args)
%MYFUNCTION One-line description goes here
% Comments
% about the function
% with an unknown number of lines
[ 2 3 4 ]
command1(); % another comment
command2();
% another comment
command3();
end
\end{filecontents*}

\usepackage[framed]{matlab-prettifier}

\lstset{style = Matlab-editor}

\begin{document}

\lstinputlisting
  [caption  = {\texttt{mlonlyheader=false}}]
  {code.txt}

\lstinputlisting
[
  caption    = {\texttt{mlonlyheader=true}},
  mlonlyheader = true,
]{code.txt}

\end{document}

Using only listings

listings output

\documentclass{article}

% --- write to file ---
\usepackage{filecontents}
\begin{filecontents*}{code.txt}
function myfunction(args)
%MYFUNCTION One-line description goes here
% Comments
% about the function
% with an unknown number of lines
[ 2 3 4 ]
command1(); % another comment
command2();
% another comment
command3();
end
\end{filecontents*}

\usepackage{listings}

\makeatletter
% We define a new boolean key for convenience
\lst@Key{onlyheader}{false}[t]{\lstKV@SetIf{#1}\lst@ifonlyheader}

% We'll use this switch to keep track of where we are
\newif\iffirstnoncommentline

% --- Careful! the following modifications are global ---
% (i.e. will apply to all listings)
\lst@AddToHook{PreInit}{\global\firstnoncommentlinetrue}
\lst@AddToHook{Output}{\dropOutput}

\lst@AddToHook{OutputOther}{\dropOutput}

% helper macro
\newcommand\dropOutput
{%
  \lst@ifonlyheader%
    \ifnum\lst@lineno>1%  
      \lst@ifLmode%
      \else
        \iffirstnoncommentline%
          \lst@EnterMode\lst@Pmode{}%
          \lst@BeginDropOutput\lst@Pmode%
        \fi
        \global\firstnoncommentlinefalse%
      \fi
    \fi
  \fi
}
\makeatother

\lstset{
  language = Matlab,
  frame    = single,
}

\begin{document}

\lstinputlisting
  [caption  = {\texttt{onlyheader=false}}]
  {code.txt}

\lstinputlisting
[
  caption    = {\texttt{onlyheader=true}},
  onlyheader = true,
]{code.txt}

\lstinputlisting
[
  caption    = {\texttt{onlyheader=true}},
  onlyheader = true,
]{code.txt}

\end{document}
3
  • @badboy I hadn't anticipated such a need for my matlab-prettifier package. Can you tell me why you want to extract the header? Documentation purposes?
    – jub0bs
    Commented Mar 8, 2014 at 10:29
  • yes it's for documentation :)
    – badboy
    Commented Mar 9, 2014 at 0:00
  • 1
    oh yeah, great ;) In my case it is documentation of functions I coded so I know what I write in the 'header' but it's not the case for built-in functions ...
    – badboy
    Commented Mar 9, 2014 at 0:18
3

File handling in this manner is not something that is necessarily best handled all-in-one with TeX. It would be much better to do the processing outside of TeX, and then use the result inside TeX.

This method (abstracted from How can I save shell output to a variable in LaTeX?) is somewhat of a compromise. It uses the write18 TeX primitive to run a shell command, which saves the output you seek into a temporary file. This temporary file is then used as the listing source.

I am still working out if/how this can be done with all TeX primitives, not using the shell command workaround.

\documentclass{article}
\usepackage{listingsutf8}    
\begin{document}

% Execute a sed script to identify the lines that are desired
% from the top of your code file (note that the % sign has to be 
% escaped in this line, due to LaTeX interpreting it differently)
% This command was developed with sed on Mac OSX 10.9
\immediate\write18{sed -ne '1 h; 2,/^[^\%]/ {x;p;}' myfunction.txt > '\jobname.temp'}
                % Sed command:
                % 1 h; Take first line, hold in buffer
                % 2,/^[^%]/ Lines 2 through the next line that doesn't
                %     begin with a %
                % ... {x;p;}  Hold current line in buffer (swap with previous)
                %             and then print out previously held line
                % This results in line 1 + next continuous lines beginning with % printed

% Set language -- looked like MATLAB was a prime candidate given the syntax
\lstset{language=Matlab}

% Print out original function
Contents of \verb!myfunction.txt!:
\lstinputlisting{myfunction.txt}

% Print out newly created file
Dynamic header of \verb!myfunction.txt!:
\lstinputlisting{\jobname.temp}

% Clean up temporary file
\immediate\write18{rm -f -- '\jobname.temp'}

\end{document}

The result is

Result of code example

1
  • thx for your answer
    – badboy
    Commented Mar 7, 2014 at 21:57
2

The filecontents package is used only to create the files to be input.

The code fetches the asked for lines. They may be manipulated directly, or with the listings package. For this I again write the lines to a file and input them: there surely is something easier and more elegant, but it turns out I am completely unfamiliar with listings. [The update adds \lstset{language=Matlab}]

Notice that no shell-escape nor external tools are needed in this approach. The macro \GetHeaderAndDisplayWithListing does the job in one go. I guess the listing itself is customizable via \lstset but I have reached only page 3 of the manual.

\documentclass{article}

\usepackage{listings}

\usepackage{filecontents}% only to create files for this example
\begin{filecontents*}{badboysamplefile.txt}
function myfunction(args)
% Comments
% about the function
% with an unknown number of lines
command1(); % another comment
command2();
% another comment
command3();
end
\end{filecontents*}

\begin{filecontents*}{badboyotherfile.txt}
function myfunction(args)
% 1 Comments
% 2 about the function
% 3 with an unknown number of lines
% 4 Comments
% 5 about the function
% 6 with an unknown number of lines
% 7 comments
% 1 Comments
% 2 about the function
% 3 with an unknown number of lines
% 4 Comments
% 5 about the function
% 6 with an unknown number of lines
% 7 comments
command1(); % another comment
command2();
% another comment
command3();
end
\end{filecontents*}

\makeatletter

\def\ExtractLines #1{%
 \newread\badboy 
 \openin\badboy #1\relax 
 \edef\ELrestore{\endlinechar\the\endlinechar\relax\catcode`\%=14 }%
 \endlinechar -1
 \ExtractLines@
 \ELrestore
 %\show\ELrestore
}%

\def\ExtractLines@ {%
    \ifeof\badboy 
       \def\ExtractedLines{}\closein\badboy
    \else
      \read\badboy to \ExtractedLines
      \edef\ExtractedLines{\detokenize\expandafter{\ExtractedLines}}%
      \catcode`\% 12
      \ExtractLines@@
    \fi
}

\def\ELSEP{\par}
\def\ELgetfirst #1#2\ELgetfirst {\def\ELFirst{#1}}

\catcode`\% 12
\catcode`! 14
\def\ExtractLines@@ {!
  \ifeof\badboy \closein\badboy\else
    \read\badboy to \Extract@OneLine
    \edef\Extract@@OneLine{\detokenize\expandafter{\Extract@OneLine}}!
    \expandafter\ELgetfirst\Extract@@OneLine.\ELgetfirst
    \if %\ELFirst 
      \expandafter\expandafter\expandafter
      \def\expandafter\expandafter\expandafter
      \ExtractedLines\expandafter\expandafter\expandafter
           {\expandafter\ExtractedLines\expandafter\ELSEP\Extract@@OneLine}!
      \expandafter\expandafter\expandafter
      \ExtractLines@@
    \else
       \closein\badboy
    \fi
  \fi
}
\catcode`% 14
\catcode`\! 12
\makeatother

\newcommand\GetHeaderAndDisplayWithListing [1]{%
   \def\ELSEP {^^J}%
   \ExtractLines {#1}%
   \newwrite\badboy
   \immediate\openout\badboy badboy-extracted.temp\relax
   \immediate\write\badboy {\ExtractedLines}%
   \immediate\closeout\badboy\relax
   \lstinputlisting {badboy-extracted.temp}%
   \def\ELSEP {\par}% just in case one wants to use \ExtractLines
   % and the produced \ExtractedLines directly
}

\begin{document}
% added in update:
\lstset{language=Matlab}

First file with \verb|listings|:\medskip

\GetHeaderAndDisplayWithListing {badboysamplefile.txt}

% \ExtractLines {badboysamplefile.txt}%
%  \texttt{\ExtractedLines}
% \bigskip

And the second file with \verb|listings|:\medskip

% \ExtractLines {badboyotherfile.txt}%
% \texttt{\ExtractedLines}

\GetHeaderAndDisplayWithListing {badboyotherfile.txt}

\end{document}

The output (now using \lstset{language=Matlab}):

extracted lines with listings Matlab

The output of the initial answer (which did not even need package listings), using the now commented out \ExtractLines and \texttt{\ExtractedLines}:

extract lines

3
  • Are you able to use the result of \ExtractedLines as the input to a listings environment?
    – cslstr
    Commented Mar 7, 2014 at 21:03
  • I am novice with listings. But writing to a file and re-inputing with \lstinputlisting should definitely work, at least. (one would use something else than \par then as line separator).
    – user4686
    Commented Mar 7, 2014 at 21:20
  • @jfbu thx for your answer
    – badboy
    Commented Mar 7, 2014 at 21:57

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .