再会,
我正在尝试寻找一种有效的方法来访问远程文件夹中的文件。我为路径定义了几个命令。
下面代码的第 6 行可以运行,并且包含正确的文件,而第 8 行则显示“文件未找到”——但这两个路径不应该完全相同吗?我不允许像这样连接命令吗?
1 \newcommand{\results}{../../code/data/results/}
2 \newcommand{\synthetic}{\results synthetic/}
3 \newcommand{\sine}[1]{sine_#1hz.pdf}
4
5
6 \includegraphics[width=.3\linewidth]{\synthetic sine_100hz.pdf}
7
8 \includegraphics[width=.3\linewidth]{\synthetic \sine{100}}
也许我没有看到一些显而易见的东西。这是我第一次在命令中使用路径名。有人知道我遗漏了什么吗?
干杯
编辑:这是一个完整的代码示例:
\documentclass[]{article}
\usepackage{graphicx}
\begin{document}
\newcommand{\results}{../../code/waveletTest/data/results/}
\newcommand{\synthetic}{\results synthetic/}
\newcommand{\sine}[1]{sine_#1hz.pdf}
\includegraphics[width=.3\linewidth]{\synthetic sine_100hz.pdf}
%\includegraphics[width=.3\linewidth]{\synthetic \sine{100}}
\end{document}
此代码显示了我想要的文件。如果我在倒数第二行添加注释,则会收到此错误:
File `../../code/waveletTest/data/results/synthetic/sine_100hz.pdf' not found. ...width=.3\linewidth]{\synthetic \sine{100}}
答案1
在我的系统上,在处理你的\includegraphics
命令的某个阶段,你会得到以下序列:
\filename@parse{\synthetic\sine{100}}
\filename@parse
是 LaTeX 2ε 内核的一个例程,它将参数拆分为
- 文件路径。文件路径将存储在宏中
\filename@area
。 - 文件名(不带扩展名)。文件名(不带扩展名)将存储在 中
\filename@base
。 - 文件名的扩展名。文件名的扩展名存储在 中
\filename@ext
。如果没有扩展名,\filename@ext
则让 等于\relax
。
发生了什么:
在我的系统上\filename@parse
,语法: ,定义如下:
\filename@parse{⟨file-path/filename-specification⟩}
> \filename@parse=macro:
#1->\let \filename@area \@empty \expandafter \filename@path #1/\\
因此\filename@parse
初始化\filename@area
为等于宏\empty
- 宏\empty
在其顶层扩展期间从标记流中消失,不处理任何参数,并提供一个空的/不包含任何标记的替换文本 - 并在“\filename@path
命中”参数的第一个标记后调用例程\expandafter
一次并附加到/\\
结果中。(如果参数#1
为空,则附加的斜杠/
将被击中,\expandafter
但这并不有害,因为显式斜杠字符标记(catcode 12(其他))不可扩展。)
稍后将解释该例程\filename@path
。目前仅介绍以下内容:\filename@path
缩进以处理一系列不可扩展的显式字符标记形成文件路径/文件名规范(#1
),后面跟着类别代码 12(其他)的显式斜杠字符标记/
和控制符号标记\\
作为文件路径/文件名规范结尾的分隔符/标记。
因此,\expandafter
如果\filename@parse
提供文件路径/文件名规范的参数不是由一系列不可扩展的显式字符标记组成,而是由一个宏标记组成,而该宏标记的顶层扩展会产生一系列不可扩展的显式字符标记,则通过“一次命中”来完成。
这种“一次命中\expandafter
”意味着\includegraphics
,对于内部使用的东西\filename@parse
,您只能以标记序列的形式提供文件路径/文件名规范,其中\expandafter
一次命中序列的第一个标记足以获得形成文件路径/文件名规范的整个不可扩展的显式字符标记序列。
\expandafter
请注意,在您的场景中,对序列的第一个标记进行一次“命中”不会产生整个(完全扩展的)文件路径/文件名规范(以不可扩展的显式字符标记序列的形式),但会产生需要进行更多扩展工作的\synthetic \sine{100}
序列。\results synthetic/\sine{100}
\filename@path
因此,其底层例程无法正确拆分/拼接各个组件(文件路径、不带扩展名的文件名、文件扩展名) 。
反过来,该例程\filename@path
“期望”以一系列不可扩展的显式字符标记的形式提供文件路径/文件名规范,语法: ;是文件夹/目录分隔符 ,定义如下:
\filename@path ⟨file-path/filename-specification in terms of a sequence of non-expandable explicit character-tokens⟩/\\
/
> \filename@path=macro:
#1/#2\\->\ifx \\#2\\\def \reserved@a {\filename@simple #1.\\}\else \edef \filen
ame@area {\filename@area #1/}\def \reserved@a {\filename@path #2\\}\fi \reserve
d@a
\filename@path
/
是一个递归循环,在每次迭代中,它将文件路径/文件名规范的下一个 -delimited 段附加到宏\filename@area
,直到到达表示文件名的最后一个段。下一个(也可能是最后一个)段在 中#1
。下一个段之后的段在 中#2
。因此,最后一个段的指示为 为空#2
。
对 为空的测试#2
是: 当到达表示文件名的最后一个段时,调用宏来检查最后一个段/文件名是否包含点(),因此需要从文件名中拼接出文件扩展名。如果需要拼接出文件扩展名,则可以通过宏来完成。
\ifx\\#2\\⟨tokens in case #2 is empty⟩\else⟨tokens in case #2 is not empty⟩\fi
\filename@simple
.
\filename@dot
调用 时\filename@simple
,语法:在表示文件名的 最后一个- 分隔段上,序列将附加到最后一个段。 因此,可以收集一个点分隔参数和一个- 分隔参数,并根据 的空值检测段中存在的点或附加的点是否被用作点分隔参数的分隔符。
\filename@simple ⟨filename-specification in terms of a sequence of non-expandable explicit character-tokens⟩.\\
/
.\\
\filename@simple
#1
\\
#2
#2
\filename@simple
定义如下:
> \filename@simple=macro:
#1.#2\\->\ifx \\#2\\\let \filename@ext \relax \else \edef \filename@ext {\filen
ame@dot #2\\}\fi \edef \filename@base {#1}
如果最后一个段/文件名不包含点,则.
附加序列中的.\\
将被用作 的分隔符,#1
而\\
-delimited#2
将为空。否则,最后一个段的第一个点将被用作 的分隔符,#1
而\\
-delimited#2
将不为空。因此,#2
( \ifx\\#2\\
...) 的空性被视为最后一个段是否形成没有(点分隔)扩展名的文件名或形成由点与文件扩展名分隔的文件名的指标。如果#2
为空,则\filename@ext
“ \let
”等于\relax
。否则,在\filename@ext
通过定义时\edef
,\filename@dot
应用于第一个点后面的内容以删除附加序列.\\
。无论如何\filename@base
定义为扩展到第一个点之前的内容。
\filename@dot
定义如下:
> \filename@dot=macro:
#1.\\->#1
这种\filename@parse
机制很好。但它有一些限制。
例如,假设文件名最多包含一个点。
例如,假设文件名最多包含一个点,该点将文件的名称(不带扩展名)与文件扩展名分开,而文件扩展名又不为空。以点结尾的文件名(在某些文件系统上完全“合法”)可能会引起麻烦。
例如,不考虑具有特殊类别代码的特殊字符。例如,对于包含花括号的文件路径/文件名规范,花括号可能不匹配或可能被剥离和/或可能“掩盖”点和斜线作为分隔参数的分隔符。这些事情会造成麻烦。例如,包含哈希的文件路径/文件名规范在定义临时宏(如\reserved@a
或保存拆分文件路径/文件名规范的结果的宏)时可能会出现问题。
例如,假设\expandafter
/that 的一次“命中”触发了参数的第一个标记上的一次扩展步骤(→这就是术语“顶层扩展”的含义),足以\filename@parse
获得整个文件路径/文件名规范,即一系列不可扩展的显式字符标记。在您的场景中情况并非如此,因此在尚未扩展时尝试拆分事物\sine
,因此机制还无法“看到”将文件名(不带扩展名)与文件扩展名分开的点\filename@simple
。因此,在您的场景中,graphicx-package 错误地“假设”未指定文件扩展名。如果 graphicx-package“假设”(假设正确或错误)未指定文件扩展名,它会尝试使用一些默认扩展名。
例如,../../code/waveletTest/data/results/synthetic/sine_100hz.pdf
它尝试使用
../../code/waveletTest/data/results/synthetic/sine_100hz.pdf.pdf
、、等 。
../../code/waveletTest/data/results/synthetic/sine_100hz.pdf.png
../../code/waveletTest/data/results/synthetic/sine_100hz.pdf.jpg
这就是为什么输入“H⟨返回⟩“当错误消息在控制台/屏幕上弹出时:
I could not locate the file with any of these extensions:
.pdf,.png,.jpg,.mps,.jpeg,.jbig2,.jb2,.PDF,.PNG,.JPG,.JPEG,.JBIG2,.JB2,.eps
Try typing <return> to proceed.
If that doesn't work, type X <return> to quit.
David Carlisle(graphicx-package 的编写者)建议省略文件扩展名,.pdf
其目的在于:尽管在这种情况下,在检查文件扩展名是否存在之前进行扩展仍然无法以满足纯粹主义者的需求,但是 graphicx-package 假设没有指定文件扩展名是正确的,因此 graphicx-package 会以可行的方式尝试默认扩展名 — graphicx-package 会尝试使用
../../code/waveletTest/data/results/synthetic/sine_100hz.pdf
、
../../code/waveletTest/data/results/synthetic/sine_100hz.png
、
../../code/waveletTest/data/results/synthetic/sine_100hz.jpg
等
。
第一个已经奏效了。
所有这些问题都可以通过加载包来解决图形文件:
\documentclass[]{article}
\usepackage{graphicx}
\usepackage{grffile}
\begin{document}
\newcommand{\results}{../../code/waveletTest/data/results/}
\newcommand{\synthetic}{\results synthetic/}
\newcommand{\sine}[1]{sine_#1hz.pdf}
%\includegraphics[width=.3\linewidth]{\synthetic sine_100hz.pdf}
\includegraphics[width=.3\linewidth]{\synthetic\sine{100}}
\end{document}
顺便一提:
在非常特殊的情况下,您可以\filename@parse
通过添加以下内容来欺骗机制,使其正确拼接文件扩展名\expandafter
:
\documentclass[]{article}
\usepackage{graphicx}
\begin{document}
\newcommand{\results}{../../code/waveletTest/data/results/}
\newcommand{\synthetic}{\results synthetic/}
\newcommand{\sine}[1]{sine_#1hz.pdf}
%\includegraphics[width=.3\linewidth]{\synthetic sine_100hz.pdf}
\includegraphics[width=.3\linewidth]{\expandafter\synthetic\sine{100}}
\end{document}
至少在我的系统上这是可行的。
请注意,这确实正确地拼接了文件扩展名但这不能正确地将文件路径与文件名分开。
文件路径将被视为空。文件名将采用
序列。 这似乎并不重要。\synthetic sine_100hz
但\filename@parse
它是 LaTeX 2ε 内核的一个宏。而且最近 LaTeX 2ε 内核发生了很多变化和创新。可能\filename@parse
您系统上的运行效果与我的系统上的不一样。
我假设“命中参数的第一个标记\expandafter
一次,以从宏的顶层扩展中获取文件路径/文件名规范(以不可扩展的显式字符标记的形式)”不会被从中删除\filename@parse
。
因此,您可以应用一些\romannumeral
需要命中一次才能\expandafter
传递文件路径/文件名规范的扩展技巧:
\documentclass[]{article}
\usepackage{graphicx}
\begin{document}
\newcommand{\results}{../../code/waveletTest/data/results/}
\newcommand{\synthetic}{\results synthetic/}
\newcommand{\sine}[1]{sine_#1hz.pdf}
%\includegraphics[width=.3\linewidth]{\synthetic sine_100hz.pdf}
\includegraphics[width=.3\linewidth]{\romannumeral0\expandafter\synthetic\sine{100}}
\end{document}
这里发生了什么事?
\filename@parse
's\expandafer
确实“命中” \romannumeral
。
然后\romannumeral
- 触发 TeX 的收集 -⟨数字⟩-正在进行的数量:
%\romannumeral-triggered gathering of a TeX-number-quantity is in progress:
0\expandafter\synthetic\sine{100}
现在 LaTeX 找到数字0
并丢弃它。
现在收集 TeX 的过程⟨数字⟩-数量变成了收集更多数字的过程,或者终止收集 TeX-⟨数字⟩-数量:
%\romannumeral-triggered gathering of more digits is in progress; digit "0" found.
\expandafter\synthetic\sine{100}
现在 LaTeX 会扩展\expandafter
。扩展的结果\expandafter
是扩展\sine
:
%\romannumeral-triggered gathering of more digits is in progress; digit "0" found.
\synthetic sine_100hz.pdf
现在 LaTeX 扩展了\synthetic
。
%\romannumeral-triggered gathering of more digits is in progress; digit "0" found.
\results synthetic/sine_100hz.pdf
现在 LaTeX 扩展了\results
。
%\romannumeral-triggered gathering of more digits is in progress; digit "0" found.
../../code/waveletTest/data/results/synthetic/sine_100hz.pdf
现在 LaTeX 找到了一个点。那个点不是数字。与空格标记不同,它不会被丢弃。与空格标记一样,它确实会结束\romannumeral
触发的 TeX(组件)收集-⟨数字⟩-数量。因此 LaTeX 仅找到数字/数字0
,而 0 不是正数。对于非正数,\romannumeral
不会默默返回任何标记:
%\romannumeral done.
../../code/waveletTest/data/results/synthetic/sine_100hz.pdf