使用 xstring 替换字符串中重新格式化的名称

使用 xstring 替换字符串中重新格式化的名称

我想使用包中的\IfSubStr和宏,有条件地将出现在较长字符串中的字符串加粗。我遇到的问题与宏的扩展有关,这取决于和宏的工作。我怎样才能让宏正常工作?\StrSubstitutexstring\makebold\surnamefirst\surnamelast\makebold

\documentclass{article}

\usepackage{xstring}

\newcommand{\name}{John Doe}

\makeatletter

% Macro to extract and store "last name" from full-name string
\newcommand{\lastname}{%
    \IfSubStr{\name}{.}%
        {\StrBehind{\name}{. }}%
        {\StrBehind{\name}{ }}%
        [\@lastname]
}

% Extract and store first name from full-name string
\newcommand{\firstname}{%
    \StrBefore{\name}{ }[\@firstname]
}

% Extract first initial from full-name string
\newcommand{\firstinitial}{%
    \StrLeft{\name}{1}[\@firstinitial]
}

% Extract middle initial (if any) from full-name string
\newcommand{\middleinitial}{%
    \StrBetween{\name}{ }{.}[\@middleinitial]
}

\newcommand{\testxstring}{%
    \middleinitial % Find middle initial
    \IfStrEq{\@middleinitial}{}%
        {No middle initial!}%
        {Middle initial: \@middleinitial}
}

% Surname first
\newcommand{\surnamefirst}{%
    \firstinitial
    \middleinitial % Find middle initial
    \lastname
    \IfStrEq{\@middleinitial}{}%
        {\@lastname, \@firstinitial.}%
        {\@lastname, \@firstinitial.~\@middleinitial.}
}

% Surname last
\newcommand{\surnamelast}{%
    \firstinitial
    \middleinitial % Find middle initial
    \lastname
    \IfStrEq{\@middleinitial}{}%
        {\@firstinitial.~\@lastname}%
        {\@firstinitial.~\@middleinitial.~\@lastname}
}

% Render full name in bold
\newcommand{\makeboldname}[1]{%
    \expandarg
    \IfSubStr{#1}{\getvalue{name}}%
        {\StrSubstitute{#1}{\name}{\textbf{\name}}\par}%
        {#1}
}

% Render only last name in bold
\newcommand{\makeboldlastname}[1]{%
    \lastname
    \expandarg
    \IfSubStr{#1}{\@lastname}%
        {\StrSubstitute{#1}{\@lastname}{\textbf{\@lastname}}\par}%
        {#1}
}

% Render surname-first and surname-last in bold
\newcommand{\makebold}[1]{%
    \expandarg
    \IfSubStr{#1}{\surnamefirst}%
        {\StrSubstitute{#1}{\surnamefirst}{\textbf{\surnamefirst}}\par}%
        {\IfSubStr{#1}{\surnamelast}%
            {\StrSubstitute{#1}{\surnamelast}{\textbf{\surnamelast}}\par}%
            {#1}%
        }
}

\makeatother

\begin{document}

    \name

    \surnamefirst

    \surnamelast

    \makeboldname{His name was John Doe.}

    \makeboldlastname{Again, Doe was his last name.}

    \makebold{I heard that Doe, J. was from Kentucky.}

    \makebold{There was also a J. Doe from New York.}

\end{document}

预期输出:

约翰·多伊

多伊,J.

J·多伊

他以前的名字是约翰·多伊

再次,多伊是他的姓氏。

我听说多伊,J.来自肯塔基州。

还有J·多伊来自纽约。

答案1

这似乎是正则表达式的工作:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\definename}{m}
 {
  \adam_name_define:n { #1 }
 }

\NewDocumentCommand{\makebold}{m}
 {
  \adam_name_makebold:n { #1 }
 }

\NewDocumentCommand{\makeboldlastname}{m}
 {
  \adam_name_makebold_lastname:n { #1 }
 }


\tl_new:N \l_adam_name_full_tl
\tl_new:N \l_adam_name_first_tl
\tl_new:N \l_adam_name_last_tl
\tl_new:N \l_adam_name_initials_tl
\tl_new:N \l__adam_name_input_tl

\cs_new_protected:Nn \adam_name_define:n
 {
  % store the full name
  \tl_set:Nn \l_adam_name_full_tl { #1 }
  % duplicate it in two token lists to be modified
  \tl_set_eq:NN \l_adam_name_first_tl \l_adam_name_full_tl
  \tl_set_eq:NN \l_adam_name_last_tl \l_adam_name_full_tl
  % remove everything from the last space to the end (first name)
  \regex_replace_once:nnN { (.*)\s[^\s]*\Z } { \1 } \l_adam_name_first_tl
  % remove everything up to the last space (surname)
  \regex_replace_once:nnN { .*\s([^\s]*)\Z } { \1 } \l_adam_name_last_tl
  % duplicate the token list with the first name
  \tl_set_eq:NN \l_adam_name_initials_tl \l_adam_name_first_tl
  % remove letters after the first in the first name part, replacing them with a period (initials)
  \regex_replace_all:nnN { ([[:alpha:]])[[:alpha:]]+ } { \1. } \l_adam_name_initials_tl
 }

\cs_new_protected:Nn \adam_name_makebold:n
 {
  \tl_set:Nn \l__adam_name_input_tl { #1 }
  % \u{<token list name>} stands for the contents of the token list
  % we'll search for “full name” or “initials surname” or
  % “surname, first name” or “surname, initials”
  % and replace the match with \textbf{\match}
  \regex_replace_once:nnN
   {
    (
     \u{l_adam_name_full_tl}                                   % name surname
     |
     \u{l_adam_name_initials_tl} \s \u{l_adam_name_last_tl}    % initials surname
     |
     \u{l_adam_name_last_tl} , \s \u{l_adam_name_first_tl}     % surname, name
     |
     \u{l_adam_name_last_tl} , \s \u{l_adam_name_initials_tl}  % surname, initials
    )
   }
   { \c{textbf} \cB\{ \1 \cE\} }
   \l__adam_name_input_tl
  % print
  \tl_use:N \l__adam_name_input_tl
 }

\cs_new_protected:Nn \adam_name_makebold_lastname:n
 {
  \tl_set:Nn \l__adam_name_input_tl { #1 }
  % search for the surname and replace it with \textbf{<match>}
  \regex_replace_once:nnN
   { (\u{l_adam_name_last_tl}) }
   { \c{textbf} \cB\{ \1 \cE\} }
   \l__adam_name_input_tl
  \tl_use:N \l__adam_name_input_tl
 }

\ExplSyntaxOff

\begin{document}

\definename{John Doe}

\makebold{His name was John Doe.}

\makebold{His name was J. Doe.}

\makebold{His name was Doe, John.}

\makebold{His name was Doe, J. again.}

\makeboldlastname{Again, Doe was his last name.}

\definename{John T. Doe}

\makebold{His name was John T. Doe.}

\makebold{His name was J. T. Doe.}

\makebold{His name was Doe, John T. again.}

\makebold{His name was Doe, J. T. again.}

\makeboldlastname{Again, Doe was his last name.}

\end{document}

在此处输入图片描述

答案2

xstring正如承诺的那样,下面是其作者 Christian Tellechea给出的答案。

\documentclass{article}

\usepackage[T1]{fontenc}

\usepackage{xstring}

\def\makebold#1#2{%
    \begingroup\expandarg
    \StrCount{#1}{ }[\nbspaces]%
    \StrCut[\nbspaces]{#1}{ }\firstname\lastname
    \StrSplit\firstname1\firstnameinitial\fisrtnameremain
    \IfSubStr\fisrtnameremain{ }
        {\StrBehind\fisrtnameremain{ }[\fisrtnameremain]}
        {\let\fisrtnameremain\empty}%
    \edef\firstnameinitial{\firstnameinitial.\ifx\fisrtnameremain\empty\else\space\fi\fisrtnameremain}%
    \edef\stringA{#1}%
    \edef\stringB{\firstnameinitial\space\lastname}%
    \edef\stringC{\lastname, \firstname}%
    \edef\stringD{\lastname, \firstnameinitial}%
    \StrSubstitute{#2}\stringA{\expandafter\textbf\expandafter{\stringA}}[\finaltext]%
    \StrSubstitute\finaltext\stringB{\expandafter\textbf\expandafter{\stringB}}[\finaltext]%
    \StrSubstitute\finaltext\stringC{\expandafter\textbf\expandafter{\stringC}}[\finaltext]%
    \StrSubstitute\finaltext\stringD{\expandafter\textbf\expandafter{\stringD}}[\finaltext]%
    \StrSubstitute\finaltext\lastname{\expandafter\textbf\expandafter{\lastname}}[\finaltext]%
    \finaltext
    \endgroup
}

\begin{document}

    \makebold{John Doe}{His name was John Doe.}

    \makebold{John Doe}{His name was J. Doe.}

    \makebold{John Doe}{His name was Doe, John.}

    \makebold{John Doe}{Again, Doe was his last name.}
    \bigskip

    \makebold{John T. Doe}{His name was John T. Doe.}

    \makebold{John T. Doe}{His name was J. T. Doe.}

    \makebold{John T. Doe}{His name was Doe, John T. again.}

    \makebold{John T. Doe}{His name was Doe, J. T. again.}

    \makebold{John T. Doe}{Again, Doe was his last name.}

\end{document}

输出结果与预期一致。虽然正则表达式更通用,但它确实不那么冗长。

相关内容