我刚刚意识到,当我将其用作\par
宏参数分隔符时,该参数包含一个尾随空格。
梅威瑟:
\def\test #1\par{\def\param{#1}}
\test test
\hbox{\vrule\param\vrule} % prints "|test |"
\bye
这是一个错误还是标准行为?如果是后者,为什么会这样?谢谢。
回复 wipet 的回答
@wipet:谢谢,但我需要更复杂的解决方案,而且我不确定我是否能够适应你的建议。
以下是我正在做的事情。我有一个包含大量宏的文件,其中包括定义和放置图片及其说明的宏,如下所示。
% picture definition
% #1 - reference name (must be unique within a book or article, not necessarily within a volume or entire collection)
% #2 - base file name (without suffix)
% #3 - picture title
\def\picdef #1 #2 #3\par{%
\expandafter\ifx\csname picfile-#1\endcsname\relax\else
\errmessage{Picture definition duplicity: reference #1 already defined!}\fi
\expandafter\def\csname picfile-#1\endcsname{\localPath/imgs/#2}%
\expandafter\def\csname pictitle-#1\endcsname{#3\unskip}%
}
% picture description definition
% #1 - reference name
% #2 - picture description
\long\def\picdesc #1 #2\descend{%
\expandafter\ifx\csname picfile-#1\endcsname\relax
\errmessage{Define picture first before defining its description}%
\else
\expandafter\expandafter\expandafter\long\expandafter\def\csname picdesc-#1\endcsname{#2\unskip}%
\fi
}
% user macros to actually place the picture or its description
\def\obrazek #1 {%
\expandafter\ifx\csname picfile-#1\endcsname\relax
\message{Warning: unknown picture reference #1 (typo or not yet defined?)!}%
\else
\ifhmode\par\fi
\begin{figure}[t]
\centerline{\includegraphics[width=0.9\textwidth]{\csname picfile-#1\endcsname}}
\vskip 14pt
\centerline{\large \bfseries {\csname pictitle-#1\endcsname}}
\end{figure}
\fi
}
\def\obrazekPopis #1 {%
\expandafter\ifx\csname picfile-#1\endcsname\relax
\message{Warning: unknown picture reference #1 (typo or not yet defined?)!}%
\else
\ifhmode\par\fi
\csname pictitle-#1\endcsname: \csname picdesc-#1\endcsname\par
\fi
}
然后,对于某一卷中的特定作品,有一个包含如下图片定义的文件:
\picdef
logo-s % small logo
logo_min % logo_min.pdf
Obr. 1 % Czech for Pic. 1
\picdesc logo-s
This is what a~small logo is supposed to look like.
\descend
\endinput
最后,在实际文本中,图片的放置方式如下:
Some text
and text
\obrazek logo-s
Some other text
etc.
\obrazekPopis logo-s
And text continues on
答案1
之所以这样,是因为事实就是这样。本质上,第一行末尾会像往常一样变成空格,第二行换行符会变成\par
。正常\par
行为是\unskip
先删除最后一个粘合节点(通常从这个空格中删除),然后再换行。
以下有几种可能性。
\def\test #1\par{\def\param{#1}}
\test test
\hbox{\vrule\param\vrule} % prints "|test |"
\def\testB #1 \par{\def\param{#1}}
\testB test
\hbox{\vrule\param\vrule} % prints "|test|"
\def\testC #1\par{\def\param{#1\unskip}}
\testC test
\hbox{\vrule\param\vrule} % prints "|test|"
\bye
\par
请注意,如果您在使用中使用实际的显式,如
\test test\par
那么就没有额外的空格,所以你原来的地方就不会有多余的空格。\testB
会产生错误,因为它会寻找一个\par
以空格开头的空间。
答案2
还有另一种可能性。创建一个宏,将其参数读取到行尾。此解决方案灵感来自 OPmac 技巧 0121。宏程序员可以使用
\eoldef\macro#1{here is a parameter "#1"}
然后\macro text<end of line>
导致“#1”为text
。\eoldef
宏可以定义为:
\gdef\eoldef#1{\def#1{\begingroup \catcode`\^^M=12 \eoldefA#1}%
\expandafter\def\csname\string#1:G\endcsname}
{\catcode`\^^M=12 %
\gdef\eoldefA#1#2^^M{\endgroup\csname\string#1:G\endcsname{#2}}}
答案3
您可以使用xparse
和expl3
:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\test}{u{\par}}
{
\tl_set:Nx \param { \tl_trim_spaces:n { #1 } }
}
\ExplSyntaxOff
\begin{document}
\test test
\mbox{\vrule\param\vrule} % prints "|test|"
\end{document}
当然也可以使用纯 TeX 来完成,但仅限于pdftex
。
您只需做更多的工作就可以获得更复杂的宏。
\documentclass{article}
\usepackage[demo]{graphicx}
\usepackage{xparse}
\ExplSyntaxOn
% first and second argument delimited by spaces
\NewDocumentCommand{\picdef}{u{~}u{~}u{\par}}
{
\marcels_pic_def:nnn { #1 } { #2 } { #3 }
}
\NewDocumentCommand{\picdesc}{u{~}u{\par}}
{
\marcels_pic_desc:nn { #1 } { #2 }
}
\NewDocumentCommand{\obrazek}{u{~}}
{
\marcels_pic_obrazek:n { #1 }
}
\NewDocumentCommand{\obrazekPopis}{u{~}}
{
\marcels_pic_popis:n { #1 }
}
\seq_new:N \g_marcels_pic_list_seq
\cs_new_protected:Nn \marcels_pic_def:nnn
{
\seq_if_in:NnTF \g_marcels_pic_list_seq { #1 }
{
\msg_error:nnn { marcels/pic } { duplicate } { #1 }
}
{
\__marcels_pic_defnew:nnn { #1 } { #2 } { #3 }
}
}
\cs_new_protected:Nn \__marcels_pic_defnew:nnn
{
\seq_gput_right:Nn \g_marcels_pic_list_seq { #1 }
\prop_gclear_new:c { g_marcels_pic_#1_prop }
\prop_gput:cnn { g_marcels_pic_#1_prop } { image } { \LocalPath/imgs/#2 }
\prop_gput:cnx { g_marcels_pic_#1_prop } { title } { \tl_trim_spaces:n { #3 } }
}
\cs_new_protected:Nn \marcels_pic_desc:nn
{
\seq_if_in:NnTF \g_marcels_pic_list_seq { #1 }
{
\prop_gput:cnn { g_marcels_pic_#1_prop } { desc } { #2 }
}
{
\msg_warning:nnn { marcels/pic } { early } { #1 }
}
}
\cs_new_protected:Nn \marcels_pic_obrazek:n
{
\seq_if_in:NnTF \g_marcels_pic_list_seq { #1 }
{
\__marcels_pic_obrazek_out:n { #1 }
}
{
\msg_warning:nnn { marcels/pic } { undefined } { #1 }
}
}
\cs_new_protected:Nn \__marcels_pic_obrazek_out:n
{
\par
\begin{figure}[tp]
\centering
\includegraphics[width=0.9\textwidth]
{
\prop_item:cn { g_marcels_pic_#1_prop } { image }
}
\par\vspace{14pt}
{\large\bfseries \prop_item:cn { g_marcels_pic_#1_prop } { title }}
\end{figure}
}
\cs_new_protected:Nn \marcels_pic_popis:n
{
\seq_if_in:NnTF \g_marcels_pic_list_seq { #1 }
{
\__marcels_pic_popis_out:n { #1 }
}
{
\msg_warning:nnn { marcels/pic } { undefined } { #1 }
}
}
\cs_new_protected:Nn \__marcels_pic_popis_out:n
{
\par
\prop_item:cn { g_marcels_pic_#1_prop } { title }:~
\prop_item:cn { g_marcels_pic_#1_prop } { desc }
\par
}
\msg_new:nnnn { marcels/pic } { duplicate }
{
Picture~name~'#1'~defined
}
{
The~name~'#1'~is~already~assigned;~I'll~ignore~the~new~definition
}
\msg_new:nnn { marcels/pic } { early }
{
Description~before~definition~of~'#1'
}
\msg_new:nnn { marcels/pic } { undefined }
{
Undefined~picture~'#1'
}
\ExplSyntaxOff
\begin{document}
\picdef
logo-s % small logo
logo_min % logo_min.pdf
Obr. 1 % Czech for Pic. 1
\picdesc logo-s
This is what a~small logo is supposed to look like.
Some text
and text
\obrazek logo-s
Some other text
etc.
\obrazekPopis logo-s
And text continues on
\end{document}
不同的实现,但输出相同。我认为,键值接口更清晰。
\documentclass{article}
\usepackage[demo]{graphicx}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\picdef}{mm}
{%#1 = name, #2 = key-value pairs
\marcels_pic_def:nn { #1 } { #2 }
}
\NewDocumentCommand{\obrazek}{m}
{
\marcels_pic_obrazek:n { #1 }
}
\NewDocumentCommand{\obrazekPopis}{m}
{
\marcels_pic_popis:n { #1 }
}
\keys_define:nn { marcels/pic }
{
image .code:n = \prop_put:Nnn \l_marcels_pic_tmp_prop { image } { \LocalPath/imgs/#1 },
title .code:n = \prop_put:Nnn \l_marcels_pic_tmp_prop { title } { #1 },
desc .code:n = \prop_put:Nnn \l_marcels_pic_tmp_prop { desc } { #1 },
}
\seq_new:N \g_marcels_pic_list_seq
\prop_new:N \l_marcels_pic_tmp_prop
\cs_new_protected:Nn \marcels_pic_def:nn
{
\seq_if_in:NnTF \g_marcels_pic_list_seq { #1 }
{
\msg_error:nnn { marcels/pic } { duplicate } { #1 }
}
{
\__marcels_pic_defnew:nn { #1 } { #2 }
}
}
\cs_new_protected:Nn \__marcels_pic_defnew:nn
{
\seq_gput_right:Nn \g_marcels_pic_list_seq { #1 }
\prop_clear:N \l_marcels_pic_tmp_prop
\keys_set:nn { marcels/pic } { #2 }
\prop_new:c { g_marcels_pic_#1_prop }
\prop_gset_eq:cN { g_marcels_pic_#1_prop } \l_marcels_pic_tmp_prop
}
\cs_new_protected:Nn \marcels_pic_desc:nn
{
\seq_if_in:NnTF \g_marcels_pic_list_seq { #1 }
{
\prop_gput:cnn { g_marcels_pic_#1_prop } { desc } { #2 }
}
{
\msg_warning:nnn { marcels/pic } { early } { #1 }
}
}
\cs_new_protected:Nn \marcels_pic_obrazek:n
{
\seq_if_in:NnTF \g_marcels_pic_list_seq { #1 }
{
\__marcels_pic_obrazek_out:n { #1 }
}
{
\msg_warning:nnn { marcels/pic } { undefined } { #1 }
}
}
\cs_new_protected:Nn \__marcels_pic_obrazek_out:n
{
\par
\begin{figure}[tp]
\centering
\includegraphics[width=0.9\textwidth]
{
\prop_item:cn { g_marcels_pic_#1_prop } { image }
}
\par\vspace{14pt}
{\large\bfseries \prop_item:cn { g_marcels_pic_#1_prop } { title }}
\end{figure}
}
\cs_new_protected:Nn \marcels_pic_popis:n
{
\seq_if_in:NnTF \g_marcels_pic_list_seq { #1 }
{
\__marcels_pic_popis_out:n { #1 }
}
{
\msg_warning:nnn { marcels/pic } { undefined } { #1 }
}
}
\cs_new_protected:Nn \__marcels_pic_popis_out:n
{
\par
\prop_item:cn { g_marcels_pic_#1_prop } { title }:~
\prop_item:cn { g_marcels_pic_#1_prop } { desc }
\par
}
\msg_new:nnnn { marcels/pic } { duplicate }
{
Picture~name~'#1'~defined
}
{
The~name~'#1'~is~already~assigned;~I'll~ignore~the~new~definition
}
\msg_new:nnn { marcels/pic } { early }
{
Description~before~definition~of~'#1'
}
\msg_new:nnn { marcels/pic } { undefined }
{
Undefined~picture~'#1'
}
\ExplSyntaxOff
\begin{document}
\picdef{logo-s} % small logo
{
image=logo_min, % logo_min.pdf
title=Obr. 1, % Czech for Pic. 1
desc=This is what a~small logo is supposed to look like.
}
Some text
and text
\obrazek{logo-s}
Some other text
etc.
\obrazekPopis{logo-s}
And text continues on
\end{document}