
Cómo extraer la primera línea de un archivo matlab simpleylas siguientes líneas justo después comienzan con el personaje %
? Se desconoce el número de líneas a extraer. Luego quiero usarlo listingsutf8
para mostrar el código extraído.
Ejemplo de archivo matlab:
function myfunction(args)
% Comments
% about the function
% with an unknown number of lines
command1(); % another comment
command2();
% another comment
command3();
end
líneas para extraer:
function myfunction(args)
% Comments
% about the function
% with an unknown number of lines
Respuesta1
Puedes hacerlo con una combinación de listings
macros internas relativamente arcanas; no es necesario escribir en un archivo externo. Para mayor comodidad, he definido una nueva clave booleana llamada onlyheader
. Si se establece esa clave, se descarta cualquier salida después del primer bloque contiguo de líneas de comentarios (es decir, el encabezado de la función).
Editar: Esta característica se ha implementado enversión 0.2 dematlab-prettifier
a través de una clave llamada mlonlyheader
.
\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}
Usando sololistings
\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}
Respuesta2
El manejo de archivos de esta manera no es algo que necesariamente se maneje mejor todo en uno con TeX. Sería mucho mejor realizar el procesamiento fuera de TeX y luego usar el resultado dentro de TeX.
Este método (abstraído de¿Cómo puedo guardar la salida del shell en una variable en LaTeX?) es una especie de compromiso. Utiliza la write18
primitiva TeX para ejecutar un comando de shell, que guarda el resultado que busca en un archivo temporal. Este archivo temporal se utiliza luego como fuente de listado.
Todavía estoy averiguando si esto se puede hacer con todas las primitivas TeX y cómo hacerlo, sin utilizar la solución alternativa del comando Shell.
\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}
El resultado es
Respuesta3
El filecontents
paquete se utiliza únicamente para crear los archivos que se introducirán.
El código recupera las líneas solicitadas. Podrán manipularse directamente o con el listings
paquete. Para esto vuelvo a escribir las líneas en un archivo y las ingreso: seguramente hay algo más fácil y elegante, pero resulta que no lo conozco en absoluto listings
. [La actualización agrega \lstset{language=Matlab}
]
Tenga en cuenta que en este enfoque no se necesitan shell-escape ni herramientas externas. La macro \GetHeaderAndDisplayWithListing
hace el trabajo de una sola vez. Supongo que el listado en sí se puede personalizar, \lstset
pero solo llegué a la página 3 del 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}
La salida (ahora usando \lstset{language=Matlab}
):
El resultado de la respuesta inicial (que ni siquiera necesitaba el paquete listings
), usando el ahora comentado \ExtractLines
y \texttt{\ExtractedLines}
: