是否有可能在 LaTeX 中有条件地输入文件?

是否有可能在 LaTeX 中有条件地输入文件?

我是 LaTeX 的新手,以下是我想要实现的目标:

  • 我的工作目录中有 Markdown 和 LaTeX 文件,比如说markdown_1.mdlatex_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}

相关内容