写入文件:使用宏作为文件名

写入文件:使用宏作为文件名

我正在设置一个复杂的用户手册,由几个独立的文档组成。对于整体目录,我希望每个文档将一些数据写入一个文件,然后由目录文件解析该文件。

重点是:我想在宏中使用字符串作为文件名,但不起作用。这一定与正确的扩展有关,但我找不到错误...

\documentclass[10pt]{article}

 \newcommand{\docnr}{foo bar}

 \usepackage{stringstrings}
 \newcommand{\tocfile}{../toc/\noblanks{\docnr}.tex}

 \newcommand{\SToc}{
   \newwrite\tempfile
   \immediate\openout\tempfile=\tocfile

   %other stuff
   \immediate\write\tempfile{\docnr}
   %other stuff

   \immediate\closeout\tempfile
}

\begin{document}
   \SToc
   Hello World.
\end{document}

编辑:对我来说,一种完全不同的方法是:我正在寻找一个环境,其中的全部内容尽可能地展开,然后写入文件。这样,我就可以\input稍后将其简单地放入目录中。例如

\newcommand{\docnr}{foo bar}
\begin{writetofile}[../toc/\noblanks{\docnr}.tex]
   \textbf{\docnr}
\end{writetofile}

文件 foobar.tex 将包含

\textbf{foo bar}

有许多环境可以将命令逐字(不扩展)写入文件 - 我现在搜索了完全相反的情况,但什么也没找到。咕噜!

答案1

LaTeX 内核有一个命令\zap@space,它是可扩展的,可以在文件名中使用,例如:

\newcommand*{\docnr}{foo bar}

\makeatletter
\edef\docnr@wosp{\docnr\space}
\newcommand{\tocfile}{}
\edef\tocfile{../toc/\expandafter\zap@space\docnr@wosp\@empty.tex}
\makeatother

\typeout{[tocfile=\tocfile]}

结果:

[tocfile=../toc/foobar.tex]

不带页码的书写

当不需要页码时,就不需要延迟书写,我们可以使用\immediate\write。LaTeX 的保护机制由

\let\protect\noexpand

在编写条目时。然后\textbf{\docnr}变成\textbf {foo bar}。TeX 在命令名称后添加一个空格,另一个空格来自 LaTeX 的受保护命令,这些命令扩展为具有相同名称的宏,只是在宏名称中添加了一个尾随空格。

\protect可以像往常一样通过在其前面加上前缀来保护脆弱的宏。\string也可以使用,它避免了命令名称后的空格。

\documentclass{article}

\newcommand*{\docnr}{foo bar}

\makeatletter
\edef\docnr@wosp{\docnr\space}
\newcommand{\tocfile}{}
\edef\tocfile{\expandafter\zap@space\docnr@wosp\@empty.tex}
\makeatother

% toc file management
\makeatletter
\if@filesw
  \let\toc@handle\relax
  \DeclareRobustCommand{\addtotoc}[1]{%
    \ifx\toc@handle\relax
      \newwrite\toc@handle
      \immediate\openout\toc@handle=\tocfile\relax
    \fi
    \begingroup
      \let\protect\noexpand
      \immediate\write\toc@handle{#1}%
    \endgroup
  }%
  \newcommand*{\closetoc}{%
    \immediate\closeout\toc@handle
  }%
\else
  \newcommand{\addtotoc}[1]{}%
  \newcommand*{\closetoc}{}%
\fi
\makeatother

\begin{document}
  \addtotoc{\textbf{\docnr}, %
    written via macro \string\verb|\string\addtotoc|.}
  \closetoc
  \input{\tocfile}
\end{document}

结果

文件foobar.tex

\textbf  {foo bar}, written via macro \verb|\addtotoc|.

\addtotoc该文件可以在第一个之前和之后读取\closetoc

使用页码进行延迟书写

\documentclass{article}

\newcommand*{\docnr}{foo bar}

\makeatletter
\edef\docnr@wosp{\docnr\space}
\newcommand{\tocfile}{}
\edef\tocfile{\expandafter\zap@space\docnr@wosp\@empty.tex}
\makeatother

% toc file management
\makeatletter
\if@filesw
  \let\toc@handle\relax
  \DeclareRobustCommand*{\addtotoc}{%
    \ifx\toc@handle\relax
      \newwrite\toc@handle
      \immediate\openout\toc@handle=\tocfile\relax
    \fi
    \protected@write\toc@handle{}% + argument of \addtotoc
  }%
  \newcommand*{\closetoc}{%
    \closeout\toc@handle
  }%
\else
  \newcommand{\addtotoc}[1]{}%
  \newcommand*{\closetoc}{}%
\fi
\makeatother

\begin{document}
  Hello World!

  \addtotoc{\textbf{\docnr} on page \thepage,
    written via macro \string\verb|\string\addtotoc|.}
  \closetoc
  \newpage
  \input{\tocfile}
\end{document}

结果延迟写入

文件foobar.tex

\textbf  {foo bar} on page 1, written via macro \verb|\addtotoc|.

该文件可以在第一页之前和之后读取,在那里调用\addtotoc延迟。\closeout

答案2

您可以尝试使用xstring\StrDel,先预处理文件名并将其存储到\tocfile宏中。

\noblanks不幸的是,宏stringstrings无法扩展。(查看stringstrings包或使用texdef以查看)

其他一些注意事项:可能会出现pdflatex拒绝在以 开头的路径名上写入的情况.

尝试找到texmf.cnf并在末尾添加此行:

openout_any = a

然而,这可能是一个安全漏洞。

\documentclass[10pt]{article}


\newwrite\tempfile

\newcommand{\docnr}{foo bar}

%\usepackage{stringstrings}
\usepackage{xstring}
\gdef\tocfile{}
\StrDel[0]{../toc/\docnr.tex}{ }[\tocfile]


\newcommand{\SToc}{%
  \immediate\openout\tempfile=\tocfile
  % other stuff
  \immediate\write\tempfile{\docnr}
  % other stuff
  \immediate\closeout\tempfile
}

\begin{document}
\SToc
Hello World.
\end{document}

答案3

filecontents 可以将外部文件写入父目录中吗?针对在当前目录上方或给定明确路径的目录中写入文件的问题。

这就是说,我们可以使用一组简单的宏来解决您的问题expl3

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn

\iow_new:N \g_victor_output_stream

\tl_new:N \l_victor_filename_tl

%\tl_const:Nn \c_victor_prefix_tl { ../toc/ } % use this one
\tl_const:Nn \c_victor_prefix_tl { ./ } % for the test

\cs_generate_variant:Nn \iow_open:Nn { NV }

\NewDocumentCommand{\SToc}{ O{\docnr} }
 {
  \tl_set:Nx \l_victor_filename_tl { \c_victor_prefix_tl #1 }
  \tl_replace_all:Nnn \l_victor_filename_tl { ~ } { }
  \iow_open:NV \g_victor_output_stream \l_victor_filename_tl
  \STocStuffA
  \iow_now:Nx \g_victor_output_stream { #1 }
  \STocStuffB
  \iow_close:N \g_victor_output_stream
 }
\ExplSyntaxOff

\newcommand{\docnr}{foo bar}

\newcommand{\STocStuffA}{} % for the test
\newcommand{\STocStuffB}{} % for the test

\begin{document}

\SToc

Hello World.

\end{document}

\STocStuffA\STocStuffB包含您在代码中提示的写入操作之前和之后需要做的事情。

只需更改前缀即可获取与当前工作目录(主 TeX 文件所属的目录)不同的位置。

您可以\SToc不使用可选参数或使用一个参数来调用,这将代替默认的\docnr。可能您不需要此功能,但添加它很容易,所以我不想失去这个为您的宏添加灵活性的机会。

我们准备要传递给的字符串\openoutexpl3其中变为\iow_open:Nn,我们使用的变体),删除所有空格。在写入操作中,我们可以直接使用参数(\docnr默认情况下)。


注意您的代码:不要将\newwrite声明放在宏中\SToc,因为每次调用都会浪费输出流。因此\newwrite\tempfile必须超出定义范围(并使用比应使用的\SToc名称更具体的名称)。\tempfile

相关内容