一种只按顺序“执行”乳胶程序代码(\def,\let)而忽略其余部分的方法?

一种只按顺序“执行”乳胶程序代码(\def,\let)而忽略其余部分的方法?

我有一个外部输入文件,我正在逐行读取它。此文件包含文本以及一些 tex/latex 宏。我的目标是将此文件的内容写入另一个外部输出文件,并且我希望能够用符号替换一些 latex 命令(更具体地说:我希望能够将简单的 latex 命令转换为相应的 markdown 命令)。

到目前为止,它对于简单的命令效果很好,例如 \textbf{bold} 被翻译成大胆的。我通过在写回输出文件之前重新定义 \textbf 来实现这一点(我为此使用了 \write):

\def\textbf#1{*#1*} 

现在我希望能够对 itemize 执行相同的操作。我想要以下代码:

\begin{itemize}
  \item one
  \item two
  \item three
\end{itemize}

翻译为:

*   one
*   two
*   three

第一个想法是忽略 \begin{itemize} 和 \end{itemize} 并简单地重新定义 \item 以便将其扩展到 markdown itemize 前缀。

但是,我希望能够处理嵌套的 itemize。我一直在使用相同的方法(重新定义 \begin 和 \end,这反过来将使用修改后的 \itemize 和 \enditemize),跟踪嵌套级别并相应地增加 markdown 前缀。您可以想象,事情出了问题:由于该过程涉及大量 \def、\if、\begingroup 等,我最终在输出文件中获得的是一系列 latex 代码,主要包含 \def、括号等。

这让我想到了我的问题:在我将包含文本和“程序 Latex 代码”(例如 \def、\if 等)的行发送到 \write 之前,有没有办法“执行”它包含的程序代码,然后将输出写入外部文件?

我一直在使用我所知道的所有技巧(\edef、\expandafter、\romannumeral,...)来强制扩展,但目前没有成功。

这是一个非常简单的例子,用来说明我想要实现的目标:

\documentclass{minimal}

\usepackage{filecontents}

\begin{filecontents*}{temp.tex}
This is \textbf{bold}

This is itemize:
\begin{itemize}
  \item one
  \item two
  \item three
\end{itemize}
\end{filecontents*}

\newread\filein
\newwrite\fileout

\def\markdownbf#1{**#1**}
\def\markdownbegin#1{\csname markdown#1\endcsname}
\def\markdownend#1{\csname markdownend#1\endcsname}
\def\markdownitemize{\edef\markdownprefix{*   }}
\def\markdownenditemize{}
\def\markdownitem{}
\def\markdownpar{}

\def\activatemarkdowncommands{%
  \let\textbf=\markdownbf%
  \let\begin=\markdownbegin%
  \let\end=\markdownend%
  \let\itemize=\markdownitemize%
  \let\enditemize=\markdownenditemize%
  \let\item=\markdownitem%
  \let\par=\markdownpar%
}

\def\translatefileA{%
  \begingroup%
    \edef\markdownprefix{}%
    \activatemarkdowncommands%
    \immediate\openin\filein temp.tex%
    \immediate\openout\fileout out.txt%
    \loop\unless\ifeof\filein%
      \immediate\read\filein to\fileline%
      \immediate\write\fileout{\markdownprefix\fileline}%
    \repeat%
    \immediate\closeout\fileout%
    \immediate\closein\filein%
  \endgroup%
}

\def\premarkdownbf#1{}
\def\premarkdownbegin#1{\csname premarkdown#1\endcsname}
\def\premarkdownend#1{\csname premarkdownend#1\endcsname}
\def\premarkdownitemize{\edef\storemarkdownprefix{*   }}
\def\premarkdownenditemize{\edef\markdownprefix{}}
\def\premarkdownitem{\let\markdownprefix\storemarkdownprefix}
\def\premarkdownpar{}

\def\postmarkdownbf#1{**#1**}
\def\postmarkdownbegin#1{\csname postmarkdown#1\endcsname}
\def\postmarkdownend#1{\csname postmarkdownend#1\endcsname}
\def\postmarkdownitemize{}
\def\postmarkdownenditemize{}
\def\postmarkdownitem{}
\def\postmarkdownpar{}

\def\activatepremarkdowncommands{%
  \let\textbf=\premarkdownbf%
  \let\begin=\premarkdownbegin%
  \let\end=\premarkdownend%
  \let\itemize=\premarkdownitemize%
  \let\enditemize=\premarkdownenditemize%
  \let\item=\premarkdownitem%
  \let\par=\premarkdownpar%
}

\def\activatepostmarkdowncommands{%
  \let\textbf=\postmarkdownbf%
  \let\begin=\postmarkdownbegin%
  \let\end=\postmarkdownend%
  \let\itemize=\postmarkdownitemize%
  \let\enditemize=\postmarkdownenditemize%
  \let\item=\postmarkdownitem%
  \let\par=\postmarkdownpar%
}

\def\translatefileB{%
  \begingroup%
    \edef\markdownprefix{}%
    \immediate\openin\filein temp.tex%
    \immediate\openout\fileout out.txt%
    \loop\unless\ifeof\filein%
      \immediate\read\filein to\fileline%
      \activatepremarkdowncommands%
      {\nullfont\fileline}%
      \activatepostmarkdowncommands%
      \immediate\write\fileout{\markdownprefix\fileline}%
    \repeat%
    \immediate\closeout\fileout%
    \immediate\closein\filein%
  \endgroup%
}

\begin{document}
  \translatefileB
\end{document}

如果我使用\translatefileA,那么我就会进入out.txt

This is **bold** 

This is itemize: 
\edef {* } 
one 
two 
three 

如果我使用\translatefileB,那么我会得到:

This is **bold** 

This is itemize: 

* one 
* two 
* three 

第一个版本是错误的,因为\markdownprefix没有按应有的方式设置,因为\markdown\itemize我猜想因为它没有按预期进行扩展。

第二个版本还可以。使用 \nullfont 删除 PDF 文件中不需要的输出。到目前为止,这似乎是我能想到的最佳解决方案。

答案1

使用 来检查 infile 中的子字符串\readline\readline将其作为原始逐字输入读取,并且仅当您的命令在其中时才执行该行,否则将忽略该行。所以有两次读取。以“L”为后缀表示运行 LaTeX,以“V”为后缀表示逐字(纯文本)

编辑:以前的代码不起作用。现已修复。

\documentclass{article}
\usepackage{substr, pgf, tikz}
\begin{document}
\makeatletter
\newread\fileinL
\newread\fileinV
\openin\fileinL=inFile.dat
\openin\fileinV=inFile.dat

\gdef\executecommands{\string\xdef, \string\let}

\loop\unless\ifeof\fileinL%
  \readline\fileinV to\filelineV%
  \read\fileinL to\filelineL%
  \foreach \cmd in \executecommands{
    \IfSubStringInString{\cmd}{\filelineV}{\filelineL}{}
  }
\repeat%
\end{document}

答案2

\nullfont 似乎回答了我的问题,同时它还需要一些工作才能正确处理数学模式(请参阅数学模式中的 \nullfont)。

以下是处理数学模式的修改后的代码(即:抑制所有输出,但扩展宏并定义它们)。宏 \translatefile 将读取 temp.tex 的内容,扩展其包含的宏(第一阶段),而不生成 PDF 输出,并将其翻译成 markdown(第二阶段),生成文件 out.txt。它不是一个功能齐全的翻译器,而是一个概念证明:它只处理 \textbf 和(非嵌套)itemize。

Node:另一个(可能更好)实现相同效果的解决方案是使用 \setbox0\vbox{....},而不是 David Carlisle 建议的 \nullfont。但是我对这种命令不够熟悉,无法使用它!

\documentclass{minimal}

\usepackage{filecontents}

\begin{filecontents*}{temp.tex}
This is \textbf{bold}, this is math: $1+x^2$.

This is itemize:
\begin{itemize}
  \item one,
  \item two,
  \item three.
\end{itemize}
\end{filecontents*}

\newread\filein
\newwrite\fileout

\begingroup
  \catcode`\ =12%
  \gdef\SPACE{ }%
\endgroup

\def\premarkdownbf#1{}
\def\premarkdownbegin#1{\csname premarkdown#1\endcsname}
\def\premarkdownend#1{\csname premarkdownend#1\endcsname}
\def\premarkdownitemize{\edef\storemarkdownprefix{*\SPACE\SPACE\SPACE}}
\def\premarkdownenditemize{\edef\markdownprefix{}}
\def\premarkdownitem{\let\markdownprefix\storemarkdownprefix}
\def\premarkdownpar{}

\def\postmarkdownbf#1{**#1**}
\def\postmarkdownbegin#1{\csname postmarkdown#1\endcsname}
\def\postmarkdownend#1{\csname postmarkdownend#1\endcsname}
\def\postmarkdownitemize{}
\def\postmarkdownenditemize{}
\def\postmarkdownitem{}
\def\postmarkdownpar{}

\def\activatepremarkdowncommands{%
  \let\textbf=\premarkdownbf%
  \let\begin=\premarkdownbegin%
  \let\end=\premarkdownend%
  \let\itemize=\premarkdownitemize%
  \let\enditemize=\premarkdownenditemize%
  \let\item=\premarkdownitem%
  \let\par=\premarkdownpar%
}

\def\activatepostmarkdowncommands{%
  \let\textbf=\postmarkdownbf%
  \let\begin=\postmarkdownbegin%
  \let\end=\postmarkdownend%
  \let\itemize=\postmarkdownitemize%
  \let\enditemize=\postmarkdownenditemize%
  \let\item=\postmarkdownitem%
  \let\par=\postmarkdownpar%
}

\newfont\dummyfont{dummy}

\def\translatefile{%
  \begingroup%
    \nullfont%
    \everymath{%
      \count0=0%
      \loop%
        \ifnum\count0<16%
        \textfont\count0\dummyfont%
        \scriptfont\count0\dummyfont%
        \scriptscriptfont\count0\dummyfont%
        \advance\count0 by 1%
      \repeat%
    }%
    \edef\markdownprefix{}%
    \immediate\openin\filein temp.tex%
    \immediate\openout\fileout out.txt%
    \loop\unless\ifeof\filein%
      \immediate\read\filein to\fileline%
      \activatepremarkdowncommands%
      \fileline%
      \activatepostmarkdowncommands%
      \immediate\write\fileout{\markdownprefix\fileline}%
    \repeat%
    \immediate\closeout\fileout%
    \immediate\closein\filein%
  \endgroup%
}

\begin{document}
  This should be visible.
  \translatefile %This should not be visible, only processed to out.txt
  This should be visible.
\end{document}

相关内容