\lstinputlisting 总是解析整个源文件

\lstinputlisting 总是解析整个源文件

我正在使用很多\lstinputlisting命令来显示大型源文件中的片段。

例如:

\lstinputlisting[firstline=8244,lastline=8250]{source.c}
\lstinputlisting[firstline=9244,lastline=9250]{source.c}

等等。

编译时间太长了,我确信这是因为listings要解析整个文件,直到firstline每个\lstinputlisting命令。当我执行如下操作时,编译时间很好:

\lstinputlisting[firstline=14,lastline=34]{source.c}
\lstinputlisting[firstline=24,lastline=44]{source.c}

有没有办法避免每次解析整个文件?

答案1

我尝试了一个简单的基准测试,每次使用 TeX 将所需范围复制到临时文件中然后用于lstinputlisting输入该文件实际上比较慢。

%! TEX program = lualatex
\documentclass{article}
\usepackage{listings}
\usepackage{l3benchmark}


\ExplSyntaxOn
\NewDocumentCommand \CopyPartialFile {mmmm} {
\ior_open:Nn \g_tmpa_ior {#1}
\iow_open:Nn \g_tmpa_iow {#2}
\prg_replicate:nn {#3} {
    \ior_str_get:NN \g_tmpa_ior \l_tmpa_str
}
\prg_replicate:nn {#4} {
    \ior_str_get:NN \g_tmpa_ior \l_tmpa_str
    \iow_now:Nx \g_tmpa_iow {\l_tmpa_str}
}
\ior_close:N \g_tmpa_ior
\iow_close:N \g_tmpa_iow
}
\NewDocumentCommand \Repeat {mm} { \prg_replicate:nn {#1} {#2} }
\ExplSyntaxOff


\begin{document}

\ExplSyntaxOn
\benchmark_tic:
\ExplSyntaxOff

\Repeat{100}{%
\lstinputlisting[firstline=5001,lastline=5020]{source.c}%
}

\ExplSyntaxOn
\benchmark_toc:
\ExplSyntaxOff

\ExplSyntaxOn
\benchmark_tic:
\ExplSyntaxOff

\Repeat{100}{%
\CopyPartialFile{source.c}{tmp.c}{5000}{20}%
\lstinputlisting{tmp.c}%
}

\ExplSyntaxOn
\benchmark_toc:
\ExplSyntaxOff


\end{document}

(这使得很可能listings实际上并非如此解析这些行,它只会读取并跳过它们)

TeX 没有“查找文件的第 X 行”的功能(这实际上根本不可能),因此一个明显的解决方案是将整个文件存储到内存中并在需要时提取相关的行。

该解决方案为源代码文件的每一行定义一个控制序列来存储该行内容,然后在需要时将内容写入临时文件即可\lstinputlisting

%! TEX program = lualatex
\documentclass{article}
\usepackage{listings}
\usepackage{l3benchmark}

\ExplSyntaxOn

% ======== this block of code read the file source.c and store it into several control sequences ========
\ior_open:Nn \g_tmpa_ior {source.c}
\int_zero:N \l_tmpa_int
\ior_str_map_variable:NNn \g_tmpa_ior \l_tmpa_str {
    \int_incr:N \l_tmpa_int
    \str_set_eq:cN {l__robert_fileline_ \int_use:N \l_tmpa_int _str} \l_tmpa_str
}
\ior_close:N \g_tmpa_ior

\cs_new:Npn \__robert_use_file_line:n #1 {
    \use:c {l__robert_fileline_ #1 _str} ^^J
}
% ======== this defines a function \WritePartialToFile{targetfilename.c}{firstline}{lastline}
% which writes lines firstline-lastline of source.c to targetfilename.c ========
\NewDocumentCommand \WritePartialToFile {mmm} {
    \iow_open:Nn \g_tmpa_iow {#1}
    \iow_now:Nx \g_tmpa_iow { \int_step_function:nnN {#2} {#3} \__robert_use_file_line:n }
    \iow_close:N \g_tmpa_iow
}

% ======== auxiliary function for benchmarking ========
\NewDocumentCommand \Repeat {mm} { \prg_replicate:nn {#1} {#2} }
\ExplSyntaxOff


\begin{document}

\ExplSyntaxOn
\benchmark_tic:
\ExplSyntaxOff

% ======== example usage of \WritePartialToFile to input a part of the file ========
\Repeat{100}{%
\WritePartialToFile{tmp.c}{5001}{5020}%
\lstinputlisting{tmp.c}%
}

\ExplSyntaxOn
\benchmark_toc:
\ExplSyntaxOff

\end{document}

它比原始代码快 3 倍左右。(因为哈希碰撞虽然速度没有想象的快,但已经足够好了。

相关内容