努力实现基于文件系统的包含

努力实现基于文件系统的包含

我正在尝试跟随这个答案根据文件系统层次结构自动包含文件。

我的文件结构如下:

article.tex
sections/
|-- 00-introductions.tex
|-- 10-data.tex
|-- 20-analysis.tex
|-- 90-conclusions.tex
|-- data/
|   |-- 00-xxx.tex 
|   |-- NN-xxx.tex
|
|-- analysis/
    |-- 00-xxx.tex
    |-- NN-xxx.tex

因此,我想将 中的所有内容、 中的所有内容以及 中的所有内容都包括在内sections。就article.tex这个问题而言,我们只关注。sections/data10-data.texsections/analysis20-analysis.texarticle.tex

为了尝试遵循链接的答案,我创建了以下 bash 脚本来生成适当文件的列表:

#! /bin/bash
ls -mp sections/ | grep -v / | tr ' ' '\n' > sections.list

我已将其添加到我的,.latexmkrc以便它在每个版本上运行(这部分有效)。它输出以下内容(如果相关,则有新行):

00-introduction.tex,
10-data.tex,
20-analysis.tex,
30-conclusions.tex,

我还尝试了不添加逗号的替代方法。

我的articles.tex(无意义的)看起来像这样:

\documentclass[twocolumn, a4paper]{article}

\usepackage{pgffor}

\newcommand*{\listofsections}{\input{sections.list}}

\begin{document}

\foreach \c in \listofsections {%
    \input{sections/\c}%
}%

\end{document}

如果我只是输出\listofsections它对我来说看起来很好:

文件列表

看起来好像我的循环定义出了问题,因为日志中出现了这些错误:

/home/james/repos/discos-paper/article.tex:28: 缺少插入的 endcsname。 let l.28 } % /home/james/repos/discos-paper/article.tex:28: ??? 的使用与其定义不符。 ???
!LaTeX3 错误:文件名中的 let 无效。 丢失:reserved@d =... l.28 } % /home/james/repos/discos-paper/article.tex:28: 缺少插入的 endcsname。 let l.28 } % /home/james/repos/discos-paper/article.tex:28: ??? 的使用与其定义不符。 ???
!LaTeX3 错误:文件名中的 let 无效。 丢失:reserved@d =... l.28 } % /home/james/repos/discos-paper/article.tex:28: 缺少插入的 endcsname。 let l.28 } % /home/james/repos/discos-paper/article.tex:28: ??? 的使用与其定义不符。???
! LaTeX3 错误:文件名中的 let 无效。丢失:reserved@d =... l.28 } %

!LaTeX 错误:未找到文件“sections/.tex”。

我在这里遗漏了什么?


编辑1:

出于某种原因,似乎我的 \listofsections 作为单个项目通过,并且迭代器仅被调用一次......

我现在也尝试编辑列表文件如下:

00-introduction.tex,10-data.tex,20-analysis.tex,30-conclusions.tex

它仍然不起作用,即使我把它放在里面也不起作用,\newcommand但奇怪的是,如果我将内容直接复制到 foreach 循环中它也会起作用...所以说实话,我真的不确定出了什么问题。

答案1

如果sections.list每行有一个文件名(没有逗号)

00-introduction.tex
10-data.tex
20-analysis.tex
30-conclusions.tex

您可以一次读取一行来获取文件名,然后输入:

\documentclass{article}
\newread\sections
\openin\sections=sections.list
\begin{document}

\loop
{\endlinechar=-1 \global\read\sections to \next}
\ifx\next\empty\else
\input{\next}
\repeat

\end{document}

答案2

我不认为那\foreach是正确的工具,因为它以组的形式执行循环中的每个项目。

您不是将其定义\listofsections为列表,而是定义为输入文件的命令。

\begin{filecontents*}{\jobname-sections.list}
00-introduction.tex,
10-data.tex,
20-analysis.tex,
30-conclusions.tex,
\end{filecontents*}

\documentclass[twocolumn, a4paper]{article}

\usepackage{pgffor}
\usepackage{catchfile}

\CatchFileDef{\listofsections}{\jobname-sections.list}{}

\begin{document}

\foreach \c in \listofsections {%
  \if\relax\c\relax
  \else
    %\input{sections/\c}%
    I would input ``sections/\c''\endgraf
  \fi
}

\end{document}

我更改了名称只是为了使“tryout”文件夹中的文件名保持可区分,并使用了“mock \input”来显示发生了什么。这\if\relax\c\relax是为了避免出现虚假的最终输入。

在此处输入图片描述

我喜欢的方式是

\begin{filecontents*}{\jobname-sections.list}
00-introduction.tex,
10-data.tex,
20-analysis.tex,
30-conclusions.tex,
\end{filecontents*}

\documentclass[twocolumn, a4paper]{article}

\ExplSyntaxOn

\NewDocumentCommand{\inputfromlist}{m}
 {% #1 is the name of the file containing the list
  % store the contents of the file in \l_tmpa_tl
  \file_get:nnN { #1 } { } \l_tmpa_tl
  % transform the tl into a clist
  \clist_set:NV \l_tmpa_clist \l_tmpa_tl
  % cycle through the items i the list
  \clist_map_inline:Nn \l_tmpa_clist
   {
    %\input{##1}
    I~would~input~``sections/##1''\par
   }
 }

\ExplSyntaxOff

\begin{document}

\inputfromlist{\jobname-sections.list}

\end{document}

该函数\file_get:nnNexpl3的模拟函数\CatchFileDef。然后我将内容转换为 clist(空项将自动删除)并映射它。

做什么\clist_set:NV \l_tmpa_clist \l_tmpa_tl?它与

\clist_set:Nn \l_tmpa_clist { <contents of \l_tmpa_tl> }

\clist_set:Nn函数进行规范化,因此删除空项(即由零个或多个空格组成)。

最后,该\clist_map_inline:Nn函数循环遍历列表的项目。当前项目用 表示#1,但由于我们位于命令的定义中,因此必须使用##1

答案3

如果不使用软件包来执行此操作,我可能会让 TeX 切换到 verbatim-catcode-régime,\endlinechar表示回车符的代码点编号,并通过可扩展输入文件列表\@@input并将其处理为回车符分隔的参数列表。

(传统 TeX 的内部字符编码方案是 ASCII。LuaTeX 和 XeTeX 的内部字符编码方案是 unicode,而 ASCII 是其严格子集。回车符的代码点数在 ASCII 和 unicode 中都是 13。在 .tex 输入文件中,回车符可以在 TeX 的^^-notation 中通过表示^^M;M 是拉丁字母中的^^13 个字母。 -notation 要求将类别代码 7(上标)分配给字符^,这是默认值。)

这样文件名就按照逐字逐句的编码规则读取。如果文件名包含空格字符序列和/或诸如等字符,则这可能是一个功能^,这些字符通常/可能被 TeX 以特殊方式处理,或者(如{})在宏参数(如宏的参数)中出现时需要平衡\input

\futurelet仅用于确保 TeX 向前看,并据此删除文件末尾的 eof 标记,否则file ended while scanning the use of...在抓取标记循环末尾的\inputfileloop最后一个/附加的-dummy-line 时可能会触发 -error。(或者,也可以使用它来附加最后一个 dummy-line。)\relax\everyeof

\begin{filecontents*}{\jobname_inputfiles.txt}
00-introduction.tex
10-data.tex
20-analysis.tex
30-conclusions.tex
40-weird}%}  {^#^^61^^!.tex
\end{filecontents*}

\documentclass{article}

\begingroup
\makeatletter\endlinechar=-1\relax\catcode`\^^M=12 %
\@firstofone{%
  \endgroup
  \NewDocumentCommand\inputfiles{mv}{%
     \begingroup
     \let\do\@makeother\dospecials\do\^^M\do\^^I%
     %\endlinechar=`\^^M\relax %<- This is the default, thus this line is turned into a comment.
     \def\temp{\inputfileloop{}{#2}}%
     \expandafter\futurelet\expandafter\tempb\expandafter\temp\@@input "#1" \relax^^M%
  }%
  \@ifdefinable\inputfileloop{%
    % recursively in #1 accumulate a list of \input-commands
    %   #1 list of \input-commands gathered so far
    %   #2 directory
    %   #3 filename or \relax-token to process in this iteration.
    \long\def\inputfileloop#1#2#3^^M{%
      \ifx\relax#3\expandafter\@secondoftwo\else\expandafter\@firstoftwo\fi
      % Use this for inputting:
      %{\def\temp{\inputfileloop{#1\input{#2#3}}{#2}}\futurelet\tempb\temp}{\endgroup#1}%
      % Use this for message on terminal instead of inputting:
      {\def\temp{\inputfileloop{#1\message{I would \string\input{#2#3}^^J}}{#2}}\futurelet\tempb\temp}{\endgroup#1}%
    }%
  }%
}%

\begin{document}

\inputfiles{\jobname_inputfiles.txt}{section/}

\end{document}

终端上的消息为:

I would \input{section/00-introduction.tex}
I would \input{section/10-data.tex}
I would \input{section/20-analysis.tex}
I would \input{section/30-conclusions.tex}
I would \input{section/40-weird}%}  {^#^^61^^!.tex}

对于最后一条消息,-command 的参数\input是:(
section/section/40-weird}%}␣␣{^#^^61^^!.tex
表示空格字符。)

形成该参数的标记序列在逐字分类代码机制下被标记化,因此

  • 多个空间不会合并成一个空间。
  • ^^-notation 被禁用,即^^61^^!未被标记为aa
  • {并且}不被视为用于确定范围/表示参数的括号,而是被视为普通字符。
  • #被视为普通字符,可以在从其参数定义临时宏的宏的参数中不加倍使用。
  • %被视为普通字符

我刚刚想到,当处理包含输入文件列表的文件时,您可能希望产生空行:

% Let's within the current directory/folder create the file holding
% the list of input-files:

\begin{filecontents*}{\jobname_inputfiles.txt}
00-introduction.tex
10-data.tex
20-analysis.tex
30-conclusions.tex

mysubsection/40-bla.tex
50-weird}%}  {^#^^61^^!.tex
\end{filecontents*}


\documentclass{article}

\begingroup
\makeatletter\endlinechar=-1\relax\catcode`\^^M=12 %
\@firstofone{%
  \endgroup
  \NewDocumentCommand\inputfiles{mv}{%
     \begingroup
     \let\do\@makeother\dospecials\do\^^M\do\^^I%
     %\endlinechar=`\^^M\relax %<- This is the default, thus this line is turned into a comment.
     \def\temp{\inputfileloop{}{#2}}%
     \expandafter\futurelet\expandafter\tempb\expandafter\temp\@@input "#1" \relax^^M%
  }%
  \@ifdefinable\inputfileloop{%
    % recursively in #1 accumulate a list of \input-commands
    %   #1 list of \input-commands gathered so far
    %   #2 directory;  slash etc needs to be included so you can leave it empty if you like.
    %   #3 filename or \relax-token to process in this iteration.
    \long\def\inputfileloop#1#2#3^^M{%
      \ifx\relax#3\expandafter\@secondoftwo\else\expandafter\@firstoftwo\fi
      {%
        \def\temp{%
          \ifcat$\detokenize{#3}$\expandafter\@gobble\else\expandafter\inputfileappendtolist\fi
          {#3}\inputfileloop{#1}{#2}%
        }%
        \futurelet\tempb\temp
      }{\endgroup#1}%
    }%
  }%
  \newcommand\inputfileappendtolist[4]{%
    #2{#3%
      % Use this for inputting:
      %\input{#4#1}%
      % Use this for message on terminal instead of inputting:
      \message{I would \string\input{#4#1}^^J}%
    }{#4}%
  }%
}

\begin{document}

\inputfiles{\jobname_inputfiles.txt}{mysection/}

\end{document}

终端上的消息为:

I would \input{mysection/00-introduction.tex}
I would \input{mysection/10-data.tex}
I would \input{mysection/20-analysis.tex}
I would \input{mysection/30-conclusions.tex}
I would \input{mysection/mysubsection/40-bla.tex}
I would \input{mysection/50-weird}%}  {^#^^61^^!.tex}

对于最后一条消息,-command 的参数\input是:(
mysection/50-weird}%}␣␣{^#^^61^^!.tex
表示空格字符。)

相关内容