如何指定一个上下文相关的命令来决定是否在“。”后插入空格?

如何指定一个上下文相关的命令来决定是否在“。”后插入空格?

i.我喜欢在-ie后面排版 ie 并留有半个空白的命令i.\,e

现在棘手的问题是 之后应包含什么内容e.

MWE 包含一些我希望集成的命令。我尝试使用 xspace,但似乎没有帮助。

\documentclass{article}
\newcommand{\ie}{i.\,e. }%1.5 blank
\newcommand{\iens}{i.\,e.}no blank IF followed by sign.
\newcommand{\iesb}{i.\,e.\ }%single blank

\begin{document}
    \ie without full stop or comma gets me a 1.5 * blank after the \ie's `.'.

    \iens: with a full stop or comma (or similar sign)  works as intended.

    \iesb gets me a correct blank of factor 1.

    Can I integrate these commands into a single command that dynamically adds a simple factor 1 blank after the `.` IF no sign directly follows whilst not inserting a blank at all IF a sign directly follows the command?
\end{document}

答案1

xspace 包关心是否插入空格,但不关心它是否应该是单词间空格或句子间空格。

正如解释的那样这个答案,你可以用它\@来标记两个都句子的结束和没有句子的结束取决于它是位于句号之前还是之后。

因此(正如您已经弄清楚的那样)您正在寻找的命令是\newcommand{\iex}{i.\,e.\@\xspace}

但请记住,即使使用这样的命令,也不应该总是不假思索地使用普通空格。根据此方法的其他用途,~有时 a 会更好,因为它禁止换行,请参阅 TeXbook 第 74、91-93 页:

连字符最适合用于名称中的缩写,以及几个其他常见缩写,如“Fig.”、“cf.”、“vs.”和“resp.”之后;您会发现训练自己输入 很容易。事实上,在句子中间出现的常见缩写之后cf.~Fig.~5输入(而不是空格)通常是明智的。~

我在本科论文中用了\xspace很多,直到我遇到了一个问题,它没有正确决定。我没有时间弄清楚它是什么,但我假设我的命令嵌套太多了。最后我检查了整个代码,并手动处理了所有的间距。现在我知道xspace 文档说它只能得到最多如果不正确该怎么办?(请参阅第 1 页和第 2 页)。

然而,我决定不再使用 xspace。事实上,TeX 会占用控制字后的空格,只要在控制字后添加一个空格即可{}。但输入空格很烦人,如果忘记了,甚至不会收到警告。因此,我更喜欢使用参数分隔符,即直接跟在控制序列名称后面的非字母字符,即\def\ie/{i.\,e.\@}(参见 TeXbook 第 204 页)。仍然需要输入参数分隔符,但如果忘记输入,TeX 会报错。而且由于空格不是直接跟在控制序列后面,而是跟在参数分隔符后面,因此不是吞噬。这种方法仍然有两个问题:(1)\def不检查控制序列是否已经存在(如果存在,它将被悄悄替换)和(2)您仍然需要输入参数分隔符。但是使用一些技巧,您可以让 TeXStudio 的自动完成功能为您插入它:

\makeatletter
% #1: macro name, #2: parameter delimiter
% The last '#' is an exceptional parameter delimiter standing for an opening brace (TeXbook page 204).
\def\@parameterless@newcommand#1#2#{%
    % the name of the macro without the leading escape character
    \edef\tmp@macroname{\expandafter\@gobble\string#1}%
    % if #1 is undefined
    % I am using here that \csname is letting #1 to \relax if it is undefined
    \expandafter\ifx \csname\tmp@macroname\endcsname \relax
    % then
        \def\@parameterless@do{\def#1#2}%
    \else
        \GenericError{}{Command \@backslashchar\tmp@macroname\space already defined}{}{Your command was ignored.}%
        \let\@parameterless@do=\@gobble
    \fi
    \@parameterless@do
}
% To strip the braces around macro name and parameter delimiter.
\def\@parameterless\newcommand#1{%
    \@parameterless@newcommand#1%
}
% TeXstudio does not recognize the parameter delimiter "\newcommand".
% Therefore I deceive it so that it sees \parameterless as a simple macro without parameters.
% Otherwise the autocompletion would insert an argument between \parameterless and \newcommand.
\let\parameterless=\@parameterless%
\makeatother

\parameterless\newcommand{\ie/}{i.\,e.\@}

如果您需要该命令与或一起\parameterless工作,您可以使用以下定义:\renewcommand\providecommand

\makeatletter
% #1: \newcommand, \renewcommand or \providecommand, #2: control sequence followed by parameter delimiter
\def\@parameterless#1#2{%
    \ifx\newcommand#1%
        \@parameterless@ifundefined#2{%
            % then
            \def\@parameterless@do{\def#2}%
        }{%
            % else
            \GenericError{}{Command \@backslashchar\tmp@macroname\space already defined}{as \expandafter\meaning\csname\tmp@macroname\endcsname}{Your command was ignored.}%
            \let\@parameterless@do=\@gobble
        }%
    \else\ifx\renewcommand#1%
        \@parameterless@ifundefined#2{%
            % then
            \GenericError{}{\@backslashchar\tmp@macroname\space undefined}{}{Your command was ignored.}%
            \let\@parameterless@do=\@gobble
        }{%
            % else
            \def\@parameterless@do{\def#2}%
        }%
    \else\ifx\providecommand#1%
        \@parameterless@ifundefined#2{%
            % then
            \def\@parameterless@do{\def#2}%
        }{%
            % else
            \let\@parameterless@do=\@gobble
        }%
    \else
        \GenericError{}{Use of \@backslashchar parameterless doesn't match its definition}{It should be directly followed by either one of \string\newcommand, \string\renewcommand\space or \string\providecommand.}{Your command was ignored.}%
        \let\@parameterless@do=\@gobble
    \fi\fi\fi
    \@parameterless@do
}
% #1: control sequence, #2: parameter delimiter
% The last '#' is an exceptional parameter delimiter standing for an opening brace (TeXbook page 204).
% if (#1 is defined) {\@firstoftwo} else {\@secondoftwo}
\def\@parameterless@ifundefined#1#2#{%
    % the name of the macro without the leading escape character
    \edef\tmp@macroname{\expandafter\@gobble\string#1}%
    % if #1 is undefined
    % I am using here that \csname is letting #1 to \relax if it is undefined
    \expandafter\ifx \csname\tmp@macroname\endcsname \relax
    % then
        \expandafter\@firstoftwo
    \else
        \expandafter\@secondoftwo
    \fi
}
% To deceive TeXstudio into believing \parameterless was a macro without parameters.
% Otherwise the autocompletion would insert undesired braces.
\let\parameterless=\@parameterless%
\makeatother

但是,还有一些事情需要考虑:如果缩写位于句子末尾怎么办?你可能不想要两个点。点后的空格应该是句子间空格。但是,此宏确保以下空格将是单词间空格。无法提前判断下一个标记是否是点,因为\@ifnextchar空格后面会吞噬 (LaTeX 2e 来源第 35 页)。

但是,人们可以将参数分隔符替换为参数,并根据参数插入明确的句末点或非句末点。因此,通常情况下,在句子内部,人们使用上述语法,\ie/但在句子末尾,斜线被句号替换:斜线\ie.后面的空格仍然不会被占用,因为它不是直接跟在控制字后面,而是跟在参数后面。但是,如果人们忘记了参数,TeX 将不再抱怨。但只要人们使用自动完成功能,就不会忘记插入参数。

请注意,根据以下定义,替换文本必须以 结尾.(不带\@)。

\makeatletter
% #1: \newcommand, \renewcommand or \providecommand, #2: control sequence followed by parameter delimiter, #3: replacement text
\def\@abbr#1#2#3{%
    \ifx\newcommand#1%
        \@abbr@ifundefined#2{%
        % then
            \edef\@abbr@do{\def\expandafter\noexpand\csname\tmp@macroname\endcsname####1}%
        }{%
        % else
            \GenericError{}{Command \@backslashchar\tmp@macroname\space already defined}{as \expandafter\meaning\csname\tmp@macroname\endcsname}{Your command was ignored.}%
            \let\@abbr@do=\@gobble
        }%
    \else\ifx\renewcommand#1%
        \@abbr@ifundefined#2{%
        % then
            \GenericError{}{\@backslashchar\tmp@macroname\space undefined}{}{Your command was ignored.}%
            \let\@abbr@do=\@gobble
        }{%
        % else
            \edef\@abbr@do{\def\expandafter\noexpand\csname\tmp@macroname\endcsname####1}%
        }%
    \else\ifx\providecommand#1%
        \@abbr@ifundefined#2{%
        % then
            \edef\@abbr@do{\def\expandafter\noexpand\csname\tmp@macroname\endcsname####1}%
        }{%
        % else
            \let\@abbr@do=\@gobble
        }%
    \else
        \GenericError{}{Use of \@backslashchar abbr doesn't match its definition}{It should be directly followed by one of \string\newcommand, \string\renewcommand\space or \string\providecommand.}{Your command was ignored.}%
        \let\@abbr@do=\@gobble
    \fi\fi\fi
    % strip the trailing dot so that I can replace it by an explicit end-of-sentence-dot or not-end-of-sentence-dot. (so that it works with upper case abbreviations, too.)
    \@abbr@def\@abbr@content#3\@abbr@enddef%
    \expandafter\@abbr@do\expandafter{\@abbr@content\ifx.##1\@.\else.\@\fi}%
}
\def\@abbr@def#1#2.\@abbr@enddef{\def#1{#2}}%
% #1: control sequence, #2: parameter delimiter
% The last '#' is an exceptional parameter delimiter standing for an opening brace (TeXbook page 204).
% if (#1 is defined) {\@firstoftwo} else {\@secondoftwo}
\def\@abbr@ifundefined#1#2#{%
    % the name of the macro without the leading escape character
    \edef\tmp@macroname{\expandafter\@gobble\string#1}%
    % if #1 is undefined
    % I am using here that \csname is letting #1 to \relax if it is undefined
    \expandafter\ifx \csname\tmp@macroname\endcsname \relax
    % then
        \expandafter\@firstoftwo
    \else
        \expandafter\@secondoftwo
    \fi
}
% To deceive TeXstudio into believing \abbr was a macro without parameters.
% Otherwise the autocompletion would insert undesired braces.
\let\abbr=\@abbr%
\makeatother

\abbr\newcommand{\ie/}{i.\,e.}

既然我们已经这样做了,我们不妨让这些点活跃起来,这样我们就不需要手动输入了\,

\def\makedotactive{\catcode`.=\active}
\def\makedotother{\catcode`.=12\relax}
\def\dotOther{.}

\makeatletter
% #1: \newcommand, \renewcommand or \providecommand, #2: control sequence followed by parameter delimiter, #3: replacement text
\def\@abbr#1#2#3{%
    \makedotother
    \ifx\newcommand#1%
        \@abbr@ifundefined#2{%
        % then
            \edef\@abbr@do{\def\expandafter\noexpand\csname\tmp@macroname\endcsname####1}%
        }{%
        % else
            \GenericError{}{Command \@backslashchar\tmp@macroname\space already defined}{as \expandafter\meaning\csname\tmp@macroname\endcsname}{Your command was ignored.}%
            \let\@abbr@do=\@gobble
        }%
    \else\ifx\renewcommand#1%
        \@abbr@ifundefined#2{%
        % then
            \GenericError{}{\@backslashchar\tmp@macroname\space undefined}{}{Your command was ignored.}%
            \let\@abbr@do=\@gobble
        }{%
        % else
            \edef\@abbr@do{\def\expandafter\noexpand\csname\tmp@macroname\endcsname####1}%
        }%
    \else\ifx\providecommand#1%
        \@abbr@ifundefined#2{%
        % then
            \edef\@abbr@do{\def\expandafter\noexpand\csname\tmp@macroname\endcsname####1}%
        }{%
        % else
            \let\@abbr@do=\@gobble
        }%
    \else
        \GenericError{}{Use of \@backslashchar abbr doesn't match its definition}{It should be directly followed by one of \string\newcommand, \string\renewcommand\space or \string\providecommand.}{Your command was ignored.}%
        \let\@abbr@do=\@gobble
    \fi\fi\fi
    % strip the trailing dot so that I can replace it by an explicit end-of-sentence-dot or not-end-of-sentence-dot. (so that it works with upper case abbreviations, too.)
    \@abbr@def\@abbr@content#3\@abbr@enddef%
    \expandafter\@abbr@do\expandafter{\@abbr@content\ifx.##1\@.\else.\@\fi}%
}

% #1: control sequence, #2: parameter delimiter
% The last '#' is an exceptional parameter delimiter standing for an opening brace (TeXbook page 204).
% if (#1 is defined) {\@firstoftwo} else {\@secondoftwo}
\def\@abbr@ifundefined#1#2#{%
    % the name of the macro without the leading escape character
    \edef\tmp@macroname{\expandafter\@gobble\string#1}%
    % if #1 is undefined
    % I am using here that \csname is letting #1 to \relax if it is undefined
    \expandafter\ifx \csname\tmp@macroname\endcsname \relax
    % then
        \expandafter\@firstoftwo
    \else
        \expandafter\@secondoftwo
    \fi
}

\makedotactive
\def\@abbr@def#1#2.\@abbr@enddef{\edef#1{#2}}%
\newcommand{\defdot}{\def.}
\makedotother

\outer\def\abbr{%
    \makedotactive
    \@abbr
}
\makeatother

\defdot{\dotOther\noexpand\,}
\abbr\newcommand{\ie/}{i.e.}

在上面的代码中,\edefin\@abbr@def使 TeX 在定义缩写时扩展活动点(与使用缩写时相反)。并且它要求如果想要使用类似于\emph缩写替换文本中的命令,该命令前面需要加上\noexpand

如果使用缩写时,应扩大活动点,必须用正常的 替换\edefin 。此外,还必须删除in 。\@abbr@def\def\noexpand\defdot

如果您收到错误消息,则! Paragraph ended before \@abbr@def was complete替换文本没有以应有的(活动)点结尾。

\abbr旨在更改其参数中的 catcodes。因此它不能在参数或替换文本中使用。我通过将其声明为来确保这一点\outer。如果您无论如何尝试这样做,您将收到错误消息! Forbidden control sequence found

无论如何,这是 TeXbook 对缩写“ie”的解释(第 74 页):

风格手册会告诉您,缩写“eg”和“ie”后面始终应该跟逗号,而不能跟空格,因此这些特殊情况不需要任何特殊处理。

答案2

借鉴LaTeX 宏 \space 的使用(与 \␣ 相比)我想出了以下解决方案

\documentclass{article}
\usepackage{xspace} 
\newcommand{\iex}{i.\,e.\@\xspace}
\begin{document}
    \iex, bla c

    \iex bla c
\end{document}

相关内容