从文件读取元数据

从文件读取元数据

说出所有家庭作业文件:hw1.tex, hw2.tex, ...使用一些常见的homework.cls问题是,在每个hw*.tex文件中这些变量都必须手动设置。

\documentclass{homework}
\author{John Smith}
\course{Math101}
\date{2022-10-16}
\hwnum{1}

\begin{document} {...} \end{document}

我的目标是消除重复的行:\author{}, \course{}, ...将它们包含在所有人共享的元数据文件中hw*.tex。正如其他人所注释的那样,这个问题可以通过以下方式解决:

\documentclass{homework}
\input{./metadata.tex}
\hwnum{1}

\begin{document} {...} \end{document}

剩下的就是消除(1)\input{./metadata.tex}和(2)\hwnum{1}

  1. \input{./metadata.tex}可以通过来解决homework.cls。有\input没有办法在找不到文件时停止抱怨?

  2. 是否可以hwnum从文件名进行解析。例如:如果文件名是 hw39.tex,则homework.cls自动设置\hwnum{39}

答案1

广告 1:

\InputIfFileExists{⟨file-name⟩}{}{}应该可以解决问题。

广告 2:

如果仅是为了获取主 .tex 文件名称的第一位数字序列(删除前导零),并且可以通过扩展 -primitive 获得该文件的名称(即,调用 LaTeX 时未使用\jobname命令行选项),您可以尝试使用 expl3 :-job-name\regex_extract_once:nnN

文件:hw39.tex

\ExplSyntaxOn
\cs_new:Npn \DefineFromFirstDigitsMatch #1#2 {
  \group_begin:
  \regex_extract_once:nnN {\d+}{#2}\l_tmpa_seq
  \seq_get_left:NN \l_tmpa_seq \l_tmpa_tl
  \exp_args:Nnf \use:n {\group_end: \cs_new:Npn #1} 
                {\exp_args:No \quark_if_no_value:nTF {\l_tmpa_tl}
                                                     {\exp_stop_f:}
                                                     {\int_value:w \l_tmpa_tl}}
}
\ExplSyntaxOff

\DefineFromFirstDigitsMatch{\macroa}{\foo 00123 bar\fi 45 whoozle \endcsname { 006 {777}} dd890jj}
\DefineFromFirstDigitsMatch{\macrob}{\foo {{00123}} bar\fi 45 whoozle \endcsname { 006 {777}} dd890jj}
\DefineFromFirstDigitsMatch{\macroc}{hh}
\expandafter\DefineFromFirstDigitsMatch\expandafter{\expandafter\macrod\expandafter}\expandafter{\jobname}

\documentclass{article}

\begin{document}

\ttfamily
\noindent\string\macroa=\meaning\macroa

\noindent\string\macrob=\meaning\macrob

\noindent\string\macroc=\meaning\macroc

\noindent\string\macrod=\meaning\macrod

\end{document}

在此处输入图片描述

请注意,这在 Overleaf 中不起作用,因为使用-jobname-commandline-option 调用 Overleaf TeXLive-binaries 会导致\jobname-primitive 传递短语output而不是主 .tex 文件的名称。


当你指出只需要第一个匹配的数字序列的评论到达网站时,我刚刚写完一个通用例程

\ExtractDigitSequences{⟨argument⟩}

它从参数中提取每个数字序列(删除前导零)并将其嵌套在花括号中,以便您获得未分隔的参数列表。

例如,\ExtractDigitSequences{\foo 12 bar\fi 345 whoozle \endcsname { 006 {777}} dd}收益为{12}{345}{6}{777}

由于\romannumeral-expansion,结果是通过触发正好两个扩展步骤来交付的。

如果可以通过扩展 -primitive 获得文件名\jobname并且仅包含一个数字序列,则可以执行以下操作,例如,

\newcommand\PassFirstToSecond[2]{#2{#1}}

\expandafter\PassFirstToSecond\expandafter{\jobname}{%
  \expandafter\expandafter\expandafter\newcommand
  \expandafter\expandafter\expandafter*%
  \expandafter\expandafter\expandafter\macro
  \ExtractDigitSequences
}%

如果有更多数字序列而您只想要其中一个,则可以将\ExtractDigitSequences与 的使用结合起来\UD@ExtractKthArg

以下是代码:

\errorcontextlines=10000
\makeatletter
%%=============================================================================
%% PARAPHERNALIA:
%% \UD@firstoftwo, \UD@secondoftwo, \UD@PassFirstToSecond, \UD@Exchange,
%% \UD@removespace, \UD@stopromannumeral, \UD@CheckWhetherNull,
%% \UD@CheckWhetherBrace, \UD@CheckWhetherLeadingExplicitSpace,
%% \UD@ExtractKthArg
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@Exchange[2]{#2#1}%
\@ifdefinable\UD@removespace{\UD@Exchange{ }{\def\UD@removespace}{}}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
  \expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument is blank (empty or only spaces):
%%-----------------------------------------------------------------------------
%% -- Take advantage of the fact that TeX discards space tokens when
%%    "fetching" _un_delimited arguments: --
%% \UD@CheckWhetherBlank{<Argument which is to be checked>}%
%%                      {<Tokens to be delivered in case that
%%                        argument which is to be checked is blank>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked is not blank>}%
\newcommand\UD@CheckWhetherBlank[1]{%
  \romannumeral\expandafter\expandafter\expandafter\UD@secondoftwo
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo#1{}{}}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument's first token is a catcode-1-character
%%.............................................................................
%% \CheckWhetherBrace{<Argument which is to be checked>}%
%%                   {<Tokens to be delivered in case that argument
%%                     which is to be checked has a leading
%%                     explicit catcode-1-character-token>}%
%%                   {<Tokens to be delivered in case that argument
%%                     which is to be checked does not have a
%%                     leading explicit catcode-1-character-token>}%
\newcommand\UD@CheckWhetherBrace[1]{%
  \romannumeral\expandafter\UD@secondoftwo\expandafter{\expandafter{%
  \string#1.}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@firstoftwo}{%
  \expandafter\UD@stopromannumeral\UD@secondoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument starts with a space-token
%%.............................................................................
%% \UD@CheckWhetherLeadingExplicitSpace{<Argument which is to be checked>}%
%%                                     {<Tokens to be delivered in case <argument
%%                                       which is to be checked> does have a
%%                                       leading explicit space-token>}%
%%                                     {<Tokens to be delivered in case <argument
%%                                       which is to be checked> does not have a
%%                                       a leading explicit space-token>}%
\newcommand\UD@CheckWhetherLeadingExplicitSpace[1]{%
  \romannumeral\UD@CheckWhetherNull{#1}%
  {\expandafter\UD@stopromannumeral\UD@secondoftwo}%
  {%
    % Let's nest things into \UD@firstoftwo{...}{} to make sure they are nested in braces
    % and thus do not disturb when the test is carried out within \halign/\valign:
    \expandafter\UD@firstoftwo\expandafter{%
      \expandafter\expandafter\expandafter\UD@stopromannumeral
      \romannumeral\expandafter\UD@secondoftwo
      \string{\UD@CheckWhetherLeadingExplicitSpaceB.#1 }{}%
    }{}%
  }%
}%
\@ifdefinable\UD@CheckWhetherLeadingExplicitSpaceB{%
  \long\def\UD@CheckWhetherLeadingExplicitSpaceB#1 {%
    \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
    {\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
    {\expandafter\expandafter\expandafter\UD@stopromannumeral
     \expandafter\expandafter\expandafter}%
     \expandafter\UD@secondoftwo\expandafter{\string}%
  }%
}%
%%-----------------------------------------------------------------------------
%% Extract K-th inner undelimited argument:
%%-----------------------------------------------------------------------------
%% \UD@ExtractKthArg{<integer K>}%
%%                  {<tokens in case list of undelimited args doesn't have a k-th argumnent>}%
%%                  {<list of undelimited args>} %
%% 
%% In case there is no K-th argument in <list of indelimited args> : 
%%   Does deliver <tokens in case list of undelimited args doesn't have a k-th argumnent.
%% In case there is a K-th argument in <list of indelimited args> : 
%%   Does deliver that K-th argument with one level of braces removed.
%%
%% Examples:
%%
%%   \UD@ExtractKthArg{0}{not available}{ABCDE} yields: not available
%%
%%   \UD@ExtractKthArg{3}{not available}{ABCDE} yields:  C
%%
%%   \UD@ExtractKthArg{3}{not available}{AB{CD}E} yields:  CD
%%
%%   \UD@ExtractKthArg{4}{not available}{{001}{002}{003}{004}{005}} yields: 004
%%
%%   \UD@ExtractKthArg{6}{not available}{{001}{002}{003}} yields: not available 
%%.............................................................................
\newcommand\UD@ExtractKthArg[2]{%
  \romannumeral
  % #1: <integer number K>
  % #2: <action if there is no K-th argument>
  \expandafter\UD@ExtractKthArgCheck\expandafter{\romannumeral\number\number#1 000}{#2}%
}%
\newcommand\UD@ExtractKthArgCheck[3]{%
  \UD@CheckWhetherNull{#1}{\UD@stopromannumeral#2}{% empty
    \expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}#1}{#2}{#3}%
  }%
}%
\begingroup
\def\UD@ExtractFirstArgLoop#1{%
  \endgroup
  \@ifdefinable\UD@RemoveTillFrozenrelax{%
    \long\def\UD@RemoveTillFrozenrelax##1##2#1{{##1}}%
  }%
  \newcommand\UD@ExtractKthArgLoop[3]{%
    \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo##3{}.}{\UD@stopromannumeral##2}{%
      \UD@CheckWhetherNull{##1}{%
        \UD@ExtractFirstArgLoop{##3#1}%
      }{%
        \expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}##3}%
        {\expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}##1}{##2}}%
      }%
    }%
  }%
}%
\expandafter\expandafter\expandafter\UD@ExtractFirstArgLoop
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\ifnum0=0\fi}%
%% Usage of frozen-\relax as delimiter is for speeding things up by reducing the
%% amount of iterations needed. I chose frozen-\relax because David Carlisle 
%% pointed out in   <https://tex.stackexchange.com/a/578877>
%% that frozen-\relax cannot be (re)defined in terms of \outer and cannot be
%% affected by \uppercase/\lowercase.
%%
%% \UD@ExtractKthArg's argument may contain frozen-\relax:
%% The only effect is that internally more iterations are needed for
%% obtaining the result.
\newcommand\UD@ExtractFirstArgLoop[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  {\expandafter\UD@stopromannumeral\UD@secondoftwo{}#1}%
  {\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillFrozenrelax#1}}%
}%
%%-----------------------------------------------------------------------------
%% Fork whether argument is digit 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, s.th. else
%%.............................................................................
\@ifdefinable\UD@gobbletoexclam{\long\def\UD@gobbletoexclam#1!{}}%
\@ifdefinable\UD@DigitFork{%
  \long\def\UD@DigitFork#1!1!2!3!4!5!6!7!8!9!0!#2#3!!!!{#2}%
}%
\newcommand\UD@ForkDigit[1]{%
  % #1 argument to examine
  % #2 = {%
  %        {tokens in case digit 1}%
  %        {tokens in case digit 2}%
  %        {tokens in case digit 3}%
  %        {tokens in case digit 4}%
  %        {tokens in case digit 5}%
  %        {tokens in case digit 6}%
  %        {tokens in case digit 7}%
  %        {tokens in case digit 8}%
  %        {tokens in case digit 9}%
  %        {tokens in case digit 0}%
  %        {tokens in case no digit}%
  %      }% 
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@gobbletoexclam#1!}%
  {%
    \expandafter\UD@firstoftwo\expandafter{%
      \UD@DigitFork
      !#1!2!3!4!5!6!7!8!9!0!{\UD@ExtractKthArg{1}{}}% digit 1
      !1!#1!3!4!5!6!7!8!9!0!{\UD@ExtractKthArg{2}{}}% digit 2
      !1!2!#1!4!5!6!7!8!9!0!{\UD@ExtractKthArg{3}{}}% digit 3
      !1!2!3!#1!5!6!7!8!9!0!{\UD@ExtractKthArg{4}{}}% digit 4
      !1!2!3!4!#1!6!7!8!9!0!{\UD@ExtractKthArg{5}{}}% digit 5
      !1!2!3!4!5!#1!7!8!9!0!{\UD@ExtractKthArg{6}{}}% digit 6
      !1!2!3!4!5!6!#1!8!9!0!{\UD@ExtractKthArg{7}{}}% digit 7
      !1!2!3!4!5!6!7!#1!9!0!{\UD@ExtractKthArg{8}{}}% digit 8
      !1!2!3!4!5!6!7!8!#1!0!{\UD@ExtractKthArg{9}{}}% digit 9
      !1!2!3!4!5!6!7!8!9!#1!{\UD@ExtractKthArg{10}{}}% digit 0
      !1!2!3!4!5!6!7!8!9!0!{\UD@ExtractKthArg{11}{}}!!!!% no digit
    }{}% 
  }{\UD@ExtractKthArg{11}{}}% no digit
}%
%%-----------------------------------------------------------------------------
%% Extract sequences of explicit category 12 digit character tokens from
%% argument:
%%-----------------------------------------------------------------------------
%%
%% \ExtractDigitSequences{<argument>}
%%
%% If {<argument>} is, e.g.,   {\foo 12 bar\fi 345 whoozle \endcsname { 006 {777}} dd}
%% , then you get a list of undelimited/brace-nested arguments:
%% {12}{345}{006}{777}
%%
%% Due to \romannumeral-expansion the result is delivered after two
%% expansion-steps.
%%
%%.............................................................................
\newcommand\UD@DigitExtractLoop[3]{%
  % #1 remaining token list to process
  % #2 Numbers gathered so far
  % #3 digits gathered so far
  \UD@CheckWhetherBlank{#1}{%
    \UD@CheckWhetherNull{#3}{\UD@stopromannumeral#2}%
                            {\expandafter\UD@PassFirstToSecond\expandafter{\number#3}{\UD@stopromannumeral#2}}%
  }{%
    \UD@CheckWhetherBrace{#1}{%
      \expandafter\UD@PassFirstToSecond\expandafter{%
        \romannumeral\expandafter\UD@Exchange\expandafter{%
          \romannumeral\expandafter\expandafter\expandafter\UD@DigitExtractLoop
                       \expandafter\expandafter\expandafter{\UD@ExtractKthArg{1}{}{#1}}{}{}%
        }{%
          \UD@CheckWhetherNull{#3}{\UD@stopromannumeral#2}%
                                  {\expandafter\UD@PassFirstToSecond\expandafter{\number#3}{\UD@stopromannumeral#2}}%
        }%
      }%
      {\expandafter\UD@DigitExtractLoop\expandafter{\UD@firstoftwo{}#1}}{}%
    }{%
      \UD@CheckWhetherLeadingExplicitSpace{#1}{%
        \UD@CheckWhetherNull{#3}{%
          \expandafter\UD@DigitExtractLoop\expandafter{\UD@removespace#1}{#2}{}%
        }{%
          \expandafter\expandafter\expandafter\UD@PassFirstToSecond\expandafter\expandafter\expandafter{%
            \expandafter\UD@PassFirstToSecond\expandafter{\number#3}{#2}%
          }{%
            \expandafter\UD@DigitExtractLoop\expandafter{\UD@removespace#1}%
          }{}%
        }%
      }{%
        \expandafter\expandafter\expandafter\UD@ForkDigit
        \expandafter\expandafter\expandafter{\UD@ExtractKthArg{1}{}{#1}}{%
          {\UD@Exchange{{#2}{#31}}}{\UD@Exchange{{#2}{#32}}}{\UD@Exchange{{#2}{#33}}}%
          {\UD@Exchange{{#2}{#34}}}{\UD@Exchange{{#2}{#35}}}{\UD@Exchange{{#2}{#36}}}%
          {\UD@Exchange{{#2}{#37}}}{\UD@Exchange{{#2}{#38}}}{\UD@Exchange{{#2}{#39}}}%
          {\UD@Exchange{{#2}{#30}}}%
          {\UD@CheckWhetherNull{#3}{\UD@Exchange{{#2}{}}}{%
            \expandafter\expandafter\expandafter\UD@Exchange
            \expandafter\expandafter\expandafter{%
            \expandafter\expandafter\expandafter{%
              \expandafter\UD@PassFirstToSecond\expandafter{\number#3}{#2}%
            }{}}%
          }}%
        }%
        {\expandafter\UD@DigitExtractLoop\expandafter{\UD@firstoftwo{}#1}}%
      }%
    }%
  }%
}%
\newcommand\ExtractDigitSequences[1]{\romannumeral\UD@DigitExtractLoop{#1}{}{}}%
\makeatother


\documentclass{article}

\begin{document}

\begin{verbatim}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \ExtractDigitSequences{\foo 12 bar\fi 345 whoozle \endcsname { 006 {777}} dd890jj}%
}%
{\ttfamily \meaning\test}
\end{verbatim}

\noindent yields:
\vskip\partopsep\vskip\topsep

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \ExtractDigitSequences{\foo 12 bar\fi 345 whoozle \endcsname { 006 {777}} dd890jj}%
}%
{\ttfamily\noindent\meaning\test}

\end{document}

在此处输入图片描述

相关内容