![是否有可能在 LaTeX 中有条件地输入文件?](https://linux22.com/image/417824/%E6%98%AF%E5%90%A6%E6%9C%89%E5%8F%AF%E8%83%BD%E5%9C%A8%20LaTeX%20%E4%B8%AD%E6%9C%89%E6%9D%A1%E4%BB%B6%E5%9C%B0%E8%BE%93%E5%85%A5%E6%96%87%E4%BB%B6%EF%BC%9F.png)
我是 LaTeX 的新手,以下是我想要实现的目标:
- 我的工作目录中有 Markdown 和 LaTeX 文件,比如说
markdown_1.md
和latex_1.tex
。 - 在我的主 LaTeX 文件中
main.tex
,我定义了一个新命令\inject
,该命令用于检查参数是 LaTeX 还是 Markdown 文件。如果文件是 Markdown,则应执行\input{.cache/markdown_1.tex}
和\input{latex_1.tex}
。
为什么
.cache/markdown.tex
?因为我\write18
在文件中有一个命令,可以将 Markdown 转换为 Latex 并将其输出到那里。
到目前为止,我已经通过研究在线的各种 StackExchange 线程进行了以下尝试:
\usepackage{etoolbox}
\makeatletter
\newcommand{\inject}[1]{
\def\textendash{-}%
\filename@parse{#1}%
\edef\filename@base{\detokenize\expandafter{\filename@base}}%
\def\file{\ifstrequal{\filename@ext}{md}{.cache/\[email protected]}{#1}}%
\input{\file}%
}
\makeatother
我称之为:
\inject{markdown_1.md}
\inject{latex_1.tex}
问题在于,当我使用时,PDF 没有生成latexmk
。我已传递-shell-escape
给 pdfLatex。
我怎样才能使它发挥作用?
答案1
问题是\ifstrequal
不扩展其参数。这可以通过强制扩展来解决,但让我基于expl3
及其更丰富的函数和测试提供提出不同的解决方案。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\inject}{m}
{
\utkarsh_inject:n { #1 }
}
\tl_new:N \l__utkarsh_inject_dir_tl
\tl_new:N \l__utkarsh_inject_name_tl
\tl_new:N \l__utkarsh_inject_ext_tl
\cs_new_protected:Nn \utkarsh_inject:n
{
\file_parse_full_name:nNNN
{ #1 } % the argument
\l__utkarsh_inject_dir_tl % stores the directory
\l__utkarsh_inject_name_tl % stores the file name
\l__utkarsh_inject_ext_tl % stores the extension
\tl_if_empty:NTF \l__utkarsh_inject_ext_tl
{% no extension
\file_input:n { #1 }
}
{% there is an extension
\str_if_eq:eeTF { \tl_to_str:N \l__utkarsh_inject_ext_tl } { .tex }
{% it is .tex
\file_input:n { #1 }
}
{% it is not .tex
\file_input:n { .cache/\l__utkarsh_inject_name_tl.tex }
}
}
}
\ExplSyntaxOff
\begin{document}
\inject{test}
\inject{test.tex}
\inject{testmd.md}
\end{document}
我test.tex
在工作目录中创建了一个文件,并testmd.tex
在.cache
其子目录中创建了一个文件。
如果你坚持etoolbox
,你需要逆转测试并扩大\filename@ext
\documentclass{article}
\usepackage{etoolbox}
\makeatletter
\newcommand{\inject}[1]{%
\filename@parse{#1}%
\edef\filename@base{\detokenize\expandafter{\filename@base}}%
\expandafter\ifstrequal\expandafter{\filename@ext}{tex}
{\def\file@to@input{#1}}% the extension is .tex
{\edef\file@to@input{.cache/\[email protected]}}% the extension is not .tex
\input{\file@to@input}%
}
\makeatother
\begin{document}
\inject{test.tex}
\inject{testmd.md}
\end{document}
扩展有在这种情况下,需要指定;也可以添加空扩展测试。
答案2
etoolbox 包的手册说:
\ifstrequal{⟨string⟩}{⟨string⟩}{⟨true⟩}{⟨false⟩}
比较两个字符串并执行⟨真的⟩如果它们相等,并且⟨错误的⟩否则。字符串在测试中不扩展,并且比较与类别代码无关。任何控制序列标记 ⟨细绳⟩参数将被去标记化并视为字符串。此命令非常强大。
“控制序列标记在任何⟨细绳⟩参数将被去标记化并被视为字符串”意味着\ifstrequal
不会扩展/执行可扩展的控制序列(如\filename@ext
),但会将它们转换为类别代码 12(其他)的明确字符标记序列(如果所讨论的字符是空格字符,则分别为 10(空格)(在 TeX 的内部字符编码方案中的代码点编号 32,使用传统 TeX 引擎时为 ASCII,使用基于 XeTeX 或 LuaTeX 的 TeX 引擎时为 unicode/utf-8))。
“这个命令很强大”均值\ifstrequal
是根据 定义的\protected
。这意味着
\ifstrequal
不能是提供给 -command 的文件名的组成部分\input
。\ifstrequal
不会被扩大\edef
。
以下三段代码是帮助您理解“扩展”的初步答案。至于为什么它们只是初步的解释将在后面解释。
在您的场景中,您可能需要手动添加一些扩展控制:
\documentclass{article}
\usepackage{etoolbox}
\makeatletter
\newcommand{\inject}[1]{%
% I don't know why you redefine \textendash, so let's do it within a local scope/group:
% \detokenize is applied in order to turn active characters into harmless characters
% of category code 12(other).
\begingroup
\def\textendash{-}%
\expandafter\filename@parse\expandafter{\detokenize{#1}}%
\expandafter\ifstrequal\expandafter{\filename@ext}{tex}{%
\expandafter\endgroup
\expandafter\def\expandafter\file\expandafter{\detokenize{#1}}%
}{%
\edef\file{\detokenize{.cache/}\detokenize\expandafter{\filename@base}\detokenize{.tex}}%
\expandafter\endgroup
\expandafter\def\expandafter\file\expandafter{\file}%
}%
\input{\file}%
}
\makeatother
\begin{document}
\inject{markdown_1.md}
\inject{latex_1.tex}
\end{document}
使用\romannumeral0
-expansion 可能不需要定义临时宏\file
:
\documentclass{article}
\usepackage{etoolbox}
\makeatletter
\newcommand\exchange[2]{#2#1}%
\newcommand{\inject}[1]{%
\begingroup
% \detokenize is applied in order to turn active characters into harmless characters
% of category code 12(other) and space-characters of whatsoever category code into
% characters of category code 10(space).
\expandafter\filename@parse\expandafter{\detokenize{#1}}%
\expandafter\ifstrequal\expandafter{\filename@ext}{tex}{%
\expandafter\endgroup\expandafter\input\expandafter{\detokenize{#1}}%
}{%
\expandafter\endgroup\expandafter\input\expandafter{%
\romannumeral0%
\expandafter\exchange\expandafter{%
\detokenize{.tex}%
}{%
\expandafter\exchange\expandafter{\detokenize\expandafter{\filename@base}}{%
\expandafter\exchange\expandafter{\detokenize{.cache/}}{ }%
}%
}%
}%
}%
}
\makeatother
\begin{document}
\inject{markdown_1.md}
\inject{latex_1.tex}
\end{document}
如果您使用 -primitive\expanded
可用的引擎,您可能可以执行以下操作:
\documentclass{article}
\usepackage{etoolbox}
\makeatletter
\newcommand{\inject}[1]{%
\begingroup
% \detokenize is applied in order to turn active characters into harmless characters
% of category code 12(other) and space-characters of whatsoever category code into
% characters of category code 10(space).
\expandafter\filename@parse\expandafter{\detokenize{#1}}%
\expandafter\ifstrequal\expandafter{\filename@ext}{tex}{%
\expandafter\endgroup\expandafter\input\expandafter{\detokenize{#1}}%
}{%
\expanded{%
\noexpand\endgroup\noexpand\input{%
\detokenize{.cache/}%
\detokenize\expandafter{\filename@base}%
\detokenize{.tex}%
}%
}%
}%
}%
\makeatother
\begin{document}
\inject{markdown_1.md}
\inject{latex_1.tex}
\end{document}
下面解释一下为什么这三个片段仅形成初步答案:
您在问题中提供的示例和这三个片段中均有\detokenize
使用。
\detokenize
任何状况之下
- 在由控制字标记反标记而得到的字符序列后面附加一个空格字符=控制字标记的名称由多个字符或类别代码为 11(字母)的单个字符组成。
- 在传递空格/对字符代码 32 的显式字符标记进行反标记时,会产生类别代码 10(空格)的空格标记。
- 将类别代码为 6(参数)的哈希字符/显式字符标记加倍,然后将其转换为类别代码 12(其他)的字符。例如,按照您在问题中提供的示例,文件名中的哈希值将在 内翻倍
\filename@base
。
避免哈希值加倍的问题在问题中得到解决使用 \message 防止哈希值重复以及其答案。
避免在控制字标记后面添加空格的问题在我的一个答案。
空格也是文件名的一个问题。由于 TeX 原始语法\input file.tex
(无花括号)中也有空格标记(字符代码 32,类别代码 10(空格))用于将文件名与其他内容分隔开,因此将属于文件名的空格转换为类别代码 12(其他)的标记可能是一个好主意。
另一个问题可能是文件名中的花括号和/或百分比字符不匹配。
除此之外,您可能希望将整个文件路径括在引号 ( "
) 中,以确保您的计算机平台正确处理带有空格的文件名/文件路径。(如果您在允许将引号作为文件名/目录名的一部分的计算机平台上工作,则可能需要执行其他操作。)
如果您只想\inject
在文档级别使用,而从不“从其他宏中”/“从宏参数中”/“从令牌寄存器中”,我建议采用以下路线:
使用解析-package 并\inject
获取 filename-argument 作为 xparse 的 v-argument-types 之一。这将防止哈希加倍。这样,空格后面的控制字标记就不会出现\detokenize
。然后申请\detokenize
将活动字符(可能由于 inputenc-package 而存在)转换为类别代码 12 的字符。然后应用一个例程,用类别代码 12(其他)的空格字符替换类别代码 10(空格)的空格标记\scantokens
:
\documentclass{article}
\usepackage{xparse}
\usepackage{etoolbox}
\makeatletter
\newcommand\exchange[2]{#2#1}%
%%-----------------------------------------------------------------------------
%% 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]{%
\romannumeral0\expandafter\@secondoftwo\string{\expandafter
\@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
\@secondoftwo\string}\@firstoftwo\expandafter{} \@secondoftwo}%
{\@firstoftwo\expandafter{} \@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Replace explicit catcode-10-spaces by explicit catcode-12-spaces in an
%% argument where the control-word-token \relax does not occur and where curly
%% braces are of category code 12(other).
%% All this is the case with tokens delivered by `\detokenize` or `\string`.
%%
%% Apply as
%% \romannumeral0\@UDSpaceReplace<token-list where to replace space-tokens><space-token>\relax{}%
%%
\begingroup
\newcommand\@UDSpaceReplace[1]{%
\endgroup
\long\def\@UDSpaceReplace##1 ##2\relax##3{%
%% ##1 - Tokens before the first space-token.
%% ##2 - Tokens between the first space-token and the end
%% of the token-list where spaces are to be
%% replaced. (The end is marked by \relax as
%% \relax doesn't occur in the token-list.)
%% ##3 - Stuff where Spaces are replaced so far.
%% #1 - Space of category code 12.
\UD@CheckWhetherNull{##2}%
{ ##3##1}{\@UDSpaceReplace##2\relax{##3##1#1}}%
}%
}%
\catcode`\ =12\relax
\@UDSpaceReplace{ }%
%%-----------------------------------------------------------------------------
%% The \inject-command.
%% Don't apply from inside other macro definitions.
%% Don't apply from inside a macro-argument.
%% Don't apply from inside a token-register.
%% Only apply on document-level.
%%.............................................................................
\NewDocumentCommand{\inject}{}{%
\begingroup
\catcode`\^^I=12\relax
\injectwithchangedcatcoderegime
}%
\NewDocumentCommand{\injectwithchangedcatcoderegime}{+v}{%
\expandafter\filename@parse\expandafter{%
\romannumeral0\expandafter\@UDSpaceReplace\detokenize{#1} \relax{}%
}%
\expandafter\ifstrequal\expandafter{\filename@ext}{tex}{%
\expandafter\endgroup\expandafter\input\expandafter{%
\romannumeral0\expandafter\@UDSpaceReplace\detokenize{"#1"} \relax{}%
}%
}{%
\expandafter\endgroup\expandafter\input\expandafter{%
\romannumeral0%
\expandafter\exchange\expandafter{%
\detokenize{.tex"}%
}{%
\expandafter\exchange\expandafter{%
\romannumeral0\expandafter\@UDSpaceReplace\detokenize\expandafter{\filename@base} \relax{}%
}{%
\ifx\filename@area\@empty\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{%
\expandafter\exchange\expandafter{%
\detokenize{".cache/}%
}{ }%
}{%
\expandafter\exchange\expandafter{%
\detokenize{cache/}%
}{%
\expandafter\exchange\expandafter{%
\romannumeral0\expandafter\@UDSpaceReplace\detokenize\expandafter{\expandafter"\filename@area} \relax{}%
}{ }%
}%
}%
}%
}%
}%
}%
}%
\makeatother
\begin{document}
\inject{markdown_1.md}
\inject{latex_1.tex}
\inject{W e i r d{#1fi}le.tex}
\inject{W e i r d{#1fi}le.md}
\inject|W e i / r d}#1fi{le.md|
\end{document}
如果您确实希望使用\inject
“来自其他宏内”/“来自宏参数内”/“来自标记寄存器内”,从而可以在正常的 catcode 制度下而不是逐字 catcode 制度下对事物进行标记,然后将其“传递”到,我建议使用适用于文件路径参数的每个标记的\inject
例程,并在结果中将类别代码 10(空格)的空格替换为类别代码 12(其他)的空格。 — 不同— 不会加倍哈希值,也不会在来自控制字标记的字符序列后面附加空格。然而,这留下了处理包含以下内容的文件名/文件路径\string
\string
\detokenize
- 不平衡的花括号和/或
- 百分比字符/注释字符和/或
- 几个连续的空格字符和/或
- 反斜杠会导致将内容标记为控制序列标记,可能是控制字标记,而 .tex 输入文件中的空格字符不会被标记,
给你:
%% Copyright (C) 2019, 2020 by Ulrich Diez ([email protected])
%%
%% This work may be distributed and/or modified under the
%% conditions of the LaTeX Project Public Licence (LPPL), either
%% version 1.3 of this license or (at your option) any later
%% version. (The latest version of this license is in:
%% http://www.latex-project.org/lppl.txt
%% and version 1.3 or later is part of all distributions of LaTeX
%% version 1999/12/01 or later.)
%% The author of this work is Ulrich Diez.
%% This work has the LPPL maintenance status 'not maintained'.
%% Usage of any/every component of this work is at your own risk.
%% There is no warranty - neither for probably included
%% documentation nor for any other part/component of this work.
%% If something breaks, you usually may keep the pieces.
%%\errorcontextlines=10000
\documentclass{article}
\usepackage{etoolbox}
\makeatletter
%%=============================================================================
%% Paraphernalia:
%% \UD@firstoftwo, \UD@secondoftwo,
%% \UD@PassFirstToSecond, \UD@Exchange, \UD@removespace
%% \UD@CheckWhetherNull, \UD@CheckWhetherBrace,
%% \UD@CheckWhetherLeadingSpace, \UD@ExtractFirstArg
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@Exchange[2]{#2#1}%
\newcommand\UD@removespace{}\UD@firstoftwo{\def\UD@removespace}{} {}%
%%-----------------------------------------------------------------------------
%% 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]{%
\romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\UD@firstoftwo\expandafter{} \UD@secondoftwo}%
{\UD@firstoftwo\expandafter{} \UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument's first token is a catcode-1-character
%%.............................................................................
%% \UD@CheckWhetherBrace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked has leading
%% catcode-1-token>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked has no leading
%% catcode-1-token>}%
\newcommand\UD@CheckWhetherBrace[1]{%
\romannumeral0\expandafter\UD@secondoftwo\expandafter{\expandafter{%
\string#1.}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\UD@firstoftwo\expandafter{} \UD@firstoftwo}%
{\UD@firstoftwo\expandafter{} \UD@secondoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument starts with a space-token
%%.............................................................................
%% \UD@CheckWhetherLeadingSpace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked>'s 1st token is a
%% space-token>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked>'s 1st token is not
%% a space-token>}%
\newcommand\UD@CheckWhetherLeadingSpace[1]{%
\romannumeral0\UD@CheckWhetherNull{#1}%
{\UD@firstoftwo\expandafter{} \UD@secondoftwo}%
{\expandafter\UD@secondoftwo\string{\UD@@CheckWhetherLeadingSpace.#1 }{}}%
}%
\@ifdefinable\UD@@CheckWhetherLeadingSpace{%
\long\def\UD@@CheckWhetherLeadingSpace#1 {%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@secondoftwo#1{}}%
{\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
{\UD@Exchange{ }{\expandafter\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter}\expandafter\expandafter
\expandafter}\expandafter\UD@secondoftwo\expandafter{\string}%
}%
}%
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%
%% \UD@ExtractFirstArg{ABCDE} yields {A}
%%
%% \UD@ExtractFirstArg{{AB}CDE} yields {AB}
%%.............................................................................
\newcommand\UD@RemoveTillUD@SelDOm{}%
\long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}%
\newcommand\UD@ExtractFirstArg[1]{%
\romannumeral0%
\UD@ExtractFirstArgLoop{#1\UD@SelDOm}%
}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
{ #1}%
{\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
%%-----------------------------------------------------------------------------
%% In case an argument's first token is an opening brace, stringify that and
%% add another opening brace before that and remove everything behind the
%% matching closing brace:
%% \UD@StringifyOpeningBrace{{Foo}bar} yields {{Foo} whereby the second
%% opening brace is stringified:
%%.............................................................................
\newcommand\UD@StringifyOpeningBrace[1]{%
\romannumeral0%
\expandafter\UD@ExtractFirstArgLoop\expandafter{%
\romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
\expandafter\expandafter
\expandafter {%
\expandafter\UD@firstoftwo
\expandafter{%
\expandafter}%
\romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
\expandafter\string
\expandafter}%
\string#1%
\UD@SelDOm}%
}%
%%-----------------------------------------------------------------------------
%% In case an argument's first token is an opening brace, remove everything till
%% finding the corresponding closing brace. Then stringify that closing brace:
%% \UD@StringifyClosingBrace{{Foo}bar} yields: {}bar} whereby the first closing
%% brace is stringified:
%%.............................................................................
\newcommand\UD@StringifyClosingBrace[1]{%
\romannumeral0\expandafter\expandafter\expandafter
\UD@StringifyClosingBraceloop
\UD@ExtractFirstArg{#1}{#1}%
}%
\newcommand\UD@CheckWhetherStringifiedOpenBraceIsSpace[1]{%
%% This can happen when character 32 (space) has catcode 1...
\expandafter\UD@CheckWhetherLeadingSpace\expandafter{%
\romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
\expandafter\UD@secondoftwo
\expandafter{%
\expandafter}%
\expandafter{%
\romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
\expandafter\UD@firstoftwo
\expandafter{%
\expandafter}%
\romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
\expandafter\string
\expandafter}%
\string#1%
}%
}%
\newcommand\UD@TerminateStringifyClosingBraceloop[2]{%
\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
\expandafter\expandafter
\expandafter{%
\expandafter\string
\romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
\expandafter#1%
\string#2%
}%
}%
\newcommand\UD@StringifyClosingBraceloopRemoveElement[4]{%
\expandafter\UD@PassFirstToSecond\expandafter{\expandafter
{\romannumeral0\expandafter\UD@secondoftwo\string}{}%
\UD@CheckWhetherStringifiedOpenBraceIsSpace{#4}{%
\UD@Exchange{\UD@removespace}%
}{%
\UD@Exchange{\UD@firstoftwo\expandafter{\expandafter}}%
}{%
\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
\expandafter#1%
\romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
\expandafter
}%
\string#4%
}{\expandafter\UD@StringifyClosingBraceloop\expandafter{#2#3}}%
}%
\newcommand\UD@StringifyClosingBraceloop[2]{%
\UD@CheckWhetherNull{#1}{%
\UD@CheckWhetherStringifiedOpenBraceIsSpace{#2}{%
\UD@TerminateStringifyClosingBraceloop{\UD@removespace}%
}{%
\UD@TerminateStringifyClosingBraceloop{\UD@firstoftwo\expandafter{\expandafter}}%
}%
{#2}%
}{%
\UD@CheckWhetherLeadingSpace{#1}{%
\UD@StringifyClosingBraceloopRemoveElement
{\UD@removespace}{\UD@removespace}%
}{%
\UD@StringifyClosingBraceloopRemoveElement
{\UD@firstoftwo\expandafter{\expandafter}}{\UD@firstoftwo{}}%
}%
{#1}{#2}%
}%
}%
%%-----------------------------------------------------------------------------
%% Stringify each token:
%%
%% \romannumeral0\StringifyLoop{}{<token 1><token 2>...<token n>}
%%
%% yields: <stringification of token 1>%
%% <stringification of token 2>%
%% ...
%% <stringification of token n>%
%%
%% whereby "stringification of token" means the result of applying \string
%% to the token in question.
%%.............................................................................
\newcommand\StringifyLoop[2]{%
\UD@CheckWhetherNull{#2}{ #1}{%
\UD@CheckWhetherBrace{#2}{%
\expandafter\expandafter\expandafter\UD@Exchange
\expandafter\expandafter\expandafter{%
\UD@StringifyClosingBrace{#2}%
}{%
\expandafter\StringifyLoop\expandafter{%
\romannumeral0%
\expandafter\expandafter\expandafter\UD@Exchange
\expandafter\expandafter\expandafter{\UD@StringifyOpeningBrace{#2}}{\StringifyLoop{#1}}%
}%
}%
}{%
\UD@CheckWhetherLeadingSpace{#2}{%
\expandafter\UD@PassFirstToSecond\expandafter{\UD@removespace#2}{%
\StringifyLoop{#1 }%
}%
}{%
\expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#2}{%
\expandafter\StringifyLoop\expandafter{%
\romannumeral0%
\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\UD@Exchange
\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter{%
\expandafter\expandafter\expandafter\string
\expandafter\UD@Exchange
\romannumeral0\UD@ExtractFirstArgLoop{#2\UD@SelDOm}{}%
}%
{ #1}%
}%
}%
}%
}%
}%
}%
%%-----------------------------------------------------------------------------
%% Replace explicit catcode-10-spaces by explicit catcode-12-spaces in an
%% argument where the control-word-token \relax does not occur and where curly
%% braces are of category code 12(other).
%% All this is the case with tokens delivered by `\detokenize` or `\string`
%%
%% Apply as
%% \romannumeral0\@UDSpaceReplace<token-list where to replace space-tokens><space-token>\relax{}%
%%
\begingroup
\newcommand\@UDSpaceReplace[1]{%
\endgroup
\long\def\@UDSpaceReplace##1 ##2\relax##3{%
%% ##1 - Tokens before the first space-token.
%% ##2 - Tokens between the first space-token and the end
%% of the token-list where spaces are to be
%% replaced. (The end is marked by \relax as
%% \relax doesn't occur in the token-list.)
%% ##3 - Stuff where Spaces are replaced so far.
%% #1 - Space of category code 12.
\UD@CheckWhetherNull{##2}%
{ ##3##1}{\@UDSpaceReplace##2\relax{##3##1#1}}%
}%
}%
\catcode`\ =12\relax
\@UDSpaceReplace{ }%
%%-----------------------------------------------------------------------------
%% The \inject-command.
%%.............................................................................
\newcommand\inject[1]{%
\begingroup
\expandafter\filename@parse\expandafter{%
\romannumeral0\expandafter\@UDSpaceReplace\romannumeral0\StringifyLoop{}{#1} \relax{}%
}%
\expandafter\ifstrequal\expandafter{\filename@ext}{tex}{%
\expandafter\endgroup\expandafter\input\expandafter{%
\romannumeral0\expandafter\@UDSpaceReplace\romannumeral0\StringifyLoop{}{"#1"} \relax{}%
}%
}{%
\expandafter\endgroup\expandafter\input\expandafter{%
\romannumeral0%
\expandafter\UD@Exchange\expandafter{%
\romannumeral0\StringifyLoop{}{.tex"}%
}{%
\expandafter\UD@Exchange\expandafter{%
\romannumeral0%
\expandafter\@UDSpaceReplace
\romannumeral0%
\expandafter\StringifyLoop\expandafter{\expandafter}\expandafter{\filename@base} \relax{}%
}{%
\ifx\filename@area\@empty\expandafter\UD@firstoftwo\else\expandafter\UD@secondoftwo\fi
{%
\expandafter\UD@Exchange\expandafter{%
\romannumeral0\StringifyLoop{}{".cache/}%
}{ }%
}{%
\expandafter\UD@Exchange\expandafter{%
\romannumeral0\StringifyLoop{}{cache/}%
}{%
\expandafter\UD@Exchange\expandafter{%
\romannumeral0%
\expandafter\@UDSpaceReplace
\romannumeral0%
\expandafter\StringifyLoop\expandafter{\expandafter}%
\expandafter{\expandafter"\filename@area} \relax{}%
}{ }%
}%
}%
}%
}%
}%
}%
}%
\makeatother
\begin{document}
\inject{markdown_1.md}
\inject{latex_1.tex}
\inject{W e i r d{#1fi}le.tex}
\inject{W e i r d{#1fi}le.md}
\begingroup
\catcode`\[=1
\inject{W e i r d \LaTeX [#{1fi}}le.tex}
\inject{W e i r_ §& d \LaTeX [#{1fi}}le.md}
\inject{W e i r_ /§& d \LaTeX [#{1fi}}le.md}
\endgroup
\end{document}
通过所有这些示例,我进入终端并看到有关未找到文件的 .log 文件错误消息。
但是,如果文件存在并且被找到,这些错误消息将包含用作文件路径的字符串。
顺便说一句: 的值\escapechar
也会影响\string
和\detokenize
。
答案3
您的标准似乎是 a) 使用 LaTeX(即限制/避免 TeX 基元)和 b) 在某些 LaTeX 文件之前输入 markdown 预处理标头。如果是这样,这将满足您的要求。
\documentclass{article}
\usepackage{etoolbox}
\newcommand*{\inject}[2][]{
\ifstrempty{#1}
{\input{#2}}
{%
\input{.cache/#1}
\input{#2}}
}
\begin{document}
% put the file without markdown pre-process header
\inject{mytexfile}
% put the file with markdown pre-process header
\inject[mymdhder]{mytexfile}
\end{document}