我已经阅读问题在 TeX.SE 中,但我不希望用户^^J
手动添加。也就是说,我希望 writer 本身就输出内容。
\documentclass{article}
\begin{document}
\newwrite\file
\immediate\openout\file=tmp.txt
\immediate\write\file{
To be or not to be,
that is % the question
}
\closeout\file
\end{document}
它应该输出
To be or not to be,
that is % the question
这是我的源代码,一开始我使用 Python 从文件中提取内容.tex
,然后我用更简单的方式重构它,原本使用 LaTeX 输出代码,这就是我遇到这个问题的原因。
抱歉我的表达能力不佳:P 非常感谢!
答案1
LaTeX 内核提供了filecontents
写入外部文件的环境,无需担心 catcode 等。在较旧的 LaTeX 版本中(2019-10-01 之前;请参阅这里) 该filecontents
软件包对此环境做了最小的更改,使其可以在文档的任何位置使用(LaTeX 版本只能在序言中使用,不允许覆盖)。在较新的版本中,此功能也包含在 LaTeX 内核中。
生产
To be or not to be,
that is % the question
你用:
\documentclass{article}
% \usepackage{filecontents} For older LaTeX releases
\begin{document}
\begin{filecontents*}[overwrite]{tmp.txt}
To be or not to be,
that is % the question
\end{filecontents*}
\end{document}
带星号的版本 ( filecontents*
) 省略了在环境标准版本中打印的标题:
%% LaTeX2e file `tmp.txt'
%% generated by the `filecontents' environment
%% from source `test' on 2020/01/12.
%%
To be or not to be,
that is % the question
关于我的(确实很懒惰的)答案的附录:
如果你坚持要重新发明轮子(我必须承认,这更有趣),那么你可以创建一个命令来\catcode
为你处理这个任务。这里我提供了一个命令的实现\verbwrite
,它可以为你完成这项工作。
该命令的语法与 LaTeX 的 有点相似\verb
:您可以使用 as\verbwrite\file{<stuff>}
或\verbwrite\file|<stuff>|
。对于后一种语法,{
可以使用 以外的任何字符来分隔内容。显然,此字符不能出现在 中。第二种语法的优点是,您在命令内容中平衡和<stuff>
没有任何限制。{
}
\documentclass{article}
\makeatletter
\long\def\@ifnextchar@other@space#1#2#3{%
\let\reserved@d=#1%
\def\reserved@a{#2}%
\def\reserved@b{#3}%
\futurelet\@let@token\@ifnch@other}
\def\@ifnch@other{%
\ifx\@let@token\other@sptoken
\let\reserved@c\@xifnch@other
\else
\ifx\@let@token\reserved@d
\let\reserved@c\reserved@a
\else
\let\reserved@c\reserved@b
\fi
\fi
\reserved@c}
{\catcode`\ =12
{\global\let\other@sptoken= }%
\gdef\@xifnch@other {\futurelet\@let@token\@ifnch@other}}%
\def\verbwrite{%
\kernel@ifnextchar*%
{\let\@ifnextchar\@ifnextchar@other@space\expandafter\verbwrite@grab\@gobble}%
{\let\@ifnextchar\kernel@ifnextchar\verbwrite@grab}}
\def\verbwrite@grab#1{%
\begingroup
\catcode`\^^M=13
\newlinechar`\^^M
\let\do\@makeother \dospecials
\catcode`\{=1
\@ifnextchar\bgroup
{\catcode`\}= 2\relax\verbwrite@brace#1}%
{\catcode`\{=12\relax\verbwrite@other#1}}
\def\verbwrite@brace#1#2{%
\immediate\write#1{\unexpanded{#2}}%
\endgroup}
\def\verbwrite@other#1#2{%
\def\verbwrite@delim##1##2#2{%
\verbwrite@brace##1{##2}}%
\verbwrite@delim#1}
\makeatother
\begin{document}
\newwrite\file
\immediate\openout\file=tmp.txt
\verbwrite\file {1-To be or not to be,
that is % the question}
\verbwrite*\file {2-To be or not to be,
that is % the question}
\verbwrite\file|3-To be or not to be,
that is } the {question|
\verbwrite\file$4-Être ou ne pas être,
вот в чем вопрос$
\verbwrite\file}5-Être ou ne pas être,
вот в чем вопрос}
\closeout\file
\end{document}
请注意,我花了 68 分钟编写此命令,因此它肯定不能称为强大。请谨慎操作 :)
修复1:防止使用 ε-TeX 扩展文本\unexpanded
(感谢 jfbu:)
修复2:防止过早标记分隔符(再次感谢 jfbu:)
特征 1:添加了带星号的版本,该版本忽略了逐字内容分隔符前的空格。
修复 3:实际上允许}
作为“其他”分隔符(\verbwrite\file}stuff}
)(感谢 Ulrich Diez:)
修复 4:修复功能 1 中的缺陷。一旦使用,参数的效果*
将会保留以供进一步调用。\verbwrite
答案2
下面的解释中,我在想要表明所写内容对“纯”TeX 有效,因此对 LaTeX 也有效的地方写了 (La)TeX。我这样做是为了让那些不知道 LaTeX 基本上是 TeX 加上一组宏的人知道,这些宏构成了 LaTeX 格式,并且在执行 latex.exe/latex-binary 时会自动加载。
我建议使用filecontents*
-environment。
请注意,还有一个LaTeX 2ε 软件包文件内容filecontents*
它确实消除了LaTeX 2ε 内核的环境所带来的一些限制。
如果你想重新发明轮子,你可以编写一个宏来
- 切换到 verbatim-catcode-régime,
- 将 endlinechar (通常为 /ASCII-Return) 的 catcode 切换
^^M
为 12,以便将 ASCII-return 视为数字和标点符号, - 在该 catcode-régime 下读取并标记包含要写入文件的文本的参数
- 修剪文本的前导和尾随行符
- 将文本写入文件,同时
\endlinechar
也将其作为\newlinechar
。
在 (La)TeX 中,处理输入有几个阶段。
(La)TeX 确实会逐行读取 TeX 输入,例如 .tex 输入文件。
在预处理阶段,组成行的单个字符将被转换为 (La)TeX 的内部字符编码。(对于老式 (La)TeX 引擎,内部字符编码是 ASCII。对于基于 XeTeX 或 LuaTeX 的引擎,内部字符编码是 utf-8,其中 ASCII 是其子集。)然后,将删除行右端出现的所有空格字符(在 ASCII 和 utf-8 中代码点编号都是 32,即,在所有涉及 (La)TeX 引擎内部字符编码的编码中) 。然后在行的右端插入一个字符,其在 (La)TeX 内部字符编码(即 ASCII 或 utf-8)中的代码点编号对应于整数参数的数量\endlinechar
。通常,整数参数的值为\endlinechar
13,而 ASCII 和 utf-8 中的代码点号 13(即,在所有涉及 (La)TeX 引擎内部字符编码的编码中)表示⟨返回⟩-字符。这意味着:通常是⟨返回⟩-字符插入到行的右端。
完成后,标记化阶段开始:在此阶段,(La)TeX 将构成行的字符作为将标记放入标记流的指令。在此阶段,事情开始涉及所谓的标记,例如控制序列标记(有两种形式:控制字标记和控制符号标记)和字符标记。字符标记由表示 (La)TeX 内部字符编码中的代码点编号的字符代码和类别代码组成。类别代码使字符对 (La)TeX 引擎具有特殊含义。例如,反斜杠字符的类别代码通常为 0(转义)。在标记化时类别代码为 0 的字符会导致 (La)TeX 收集控制序列标记的名称,然后将该控制序列标记放入标记流中。例如,打开花括号的类别代码通常为 1(开始分组),而关闭花括号的类别代码通常为 2(结束分组),而类别代码 1(开始分组)的字符标记用于引入组(即,由几个标记组成的宏参数或用于宏定义或⟨平衡文本⟩)之类的内容\scantokens
以及类别代码 2(结束分组)的字符标记将用于表示不再属于相关组的内容。有关类别代码的更多信息,请访问https://en.wikibooks.org/wiki/TeX/catcode。
标记化之后,会有一个“标记流”。处理标记流包括扩展可扩展标记(例如,宏标记,例如可扩展基元,如\string
或\csname...\endcsname
)以及(稍后)执行分配、创建框等。
在读取和标记 .tex 输入文件时,(La)TeX 将在预处理阶段删除每个行末的空格并在每个行末插入一个结束符。
因此输入序列
\immediate\write\file{
To be or not to be,
that is % the question
}
将在标记时(即在预处理之后)被 (La)TeX 视为
\immediate\write\file{⟨character due to endline-char-insertion⟩
To be or not to be,⟨character due to endline-char-insertion⟩
that is % the question⟨character due to endline-char-insertion⟩
}⟨character due to endline-char-insertion⟩
通常,结束符是^^M
,即⟨返回⟩。
因此,上述输入序列在标记时通常会被 (La)TeX 处理为
\immediate\write\file{⟨^^M/RETURN-character⟩
To be or not to be,⟨^^M/RETURN-character⟩
that is % the question⟨^^M/RETURN-character⟩
}⟨^^M/RETURN-character⟩
(当遇到⟨^^M/RETURN 字符⟩取决于在标记化时分配给⟨^^M/RETURN 字符⟩。
通常类别代码⟨^^M/RETURN 字符⟩是 5(行尾),这意味着根据 (La)TeX 读取装置的状态(在状态 S=跳过空白时)根本没有标记,或者(在状态 M=在行中间)将插入一个空格标记(=类别代码 10(空格)和字符代码 32 的字符标记(32 是 (La)TeX 内部字符编码中空格字符的数量)或(在状态 N=即将开始新行时)\par
将插入一个 -token。
如果类别代码为 12(其他)分配给⟨^^M/RETURN 字符⟩,(La)TeX 将插入类别代码为 12(其他)的字符标记和字符代码为 13 的字符标记(13 是⟨RETURN 字符⟩,在 (La)TeX 的内部字符编码中)进入标记流。这样的标记可以像任何其他字符标记一样进行处理。
除此之外,(La)TeX 在写入时无论如何都会在命令参数的末尾附加\write
该平台上用于结束纯文本文件中的行的字符/字节序列。
因此 - 假设我们设法让 LaTeX 接受百分比字符作为普通字符 - 命令\write
将得到类似如下的结果:
⟨token due to ^^M/RETURN-character⟩To be or not to be,⟨token due to ^^M/RETURN-character⟩that is % the question⟨token due to ^^M/RETURN-character⟩
At写作时间,
⟨platform-dependent sequence for ending the line⟩
将被附加。
如果结束符的类别代码/⟨^^M/RETURN 字符⟩在对输入进行标记时为 5(行尾),序列
⟨space⟩To be or not to be,⟨space⟩that is % the question⟨space⟩⟨platform-dependent sequence for ending the line⟩
将被写入外部文件。
如果结束符的类别代码/⟨^^M/RETURN 字符⟩在对输入进行标记时为 12(返回),序列
^^MTo be or not to be,^^Mthat is % the question^^M⟨platform-dependent sequence for ending the line⟩
将被写入外部文件。
您可以确保在写入时⟨^^M/RETURN 字符⟩还会产生 ⟨与平台相关的行尾结束序列⟩通过为整数参数分配\newlinechar
整数参数的值\endlinechar
。
如果你也这样做,那么序列
⟨platform-dependent sequence for ending the line⟩To be or not to be,⟨platform-dependent sequence for ending the line⟩that is % the question⟨platform-dependent sequence for ending the line⟩⟨platform-dependent sequence for ending the line⟩
将被写入外部文件。
但这样你可能会得到不想要的空行。
因此,您可能希望应用一个例程来删除前导和尾随⟨由于行尾插入而产生的字符⟩\write
在开始写作工作之前,先从整个论点入手。
编码示例可能如下所示:
\documentclass{article}
\makeatletter
\begingroup
\catcode`\^^M=12\relax%
\@firstofone{%
\endgroup%
\newcommand*\gobbleendl{}\def\gobbleendl ^^M{}%
\newcommand\trimendls[2]{\innertrimleadendl{#2}#1^^M\relax{#1}}%
\newcommand*\innertrimleadendl{}%
\def\innertrimleadendl#1#2^^M#3\relax#4{%
\ifx\relax#2\relax\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi%
{%
\ifx\relax#4\relax\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi%
{\trimtrailendl{}{#1}}%
{\expandafter\trimtrailendl\expandafter{\gobbleendl#4}{#1}}%
}%
{\trimtrailendl{#4}{#1}}%
}%
\newcommand*\trimtrailendl[2]{%
\innertrimtrailendl{#2}.#1\relax.^^M\relax.\relax\relax{#1}%
}%
\newcommand*\innertrimtrailendl{}%
\def\innertrimtrailendl#1#2^^M\relax.#3\relax\relax#4{%
\ifx\relax#3\relax\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi%
{\def\@tempa{#4}}%
{\expandafter\def\expandafter\@tempa\expandafter{\@gobble#2}}%
\@onelevel@sanitize\@tempa%
\newlinechar=\endlinechar%
\immediate\write#1{\@tempa}%
}%
}%
\newcommand\immediateverbatimwrite[1]{%
\begingroup
\let\do=\@makeother
\dospecials
\catcode`\ =10 %We don't want to allow space as verb-arg-delimiter.
%Thus let's remove spaces when grabbing undelimited arguments.
%\endlinechar=`\^^M%
%\catcode`\endlinechar=5 %
\bracefork{#1}%
}%
\begingroup
\catcode`\(=1 %
\catcode`\{=12 %
\@firstofone(%
\endgroup
\newcommand\bracefork[2](%
\catcode`\ =12\relax
\catcode\endlinechar=12 %
\ifx{#2\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
(%
\catcode`\{=1 %
\catcode`\}=2 %
\internalfilewritercaller(#1}(}%
}(%
\internalfilewritercaller(#1}(#2}%
}%
}%
}%
\newcommand\internalfilewritercaller[2]{%
\def\@tempa##1#2{\internalfilewriter{#1}{##1}}%
\ifx\relax#2\relax\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{\expandafter\expandafter
\expandafter\@tempa
\expandafter\expandafter
\expandafter{%
\expandafter\@gobble\string}}%
{\@tempa}%
}
\newcommand\internalfilewriter[2]{%
\trimendls{#2}{#1}%
\endgroup
}%
\makeatother
\begin{document}
\newwrite\file
\immediate\openout\file=tmp.txt\relax
A\immediateverbatimwrite{\file}
{
être ou ne pas être.
That is % the question.
}B%
C%
%
D\immediateverbatimwrite{\file} |
}être ou ne pas être.
That is % the question.
|E%
F
\immediate\closeout\file
\end{document}
通过此示例您可以获得
- 具有序列 ABCDEF 的 pdf 文件。(这表明没有引入/插入任何虚假空格/任何字符。)
- 一个名为的文本文件临时文件其内容如下:
由于换行符,显示行号的编辑器可能会将该文件显示为être ou ne pas être.⟨linebreak⟩ That is % the question.⟨linebreak⟩ }être ou ne pas être.⟨linebreak⟩ That is % the question.⟨linebreak⟩
1 être ou ne pas être. 2 That is % the question. 3 }être ou ne pas être. 4 That is % the question. 5
顺便说一句:使用 (La)TeX不是可以在行尾保留空格。
原因是 (La)TeX 确实逐行读取和标记输入,并且它对每一行输入所做的第一件事(在预处理阶段)(甚至在添加结束行字符和开始标记行之前)是删除行尾的所有空格。
因此 (La)TeX 输入如下
code⟨space⟩⟨space⟩
more code⟨space⟩⟨space⟩⟨space⟩⟨space⟩⟨space⟩
even more code⟨space⟩⟨space⟩
在任何情况下都会被预处理为
code⟨character due to endline-char-insertion⟩more code⟨character due to endline-char-insertion⟩even more code⟨character due to endline-char-insertion⟩
在进行任何进一步的处理/标记化等之前。