动态生成文件写入句柄

动态生成文件写入句柄

我正在进行一些通过自动生成文件句柄的实验\newwrite

我想要实现以下行为:

  • 用户提供文件类别列表,例如提示解决方案问题等等,以及相应扩展的列表,比如,,.hints按正确的顺序(应该如何知道哪个类别属于哪个扩展,反之亦然),例如作为命令的第一个和第二个参数,。.sol.prbTeX\RegisterOutputFiles

  • 类别和扩展之间的联系存储在两个名称不同但计数器编号相同的文本标签中,基本内容在我的另一个问题中描述(已解决,详情见此处:将文本内容写为标签并使用 \nameref* 引用它们

  • \RegisterOutputFiles应该用一些好听的名字创建文件句柄,以便记住它们,比如说\HintsFile\SolutionsFile等等,然后打开它们通过等等进行写入\immediate\openout\HintsFile=\jobname.hints,所有这些都在循环中。

现在循环失败,当自定义命令\NewWrite显示错误消息时

 ! Missing \endcsname inserted. <to be read again> 
               \protect  l.84 ....prb,.hints,.sol,.explain,.idea,.concepts}

我很确定,我错过了一些扩展或类似的东西,但我无法弄清楚,因为它可以\NewWrite通过直接注入诸如Hints等词语来工作。

这是(通常不起作用的)MWE

\documentclass{article}

\usepackage{ifthen}
\usepackage{etoolbox}
\usepackage{blindtext}
\usepackage{morewrites}
\usepackage{xcolor}
\usepackage{hyperref}%



\newrobustcmd{\NewWrite}[1]{%
\expandafter\newwrite\csname#1\endcsname%
}%


%% Later on... Create a handle from #1 and directly combine
%% it with filename #2 with \immediate\openout%
\newrobustcmd{\NewWriteOpenOut}[2]{%
\expandafter\newwrite\csname#1\endcsname%
\immediate\openout\csname #1\endcsname=#2%
}%


\makeatletter
\newcounter{pc@@filecounter}%      
\newcounter{pc@@totalfilecounter}%
\newcounter{pc@@loopcounter}%


%% Hopefully only text is in #1%
\newrobustcmd{\WriteOutputFileLabel}[1]{%
\refstepcounter{pc@@filecounter}%
\immediate\write\@auxout{%
\string\newlabel{pc::outputfile::\number\value{pc@@filecounter}}{{\thesection}{\thepage}{#1}{}}%
}% End of writing to AUX file
}%

\newrobustcmd{\WriteOutputFileNameLabel}[1]{%
\refstepcounter{pc@@filecounter}%
\immediate\write\@auxout{%
\string\newlabel{pc::outputfilename::\number\value{pc@@filecounter}}{{\thesection}{\thepage}{#1}{}}%
}% End of writing to AUX file
}%


\newrobustcmd{\RegisterOutputFiles}[2]{%
%%% Other code before
%
\setcounter{pc@@filecounter}{0}%
\forcsvlist{\WriteOutputFileLabel}{#1}%      Store the output file `categories` to label names
\setcounter{pc@@totalfilecounter}{\number\value{pc@@filecounter}}%
\setcounter{pc@@filecounter}{0}%
\forcsvlist{\WriteOutputFileNameLabel}{#2}%  Store the extensions to label 'names'
%
\noindent%
\textbf{\Large \textcolor{blue}{There are \number\value{pc@@totalfilecounter} file categories!}}
%
\setcounter{pc@@filecounter}{1}%
\setcounter{pc@@loopcounter}{\number\value{pc@@totalfilecounter}}
\addtocounter{pc@@loopcounter}{1}%
%
%
\begin{center}
\textbf{\textcolor{red}{Diagnostics}}
\end{center}
%
\whiledo{\number\value{pc@@filecounter} < \number\value{pc@@loopcounter}}{%
\noindent \nameref*{pc::outputfile::\number\value{pc@@filecounter}}  \(\longrightarrow \)  \jobname\nameref*{pc::outputfilename::\number\value{pc@@filecounter}}%
\stepcounter{pc@@filecounter}%
%% Creating the file handles file with error message `\protect` 
\NewWrite{\nameref*{pc::outputfile::\number\value{pc@@filecounter}}}%  %Creating file handle name `\anyname` 
\newline%
} %
%%% Other code after
}%

\makeatother


\begin{document}


\RegisterOutputFiles{Problem,Hints,Solution,Explanation,Ideas,Concepts}{.prb,.hints,.sol,.explain,.idea,.concepts}%

\blindtext

\end{document}

笔记:

您必须删除循环中的%before并运行两次才能看到最终的错误消息,第一个抱怨缺少引用。\NewWritepdflatex

代码尚未完成,也尚未完善,它是另一个包的一些概念研究的初始阶段。

答案1

您不能使用\nameref*inside \csname...\endcsname,因为它不是完全可扩展的。关联类别扩展最好使用不同的语法,其中两个部分彼此相邻,因此错误不太可能发生。

文件hupfer-handle.tex

\documentclass{article}

\usepackage{etoolbox}
\usepackage{morewrites}
\usepackage{xcolor}

\makeatletter
%% create a list of the open streams
\listadd{\cs@stream@list}{}% initialize

%% a unique command for allocating a stream and opening it if the
%% optional name argument is given    
\newrobustcmd{\NewWrite}[2][]{%
  \listadd{\ch@stream@list}{#2}%
  \expandafter\newwrite\csname#2\endcsname
  \ifblank{#1}{}{\immediate\openout\@nameuse{#2}=#1 }%
}

%% a helper macro for counting the number of items in the csv argument
\newcommand{\ch@count@streams}[1]{\advance\@tempcnta\@ne}
%% a helper macro for separating the two parts at the slash
\newcommand\ch@open@stream[1]{\ch@open@stream@aux#1\@nil}
\def\ch@open@stream@aux#1/#2\@nil{%
  %% allocate a stream
  \NewWrite[\jobname.#2]{#1}%
  %% save the extension
  \@namedef{ch@stream@@#1}{.#2}%
}

\newrobustcmd{\RegisterOutputFiles}[2]{%
  %%% Other code before
  %
  \@tempcnta=\z@
  \forcsvlist{\ch@count@streams}{#1}%
  \forcsvlist{\ch@open@stream}{#1}%
  \noindent
  \textbf{\Large\textcolor{blue}{There are \number\@tempcnta\space file categories!}}%
  \begin{center}
  \textbf{\textcolor{red}{Diagnostics}}
  \end{center}
  \begingroup\parindent=\z@
  \renewcommand{\do}[1]{Handle ``##1'' with extension \texttt{\@nameuse{ch@stream@@##1}}\par}
  \dolistloop{\ch@stream@list}
  \endgroup
  %%% Other code after
}

\makeatother


\begin{document}

\RegisterOutputFiles{
  Problem/prb,
  Hints/hints,
  Solution/sol,
  Explanation/explain,
  Ideas/idea,
  Concepts/concepts
}

\end{document}

输出

在此处输入图片描述

文件列表

> ls hupfer-handle.*
hupfer-handle.aux       hupfer-handle.hints hupfer-handle.mw    hupfer-handle.sol
hupfer-handle.concepts  hupfer-handle.idea  hupfer-handle.pdf   hupfer-handle.tex
hupfer-handle.explain   hupfer-handle.log   hupfer-handle.prb

答案2

这是另一个更直接的解决方案:

\documentclass{article}

\newcount\tmpnum
\def\RegisterOutputFiles#1#2{\tmpnum=0 \def\listsummary{}%
   \registeroutputfilesA #1,,\relax #2,,\relax
}
\def\registeroutputfilesA #1,#2\relax #3,#4\relax{%
  \if^#1^The number of categories: \the\tmpnum{\tt\listsummary}\else
     \if^#3^\errmessage{?? less suffixes than categories.}\skiploop \fi
     \advance\tmpnum by1
     \csname newwrite\expandafter \endcsname \csname#1\endcsname
     \immediate \expandafter\openout \csname#1\endcsname =\jobname#3
     \edef\listsummary{\listsummary\endgraf
        The \jobname#3 is opened as \expandafter\string\csname#1\endcsname}%
     \registeroutputfilesA #2\relax #4\relax
  \fi\relax
}
\def\skiploop #1\fi\relax{\fi\fi}

\begin{document}

\RegisterOutputFiles {Problem,Hints,Solution,Explanation,Ideas,Concepts}
                     {.prb,.hints,.sol,.explain,.idea,.concepts}

\end{document}

我在这里添加它是为了表明纯 TeX 的使用通常可以更加紧凑。

相关内容