这个问题让我抓狂。自从 tex SE 上友好而博学的人指导我以正确的方式进行多次字符串替换,我一直在尝试将这些知识融入我的代码中 — 但目前还没有成功。困难在于:
- 字符串替换功能使用
\noexpandarg
/\expandafter
,它与所有功能配合得不是\write18
很好,并且 \write18
and/or中存在一个令人讨厌的情况\input
,会导致 (La)TeX 抛出一条TeX capacity exceeded, sorry
消息。
让我们看看下面的 M(W)E — 它目前并没有真正发挥作用,因为我用它curl
来处理本地服务器,而这里没有介绍。对于那些感兴趣的人:此代码适用于CoffeeXeLaTeX,尝试使用 JavaScript(以及 CoffeeScript)使 TeX 可编写脚本。除非您在指定的地址拥有 Web 服务器,否则无法成功运行下面的代码;我意识到这种依赖关系对于我想要演示的效果完全是偶然的,我愿意重写该示例,以便我们可以从 M(W)E 中取出括号。话虽如此,我相信你们中的许多人在阅读此代码时会惊呼“啊哈!初学者的错误!”:
\documentclass[a4paper]{article}
\usepackage{xstring}
\usepackage[usenames,dvipsnames,svgnames,table]{xcolor}
% -----------------------------------------------------------------
% URL escaping simplified;
% as per https://tex.stackexchange.com/questions/153215/how-to-do-multiple-string-replacements
\newcommand{\urlescapestep}[2]{%
\expandafter\StrSubstitute\expandafter{\x}{#1}{#2}[\x]%
}
\newcommand{\urlescape}[1]{{%
\noexpandarg%
\StrSubstitute{#1}{\%}{\%25}[\x]%
\urlescapestep{/}{\%2F}%
\urlescapestep{a}{A}% just for this test; imagine useful stuff here
\x}}
\newcommand{\escapeONE}[1]{*#1*}
\newcommand{\escapeTWO}[1]{\StrSubstitute{#1}{a}{A}}
% -----------------------------------------------------------------
% Execute a command with `\write18`:
\newcommand{\CXtempoutroute}{/tmp/CXtempout.tex}
\newcommand{\exec}[1]{%
\immediate\write18{#1 > "\CXtempoutroute"}\input{\CXtempoutroute}}
% -----------------------------------------------------------------
% `curl` commands using the string replacement commands:
\newcommand{\curlPlain}[4]{%
\exec{curl --silent --show-error #1 #2/#3#4}}
\newcommand{\curlUrlescape}[4]{%
\exec{curl --silent --show-error #1 #2/#3\urlescape{#4}}}
\newcommand{\curlONE}[4]{%
\exec{curl --silent --show-error #1 #2/#3\escapeONE{#4}}}
\newcommand{\curlTWO}[4]{%
\exec{curl --silent --show-error #1 #2/#3\escapeTWO{#4}}}
% -----------------------------------------------------------------
\begin{document}
First, let's show our three string escaping mechanisms all work under
normal circumstances:
\verb#\urlescape#: \urlescape{abc}
\verb#\escapeONE#: \escapeONE{abc}
\verb#\escapeTWO#: \escapeTWO{abc}
These do work and turn \verb#abc# into \verb#Abc#, \verb#*abc*#, and \verb#Abc#, respectively.
Now let's use the various \verb#curl*# methods:
works: \verb#\curlPlain{}{127.0.0.1:8910}{foobar.tex/helo/}{abc}#:
\curlPlain{}{127.0.0.1:8910}{foobar.tex/helo/}{abc}
works: \verb#\curlONE{}{127.0.0.1:8910}{foobar.tex/helo/}{abc}#:
\curlONE{}{127.0.0.1:8910}{foobar.tex/helo/}{abc}
throws: \verb#\curlTWO{}{127.0.0.1:8910}{foobar.tex/helo/}{abc}#:
\curlTWO{}{127.0.0.1:8910}{foobar.tex/helo/}{abc}
\end{document}
目前的代码抛出了TeX capacity exceeded, sorry [text input levels=15]
,这似乎表明 TeX 递归调用自身太多次。
在过去无数的时光里,我还遇到了一些TeX capacity exceeded, sorry [input stack size=5000]
可怕的use of StrSubstitute doesn't match its definition
错误,这取决于我把这些神奇的、、\edef
东西放在哪里。\noexpand
\expandafter
我完全承认我当时在做货物崇拜编程,但后来我也拿出了我 1990 年台湾版的 TeX Book。我读了贝希托尔斯海姆,我经常在互联网上搜索,Google 现在想给我发一张黄金客户卡。我删除了实验代码并重新开始,这样我就可以用一些有意义的代码发布这个问题;我可能会尝试重建一些间歇性的尝试。
到目前为止我发现,当 TeX 向文件写入内容时,它会推迟写入,因为它假设“大多数情况下这就是你想要的”(我不确定我是否同意这一点;为什么不写\postpone\write18
如果你真的想让它以这种方式工作,为什么不写呢?)。你可以让它行动现在和\immediate
由于 TeX 中没有诱饵,所以这不确切地您认为它应该是什么,它只是“在大多数情况下起着类似的作用”。
作为一名程序员,我习惯于在事件循环中处理递归函数调用和延迟的异步代码执行。奇怪的是,当 TeX 抱怨时,这些知识对我没什么帮助:
上面的代码中递归调用发生在哪里?我找不到它们。
为什么我
stack size exceeded
使用时有时会收到无害输入文件的错误\input
(我相信)?\noexpand
为什么/的存在会\expandafter
破坏\write18
?我应该如何解决这些症状(除了阅读实现代码直至参数中每个命令的最后一次扩展
write18
)?所有这些是否在某种程度上与强健/脆弱命令和“可移动参数”问题相关?
我觉得自己有理由问这个复杂而巨大的问题,因为在摆弄了几个月的 TeX(并设法产生了输出,所以值得一试)之后,我感觉这些是 TeX 复杂而阴暗的部分,游客不喜欢去,没有便捷的交通,只有最坚强的人才能让它工作。这大概就是我提出 CoffeeXeLaTeX 的动机。
答案1
上一个问题中的命令\urlescape
仅适用于印刷一个“纯净”的 URL。您需要一个不同的版本才能使用它\write
;请注意,生成文字 %
并不是\%
。
\documentclass[a5paper]{article}
\usepackage{xstring}
\newcommand{\urlescapestep}[2]{%
\expandafter\StrSubstitute\expandafter{\x}{#1}{#2}[\x]%
}
% In the following group endlines do not produce spaces
% and `%' becomes a printable character
\begingroup
\endlinechar=-1
\catcode`\%=12
\gdef\urlescape#1{{
\noexpandarg
\StrSubstitute{#1}{%}{%25}[\x]
\urlescapestep{/}{%2F}
\urlescapestep{\&}{%26}
\urlescapestep{ }{%20}
\urlescapestep{\$}{%24}
\urlescapestep{+}{%2b}
\urlescapestep{,}{%2c}
\urlescapestep{:}{%3a}
\urlescapestep{;}{%3b}
\urlescapestep{?}{%3f}
\urlescapestep{@}{%40}
\urlescapestep{"}{%22}
\urlescapestep{<}{%3c}
\urlescapestep{>}{%3e}
\urlescapestep{\#}{%23}
\urlescapestep{\{}{%7b}
\urlescapestep{\}}{%7d}
\urlescapestep{|}{%7c}
\urlescapestep{\^}{%5e}
\urlescapestep{\~}{%7e}
\urlescapestep{[}{%5b}
\urlescapestep{]}{%5d}
\urlescapestep{\`}{%60}
\global\let\urlescapecurrent=\x}}
\endgroup
\newcommand{\curlUrlescape}[4]{%
\urlescape{#4}%
\exec{curl --silent --show-error #1 #2/#3\urlescapecurrent}%
}
A temporary version just for testing
\newcommand{\exec}[1]{%
\typeout{I would use^^J^^J%
#1%
^^J^^Jin \string\immediate\string\write18}%
}
\begin{document}
% a foolish URL, just for testing
\curlUrlescape{}{127.0.0.1:8910}{foobar.tex/helo/}{abc|][}
\end{document}
以下是日志文件中打印的内容:
I would use
curl --silent --show-error 127.0.0.1:8910/foobar.tex/helo/abc%7c%5d%5b
in \immediate\write18
答案2
您的问题是不熟悉 TeX 的人经常遇到的问题:您没有考虑到什么是可扩展的,什么是不可扩展的。\write
原语以与 相同的方式进行扩展\edef
。其工作方式\StrSubstitute
是不是可扩展:你不能\edef
在或类似结构内使用它\write
。相反,您必须首先使用“名称”(宏)的可选参数进行赋值。以您的演示中适用的部分为例:
\makeatletter
\newcommand{\curlTWO}[4]{%
\StrSubstitute{#4}{a}{A}[\@tempa]
\exec{curl --silent --show-error #1 #2/#3\@tempa}}
\makeatother
注意,这做了替换外部可扩展上下文(\exec
作为的包装器\write
),并且我使用\@tempa
它作为宏来保存替换的字符串。
更一般地,如果你想编写 (La)TeX 程序,你有理解扩展上下文。一些 TeX 流程要求可扩展性,以及其他堵塞它(作业,,\relax
...)。