我正在寻找一个快捷方式生成器,它不像\newcommand
-something 那样挑剔\input
,而是来自文件内部。例如,我希望有一个普通的 .tex 文件,我可以在某个时候在其中写入类似的东西,\this[20c]{\that}
并且接下来的 20 个字符在我每次在文档中写入时都会使用\that
,就像这 20 个字符是在文档的那个位置写入的一样。
答案1
当 TeX 处理 .tex 输入文件时,它会读取该文件并将其作为一组指令,用于生成所谓的标记并将它们逐个插入到标记流中。标记可以是控制序列标记或字符标记。
我最近在回答这个问题时详细阐述了处理 .tex-input 的不同阶段使命令输出成为另一个命令的(多个)参数。
您可以让 LaTeX 在 verbatim-catcode-régime 下读取并标记 .tex-input 的接下来几行或几个字符,然后再将结果字符标记集传递给\scantokens
。在 verbatim-catcode-régime 下,.tex-input-file 的每个字符都被标记化为字符标记。\scantokens
本质上就像将其参数的标记未扩展地写入文本文件一样,然后通过 启动读取和标记该文本文件的内容\input
。如果 的参数\scantokens
仅由字符标记组成,则将它们未扩展地写入(真正完成或由\scantokens
' 写入部分模拟)将产生与在 .tex-input-file 的相应位置可以找到的相同的字符集。因此\input
的 -part\scantokens
将找到与在 .tex-input-file 的相应位置可以找到的相同的字符。但是,在\scantokens
执行其部分时读取和标记该组字符的结果取决于执行其部分\input
时有效的 catcode 制度。\scantokens
\input
我想对于逐行处理,你希望有一个宏可以
- 从 verbatim-catcode-régime 下的 .tex-input-file 中读取接下来的几行,
- 在除最后一行之外的每一行标记后面插入换行符
- 并从结果中定义一个宏,将所有内容传递给
\scantokens
。
我想对于逐字符处理,你希望有一个宏可以
- 在 verbatim-catcode-régime 下从 .tex-input-file 中读取接下来的几个字符,
- 附加注释字符以确保插入的最后一个结束符
\scantokens
无效, - 并从结果中定义一个宏,将所有内容传递给
\scantokens
。
下面是一个如何实现此类宏的小例子:
\documentclass{article}
\begingroup
% Make @ a letter which can occur in names of control sequences:
\makeatletter
% Make return, which usually is TeX's endline-character, an ordinary character:
\catcode`\^^M=12 %
% Let's use as comment-character ^^A instead of %:
\catcode`\^^A=14 %
% Make % an ordinary character:
\catcode`\%=12 ^^A
\@firstofone{^^A
\endgroup^^A
\newcommand{\makenextlines}[2]{^^A
\begingroup^^A
\let\do\@makeother\dospecials^^A
\catcode`\^^I=12 ^^A
\catcode`\^^M=12 ^^A
\romannumeral0^^A
\expandafter\innermakenextlines\expandafter{\romannumeral\number\number#1 000}{#2}{}^^A
}^^A
\long\def\innermakenextlines#1#2#3#4^^M{^^A
^^A \innermakenextlines just checks whether there are more lines to collect.
^^A If not, defimes command to define to deliver the lines collected so far nested in
^^A a call to \scantokens.
^^A If so calls \innerinnermakenextlines for collecting the next line of .tex-input
^^A and then calling \innermakenextlines again.
^^A #1 = Characters m in an amount that equals the amount of lines still to collect.
^^A #2 = Command to define.
^^A #3 = Lines collected so far.
^^A #4 = In the first iteration: Remaining characters of the line which contains the call to \makenextlines.
^^A These will silently be discarded. (One could implement triggering an error-message
^^A depending on the non-emptiness of #4.)
^^A In subsequent iterations: Empty as TeX's endline-character ^^M is provided explicitly as argument-delimiter
^^A right behind the third argument.
\ifx X#1X\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi^^A
{ \endgroup^^A
\newcommand#2{\scantokens{#3}}^^A
}{^^A
\innerinnermakenextlines{#1}{#2}{#3}^^A
}^^A
}^^A
\long\def\innerinnermakenextlines#1#2#3#4^^M{^^A
^^A #1 = Characters m in an amount that equals the amount of lines to collect.
^^A #2 = Command to define.
^^A #3 = Lines collected so far.
^^A #4 = Collected next line.
\expandafter\ifx\expandafter X\@firstoftwo{}#1X\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi^^A
{^^A There is only one m left, thus the last line is to be collected. With the last line no
^^A newline-character will be attached.
\expandafter\innermakenextlines\expandafter{\@firstoftwo{}#1}{#2}{#3#4}{}^^M^^A
}{^^A Not yet the last line, so attach a newline-character ^^J. When \scantokens, which emulates
^^A unexpanded writing to file and inputtong that file, does its unexpanded-writing-part, the
^^A newline-character will not br written verbatim but will cause TeX to begin writing another line.
\expandafter\innermakenextlines\expandafter{\@firstoftwo{}#1}{#2}{#3#4^^J}{}^^M^^A
}^^A
}^^A
^^A-----------------------------------------------------------------------------------------------------------
^^A-----------------------------------------------------------------------------------------------------------
^^A-----------------------------------------------------------------------------------------------------------
\newcommand{\makenextcharacters}[2]{^^A
\begingroup^^A
\let\do\@makeother\dospecials^^A
\catcode`\^^I=12 ^^A
\catcode`\^^M=12 ^^A
\romannumeral0^^A
\expandafter\innermakenextcharacters\expandafter{\romannumeral\number\number#1 000}{#2}{}^^A
}^^A
\long\def\innermakenextcharacters#1#2#3{^^A
^^A #1 = Characters m in an amount that equals the amount of lines to collect.
^^A #2 = Command to define.
^^A #3 = Characters collected so far.
\ifx X#1X\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi^^A
{ \endgroup^^A
^^A Let's append a percent to make sure the endline-character apppended to what
^^A can be taken for the last line when \scantokens does its \input-part
^^A will have no effect as under normal catcode-régime that percent will be taken
^^A for a comment whereafter characters and thus also the appended endline-character
^^A are dropped instead of being tokenized.
\newcommand#2{\scantokens{#3%}}^^A
}{^^A
\innerinnermakenextcharacters{#1}{#2}{#3}^^A
}^^A
}^^A
\long\def\innerinnermakenextcharacters#1#2#3#4{^^A
^^A #1 = Characters m in an amount that equals the amount of lines to collect.
^^A #2 = Command to define.
^^A #3 = Characters collected so far.
^^A #4 = Collected next character.
^^A Be aware that endline-characters need to be replaced by newline-characters
^^A so that they yield line-break when \scantokens does its writing-part-
\ifx#4^^M\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi^^A
{^^A
\expandafter\innermakenextcharacters\expandafter{\@firstoftwo{}#1}{#2}{#3^^J}^^A
}{^^A
\expandafter\innermakenextcharacters\expandafter{\@firstoftwo{}#1}{#2}{#3#4}^^A
}^^A
}^^A
}%
\begin{document}
\makenextlines{10}{\thiscommand}
\super-complicated-that{ @\ \makeatletter magic @ fireworks}
\another-fragile-thing{ @\ \makeatletter smoke @ mirrors}
\super-complicated-that{ @\ \makeatletter magic @ fireworks}
\another-fragile-thing{ @\ \makeatletter smoke @ mirrors}
\super-complicated-that{ @\ \makeatletter magic @ fireworks}
\another-fragile-thing{ @\ \makeatletter smoke @ mirrors}
\super-complicated-that{ @\ \makeatletter magic @ fireworks}
\another-fragile-thing{ @\ \makeatletter smoke @ mirrors}
\super-complicated-that{ @\ \makeatletter magic @ fireworks}
\another-fragile-thing{ @\ \makeatletter smoke @ mirrors}
\show\thiscommand
\makenextcharacters{17}{\thatcommand}12345678901234567\show\thatcommand
% A linebreak is counted like one character:
\makenextcharacters{17}{\onemorecommand}1234
567890123456\show\onemorecommand
\end{document}
控制台输出是:
> \thiscommand=\long macro:
->\scantokens {\super-complicated-that{ @\ \makeatletter magic @ fireworks}
\another-fragile-thing{ @\ \makeatletter smoke @ mirrors}
\super-complicated-that{ @\ \makeatletter magic @ fireworks}
\another-fragile-thing{ @\ \makeatletter smoke @ mirrors}
\super-complicated-that{ @\ \makeatletter magic @ fireworks}
\another-fragile-thing{ @\ \makeatletter smoke @ mirrors}
\super-complicated-that{ @\ \makeatletter magic @ fireworks}
\another-fragile-thing{ @\ \makeatletter smoke @ mirrors}
\super-complicated-that{ @\ \makeatletter magic @ fireworks}
\another-fragile-thing{ @\ \makeatletter smoke @ mirrors}}.
l.113 \show\thiscommand
?
> \thatcommand=\long macro:
->\scantokens {12345678901234567%}.
l.115 ...ommand}12345678901234567\show\thatcommand
?
> \onemorecommand=\long macro:
->\scantokens {1234
567890123456%}.
l.120 567890123456\show\onemorecommand
?
[...]
No pages of output.