![字符串解析宏在 biblatex 的自定义引用命令中失败?](https://linux22.com/image/250177/%E5%AD%97%E7%AC%A6%E4%B8%B2%E8%A7%A3%E6%9E%90%E5%AE%8F%E5%9C%A8%20biblatex%20%E7%9A%84%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BC%95%E7%94%A8%E5%91%BD%E4%BB%A4%E4%B8%AD%E5%A4%B1%E8%B4%A5%EF%BC%9F.png)
我正在尝试使用与以下相同的字符串解析宏字符串解析宏在小页面环境中失败吗?;但这次是在biblatex
——的背景下,我想我也遇到了类似的问题。
以下是一个 MWE,部分代码来自Biblatex:DeclareFieldFormat 中的 ifx:
\documentclass{article}
\def\parseMyNumHelper num0#1\relax{\edef\MyNum{#1}}
\def\parseMyNum#1{\edef\temp{#1}%
\expandafter\parseMyNumHelper\temp\relax}
\usepackage{filecontents}
\begin{filecontents}{\jobname.bib}
@misc{num01,
title = {Title1},
eprint = {MR001},
eprinttype = {mrnumber}}
@misc{num02,
title = {Title},
eprint = {002},
eprinttype = {mrnumber}}
@misc{num03,
title = {Title},
eprint = {MR003 (aa)},
eprinttype = {mrnumber}}
@misc{num04,
title = {Title},
eprint = {004 (bb)},
eprinttype = {mrnumber}}
\end{filecontents}
\usepackage{biblatex}
\addbibresource{\jobname.bib}
\DeclareFieldFormat{myentrykey}{%
\parseMyNum{#1} %
\textbf{Testing \MyNum: \textit{#1}}%
}
\makeatletter
\newbibmacro*{myentrycite}{% custom
\printfield[myentrykey]{entrykey}
} % end \newbibmacro*{
\makeatother
\DeclareCiteCommand{\myentrycite}
{\usebibmacro{prenote}}
{\usebibmacro{citeindex}%
\printtext{\usebibmacro{myentrycite}}}
{}
{\usebibmacro{postnote}}
\begin{document}
\def\testvar{num01}
\typeout{ a== \testvar, }
\typeout{ -- \meaning\parseMyNumHelper}
\typeout{ -- \meaning\parseMyNum}
\parseMyNum{\testvar}
\typeout{ b== \testvar, - \temp, - \MyNum}
\myentrycite{num03}
\end{document}
在这里,作为 cite 命令的结果,我想打印引用条目键的最后一位数字。
如果你用 运行此代码一次pdflatex
,事情看起来很合理:
a== num01,
-- macro:num0#1\relax ->\edef \MyNum {#1}
-- macro:#1->\edef \temp {#1}\expandafter \parseMyNumHelper \temp \relax
b== num01, - num01, - 1
但是,如果您再次运行bibtex
,则pdflatex
在第二次运行时您会得到:
a== num01,
-- macro:num0#1\relax ->\edef \MyNum {#1}
-- macro:#1->\edef \temp {#1}\expandafter \parseMyNumHelper \temp \relax
b== num01, - num01, - 1
! Use of \parseMyNumHelper doesn't match its definition.
\temp ->n
um03
l.59 \myentrycite{num03}
? i
insert>\typeout{ \meaning\parseMyNumHelper }
macro:num0#1\relax ->\edef \MyNum {#1}
...
奇怪的是 - 我期望num03
到达宏,并且确实到达了;但是,宏仍然崩溃了?更奇怪的是 - 如果我们继续到最后,会生成以下输出:
...我所看到的是解析“几乎”成功(我得到了我预期的“测试 X”) - 除了返回了最后一个有效设置的数字......
那么,字符串解析宏/小页面......,问题是 minipage 没有正确的参数,因此干扰了下一个 \parse 命令 - 在Biblatex:DeclareFieldFormat 中的 ifx中提到:
发生这种情况是因为您正在使用 \ifx 测试,只有当 M 和 R 的类别代码为“字母”时,该测试才会成立。biblatex 将参数作为去标记化的字符串传递,因此测试失败。
...我不太确定这是否与此有关。
但无论如何,如果有人能解释为什么这个“字符串解析”宏在这里崩溃,我将不胜感激 - 以及如何让它按预期工作。
非常感谢您的任何回答,
干杯!
答案1
问题是您要传递的字符串\parseMyNumHelper
由类别代码 12 的标记组成。因此,您必须使用类别代码 12 个字母作为分隔符;最简单的方法是
\expandafter\def\expandafter\parseMyNumHelper \detokenize{num}0#1\relax{\def\MyNum{#1}}
我们\detokenize
将字符的类别代码改为num
12。
还有其他方法:
\begingroup\lccode`!=`n \lccode`?=`u \lccode`/=`m
\lowercase{\endgroup\def\parseMyNumHelper !?/0#1\relax}{\def\MyNum{#1}}
或者,带着我的regexpatch
包裹,
\usepackage{regexpatch}
\def\parseMyNumHelper num0#1\relax{\def\MyNum{#1}}
\xpatchparametertext{\parseMyNumHelper}{num}{\cO n \cO u \cO m}{}{}
当然,你的\testvar
测试不会成功,除非你把它改成
\edef\testvar{\detokenize{num01}}
\typeout{ a== \testvar, }
\typeout{ -- \meaning\parseMyNumHelper}
\typeout{ -- \meaning\parseMyNum}
\parseMyNum{\testvar}
\typeout{ b== \testvar, - \temp, - \MyNum}
更简单的方法
您想要删除四个标记:
\def\parseMyNumHelper #1#2#3#4#5\relax{\def\MyNum{#5}}
无类别代码问题。
答案2
如果您想使用 epl3,您可以通过模块解析字符串l3str
。
以下代码适用于最新版本的 expl3。
\usepackage{xparse,l3str}
\ExplSyntaxOn
\NewDocumentCommand \parseMyNum { m }
{
\tl_set:Nx \l_tmpa_tl {#1}
\cs_gset:Npn \MyNum { \str_substr:Nnn \l_tmpa_tl {4 } { \tl_count:N \l_tmpa_tl } }
}
\ExplSyntaxOff
但是如果你使用较旧的版本,你可以\tl_length:N
使用\tl_count:N
\usepackage{xparse,l3str}
\ExplSyntaxOn
\NewDocumentCommand \parseMyNum { m }
{
\tl_set:Nx \l_tmpa_tl {#1}
\cs_gset:Npn \MyNum { \str_substr:Nnn \l_tmpa_tl {4 } { \tl_length:N \l_tmpa_tl } }
}
\ExplSyntaxOff
完整代码如下:
\documentclass{article}
\usepackage{xparse,l3str}
\ExplSyntaxOn
\NewDocumentCommand \parseMyNum { m }
{
\tl_set:Nx \l_tmpa_tl {#1}
\cs_gset:Npn \MyNum { \str_substr:Nnn \l_tmpa_tl {4 } { \tl_count:N \l_tmpa_tl } }
}
\ExplSyntaxOff
\usepackage{filecontents}
\begin{filecontents}{\jobname.bib}
@misc{num01,
title = {Title1},
eprint = {MR001},
eprinttype = {mrnumber}}
@misc{num02,
title = {Title},
eprint = {002},
eprinttype = {mrnumber}}
@misc{num03,
title = {Title},
eprint = {MR003 (aa)},
eprinttype = {mrnumber}}
@misc{num04,
title = {Title},
eprint = {004 (bb)},
eprinttype = {mrnumber}}
\end{filecontents}
\usepackage{biblatex}
\addbibresource{\jobname.bib}
\DeclareFieldFormat{myentrykey}{%
\parseMyNum{#1}%
\textbf{Testing \MyNum: \textit{#1}}%
}
\makeatletter
\newbibmacro*{myentrycite}{% custom
\printfield[myentrykey]{entrykey}
} % end \newbibmacro*{
\makeatother
\DeclareCiteCommand{\myentrycite}
{\usebibmacro{prenote}}
{\usebibmacro{citeindex}%
\printtext{\usebibmacro{myentrycite}}}
{}
{\usebibmacro{postnote}}
\begin{document}
\def\testvar{num01}
\typeout{ a== \testvar, }
\typeout{ -- \meaning\parseMyNum}
\parseMyNum{\testvar}
\typeout{ b== \testvar, - \MyNum}
\myentrycite{num03}
\end{document}
答案3
好的,我终于取得了一些进展,所以我只是将其作为一种可能的解决方法发布;但是,我仍然想更好地了解发生了什么。
因为在我的例子中,我严格使用 5 个字母的单词,并且提取最后一个字母 - 经过大量的调整\detokenize
和一堆其他我只能猜测的命令后,我找到了最简单的方法 - 只需定义一个带有 5 个输入参数的宏,并且据说字符串的字符会自动拆分到位置。这是一个有点令人失望的解决方法,因为它只在有限的情况下有效(如果您处理的是短的、固定长度的字符串)。但至少(对我来说有点令人惊讶,在对这个例子苦苦思索了一段时间之后),它似乎确实有效。
以下是相对于 OP 有所改变的功能片段:
...
% notes:
% a1: cannot call this directly, still needs a wrapper!
% (though only from \parseMyNum{}: "Runaway argument? ... ")
% a2: the `typeout` here also returns cruft! comment it out!
%
\def\parseMyNumHelperB#1#2#3#4#5{% (a1)
% \typeout{ 01 #1 #2 #3 #4 #5}% (a2)
#5% output only 5-th character
}
% b1: this `expandafter..relax` stanza must be present for proper parsing!
%
\def\parseMyNumHelper#1{%
\edef\MyNum{\expandafter\parseMyNumHelperB#1\relax} % (b1)
\typeout{ == MyNum \MyNum ==} %
}
% c1: with newcommand, so we can use braces to pass variables around
% c2: here we cannot use braces anymore to pass the variable
%
\newcommand*\parseMyNum[1]{% (c1)
\long\edef\temp{#1}%
\parseMyNumHelper\temp% (c2)
}
....
% d1: directly with #1, used to pass only one char; that
% required storage in a variable (d2) - not anymore
%
\DeclareFieldFormat{myentrykey}{\relax%
%\edef\eetmp{#1} % (d2)
\parseMyNum{#1} % (d1)
\textbf{Testing \MyNum: \textit{#1}}\relax%
}
...
为什么这个可以在下工作biblatex
,而其他代码却不行,我真的不知道——但至少它有效:
...并且终端日志看起来也更好:
a== num01,
-- macro:#1->\edef \MyNum {\expandafter \parseMyNumHelperB #1\relax } \typeout
{ == MyNum \MyNum ==}
-- macro:#1->\long \edef \temp {#1}\parseMyNumHelper \temp
== MyNum 1\relax ==
b== num01, - num01, - 1\relax
== MyNum 3\relax ==
好吧,我希望可以帮助某人 - 和/或启发某人对这个问题做出解释:)
..
干杯!
编辑:仅发布一些对我有用的链接: