说出所有家庭作业文件: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}
。
\input{./metadata.tex}
可以通过来解决homework.cls
。有\input
没有办法在找不到文件时停止抱怨?是否可以
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}