我尝试了一种 - 至少在我看来 - 非常简单的将单词大写的方法:
\documentclass{article}
\begin{document}
\begingroup%
\obeyspaces%\catcode`\ \active
\def {\space\MakeUppercase}%
Hello world
\endgroup
\end{document}
不用说,我也尝试过把它包装成一个宏,比如
\documentclass{article}
\newcommand{\capitalize}[1]{\begingroup\obeyspaces\def {\space\MakeUppercase}#1\endgroup}
\begin{document}
\capitalize{Hello world}
\end{document}
这将导致 TeX 抱怨内部的语法\def
并使其\inaccessible
。
到底出了什么问题?有没有办法解决这个问题?
答案1
这是通常的\verb
在论证问题中通常不起作用的情况。
\obeyspaces
改变空间的 catcode,这意味着一个空间特点在一个文件转换为活动令牌。catcode 更改已不对已创建的标记有影响。 在您的例子中, 的整个参数已被标记化,因此之后\newcommand
根本没有空格标记。\def
\def{
您需要在之前更改空间的 catcode,\newcommand
并且\capitalize
需要在获取其参数之前更改空间的 catcode。(出于这个和其他原因,我不会为此使用 catcode 更改,而是简单地使用分隔参数来查找正常空间)
\documentclass{article}
\newcommand{\capitalize}[1]{\xcapitalize#1 \relax}
\def\xcapitalize#1 #2{%
#1%
\ifx\relax#2%
\else
\space\MakeUppercase{#2}%
\expandafter\xcapitalize
\fi}
\begin{document}
\capitalize{Hello world and this}
\end{document}
评论中请求的版本也将首字母大写,并允许参数为宏:
\documentclass{article}
\newcommand{\capitalize}[1]{\ignorespaces
\expandafter\expandafter\expandafter
\xcapitalize\expandafter\space #1 \relax}
\def\xcapitalize#1 #2{%
#1%
\ifx\relax#2%
\else
\space\MakeUppercase{#2}%
\expandafter\xcapitalize
\fi}
\begin{document}
\capitalize{hello world and this}
\newcommand\zzz{hello world and this}
\capitalize{\zzz}
\end{document}
答案2
此解决方案需要最近更新的 TeX 发行版。与 相比\obeyspaces
,它的优点是类别代码没有改变,因此宏也可以作为其他命令的参数。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\capitalize}{m}
{
\ruben_capitalize:n { #1 }
}
\seq_new:N \l__ruben_capitalize_words_seq
\seq_new:N \l__ruben_capitalize_out_seq
\cs_new_protected:Npn \ruben_capitalize:n #1
{
\seq_set_split:Nnn \l__ruben_capitalize_words_seq { ~ } { #1 }
\seq_set_map:NNn \l__ruben_capitalize_out_seq \l__ruben_capitalize_words_seq
{
\tl_mixed_case:n { ##1 }
}
\seq_use:Nn \l__ruben_capitalize_out_seq { ~ }
}
\ExplSyntaxOff
\begin{document}
\capitalize{Hello world}
\end{document}
如果你还想将作为宏传递的字符串大写,只需将定义更改\capitalize
为
\NewDocumentCommand{\capitalize}{m}
{
\ruben_capitalize:o { #1 }
}
并添加
\cs_generate_variant:Nn \ruben_capitalize:n { o }
在 的定义之后\ruben_capitalize:n
(即 之前\ExplSyntaxOff
)。
完整示例:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\capitalize}{m}
{
\ruben_capitalize:o { #1 }
}
\seq_new:N \l__ruben_capitalize_words_seq
\seq_new:N \l__ruben_capitalize_out_seq
\cs_new_protected:Npn \ruben_capitalize:n #1
{
\seq_set_split:Nnn \l__ruben_capitalize_words_seq { ~ } { #1 }
\seq_set_map:NNn \l__ruben_capitalize_out_seq \l__ruben_capitalize_words_seq
{
\tl_mixed_case:n { ##1 }
}
\seq_use:Nn \l__ruben_capitalize_out_seq { ~ }
}
\cs_generate_variant:Nn \ruben_capitalize:n { o }
\ExplSyntaxOff
\newcommand{\myhello}{hello world}
\begin{document}
\capitalize{Hello world}
\capitalize{hello world}
\capitalize{\myhello}
\capitalize\myhello
\end{document}
经典方法\obeylines
要求你发布它前吸收论点:
% First setup obeyspace and give a meaning to active space
\newcommand{\capitalize}{\begingroup\obeyspaces\setupcapspace\docapitalize}
% Just absorb the argument and end the group
\newcommand{\docapitalize}[1]{#1\endgroup}
% Define (locally) the behavior of active space
\begingroup\lccode`~=`\ % <--- don't forget this one
\lowercase{\endgroup\newcommand\setupcapspace{\def~{\space\MakeUppercase}}}
最后两行也可以
{\obeyspaces\gdef\setupcapspace{\def {\space\MakeUppercase}}}
但该\lowercase
方法避免了\obeyspaces
可能出现的虚假空间问题。
然而,分隔参数方法肯定更好,因为它允许\capitalize
将其作为其他命令的参数。
答案3
基于 LuaLaTeX 的解决方案。我们定义了一个名为 的新宏\capitalize
,它使用 Lua 函数string.upper
和tex.sprint
。 的参数\capitalize
可以是硬编码字符串,也可以是生成字符串的宏。
% !TEX TS-program = lualatex
\documentclass{article}
{\catcode\%=12
\gdef\capitalize#1{
\directlua{ str="#1";
tex.sprint ( string.gsub(" "..str, "%W%l",
string.upper):sub(2)) } }
}
\begin{document}
\capitalize{Once upon a time there was a princess
who lived in a great palace that was close to the
edge of a dark and mysterious forest.}
\end{document}
答案4
虽然您的问题是关于嵌套的\def
,s,但应用程序正在将单词大写。该titlecaps
包使用\titlecap
宏执行此操作。它允许参数具有广泛的灵活性,包括字体样式和大小更改。它还允许您设置排除词不大写(除了可选地作为参数的第一个单词)。它可以在很大程度上克服在将单词大写时出现的前导标点符号(如括号和方括号)。它可以将变音符号大写等。
在 MWE 中,我展示了您的“hello world”示例,然后使用了包文档中的过度示例。
\documentclass{article}
\usepackage{titlecaps}
\def\bs{$\backslash$}
\begin{document}
\titlecap{hello world}
\Addlcwords{for a is but and with of in as the etc on to if}
\titlecap{%
to know that none of the words typed in this paragraph were initially
upper cased might be of interest to you. it is done to demonstrate the
behavioral features of this package. first, you should know the words
that i have pre-designated as lower case. they are: ``for a is but and
with of in as the etc on to if.'' you can define your own list. note
that punctuation, like the period following the word ``if'' did not mess
up the search for lower case (nor did the quotation marks just now).
punctuation which is screened out of the lower-cased word search pattern
include . , : ; ( ) [ ] ? ! ` ' however, I cannot screen text braces;
\{for example in\} is titled, versus (for example in), since the braces
are not screened out in the search for pre-designated lower-case words
like for and in. However, \texttt{\bs textnc} provides a workaround:
\{\textnc{for example in}\}. titlecap will consider capitalizing
following a (, [, \{, or - symbol, such as (abc-def). you can use your
text\textit{\relax xx} commands, like i just did here with the prior xx,
but if you want the argument of that command to not be titled, you
either need, in this example, to add \textit{xx} to the lowercase word
list, which you can see i did not. instead, i put ``\bs relax~xx'' as
the argument, so that, in essence, the \bs relax was capitalized, not
the x. Or you could use \texttt{\bs textnc} . here i demonstrate that
text boldface, \textbf{as in the \bs textbf command}, also works fine,
as do \texttt{texttt}, \textsl{textsl}, \textsc{textsc},
\textsf{textsf}, \textit{etc}. titlecap will work on diacritical marks,
such as \"apfel, \c cacao \textit{etc.}, \scriptsize fontsize \LARGE
changing commands\normalsize\unskip, as well as national symbols such as
\o laf, \ae gis, and \oe dipus. unfortunately, i could not get it to
work on the \aa~nor the \l~symbols. the method will work with some
things in math mode, capitalizing symbols if there is a leading space,
$x^2$ can become $ x^2$, and it can process but it will not capitalize
the greek symbols, such as $\alpha$, and will choke on most macros, if
they are not direct character expansions. Additionally,
\textsf{titlecaps} also works with font changing declarations, for
example, \bs itshape\bs sffamily. \itshape\sffamily you can see that it
works fine. likewise, any subsequent \bs textxx command will, upon
completion, return the font to its prior state, such as this
\textbf{textbf of some text}. you can see that i have returned to the
prior font, which was italic sans-serif. now I will return to upright
roman\upshape\rmfamily. a condition that will not behave well is inner
braces, such as \ttfamily \bs titlecap\{blah \{inner brace material\}
blah-blah\}. \rmfamily see the section on quirks and limitations for a
workaround involving \texttt{\bs textnc}. titlecap will always
capitalize the first word of the argument (\textbf{even if it is on the
lower-case word list}), unless \texttt{\bs titlecap} is invoked with an
optional argument that is anything other than a capital p. in that case,
the first word will be titled \textit{unless} it is on the lowercase
word list. for example, i will do a \bs titlecap[\relax s]\{\relax
a~big~man\} and get ``\titlecap[s]\textnc{a big man}'' with the ``a''
not titled. i hope this package is useful to you, but as far as using
\textsf{titlecaps} on such large paragraphs\ldots \textbf{do not try
this at home!}}
\end{document}