我正在设置一个复杂的用户手册,由几个独立的文档组成。对于整体目录,我希望每个文档将一些数据写入一个文件,然后由目录文件解析该文件。
重点是:我想在宏中使用字符串作为文件名,但不起作用。这一定与正确的扩展有关,但我找不到错误...
\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
。可能您不需要此功能,但添加它很容易,所以我不想失去这个为您的宏添加灵活性的机会。
我们准备要传递给的字符串\openout
(expl3
其中变为\iow_open:Nn
,我们使用的变体),删除所有空格。在写入操作中,我们可以直接使用参数(\docnr
默认情况下)。
注意您的代码:不要将\newwrite
声明放在宏中\SToc
,因为每次调用都会浪费输出流。因此\newwrite\tempfile
必须超出定义范围(并使用比应使用的\SToc
名称更具体的名称)。\tempfile