Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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
share|improve this question
    
Are you using a language predefined by listings, such as Matlab? –  Jubobs Mar 7 at 20:37
    
yes it's true it is matlab syntax ;) but I use mcode package (mathworks.com/matlabcentral/fileexchange/…) –  badboy Mar 7 at 21:48
    
Consider using the matlab-prettifier package; it improves upon mcode in many ways. See this answer. –  Jubobs Mar 7 at 21:49
    
interesting, thx :) –  badboy Mar 7 at 21:54

3 Answers 3

up vote 6 down vote accepted

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}
share|improve this answer
    
@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? –  Jubobs Mar 8 at 10:29
    
yes it's for documentation :) –  badboy Mar 9 at 0:00
    
@badboy Oops. My mistake. I forgot to reset a switch. See my edit. I'll take that feature onboard. One issue I've noticed, though, is that, in Matlab function files, the block of comments following the first line may contain more comments than are relevant to the function header itself. Try to apply my code to the interp1 function, for instance, and you'll see what I mean; the last line retained by my onlyheader option doesn't actually belong in the header, but describes the first line of code. Unfortunately, there is little I can do about that. –  Jubobs Mar 9 at 0:09
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 Mar 9 at 0:18
    
There is a strange behavior when the first (non empty) line after the header begin with a square bracket [ . –  badboy Mar 12 at 16:12

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

share|improve this answer
    
thx for your answer –  badboy Mar 7 at 21:57

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

share|improve this answer
    
Are you able to use the result of \ExtractedLines as the input to a listings environment? –  cslstr Mar 7 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). –  jfbu Mar 7 at 21:20
    
@jfbu thx for your answer –  badboy Mar 7 at 21:57

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.