我有一个外部输入文件,我正在逐行读取它。此文件包含文本以及一些 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}