我有一个用于解析输入数据的用途tabular
,但它不是tabular
环境的一部分,而是宏的参数。因此我开发了两个宏,\readTABrow
一个将&
分离的列表读入不同的标记,另一个将分离的列表\readMANYrows
读入许多标记。\\
当我将原本会出现在tabular
“行”或“表”中的参数传递给这两个宏时,它们的行为都符合预期。问题出现在我从 中获取输出时\readMANYrows
,这会为我提供每行一个标记,每个标记都填充了&
- 分隔的数据,我将这个标记传递给 ,\readTABrow
以便进一步将表格行解析为单个单元格数据。
我怀疑这些&
字符的“性质”在被解析时会发生变化readMANYrows
,当它们到达时,它们不再看起来像制表符分隔符\readTABrow
。
我的问题是如何&
在 期间保留字符的制表符“性质” \readMANYrows
,以便我可以将包含它们的令牌直接传递给\readTABrow
而不会出现问题。
这是我的 MWE:
\documentclass{article}
\usepackage{ifthen}
\makeatletter
% FOR PROCESSING A &-SEPARATED ROW
\newcounter{TABcellindex@}
\newcommand\readTABrow[2]{%
\def\doneTABread{F}%
\def\postTAB{#2}%
\setcounter{TABcellindex@}{0}%
\whiledo{\equal{\doneTABread}{F}}{%
\stepcounter{TABcellindex@}%
\expandafter\processTAB\postTAB&\\%
\ifthenelse{\equal{\preTAB}{}}{%
\addtocounter{TABcellindex@}{-1}%
\def\doneTABread{T}%
}{%
\expandafter\protected@edef\csname #1X\roman{TABcellindex@}\endcsname{%
\preTAB}%
}%
}%
% \#1TABcells GIVES HOW MANY TAB COLUMNS WERE PROCESSED
\expandafter\xdef\csname #1TABcells\endcsname{\arabic{TABcellindex@}}%
}
\def\processTAB#1\\{%
\protected@edef\preTAB{#1}%
\protected@edef\postTAB{#2}%
}
% FOR PROCESSING A \\-SEPARATED GROUP OF ROWS
\newcounter{ROWcellindex@}
\newcommand\readMANYrows[2]{%
\def\doneROWread{F}%
\def\postROW{#2\\}%
\setcounter{ROWcellindex@}{0}%
\whiledo{\equal{\doneROWread}{F}}{%
\stepcounter{ROWcellindex@}%
\expandafter\processROW\postROW||%
\ifthenelse{\equal{\postROW}{}}{%
\def\doneROWread{T}%
}{}%
\expandafter\protected@edef\csname #1X\roman{ROWcellindex@}\endcsname{%
\preROW}%
}%
% \#1ROWs GIVES HOW MANY ROWS WERE PROCESSED
\expandafter\xdef\csname #1ROWs\endcsname{\arabic{ROWcellindex@}}%
}
\def\processROW#1\\#2||{%
\protected@edef\preROW{#1}%
\protected@edef\postROW{#2}%
}
\makeatother
\begin{document}
\verb|\readTABrow| Input: \verb|{a&b&c&d}|
\readTABrow{myrow}{a&b&c&d}\par
.\myrowXi.\par
.\myrowXii.\par
.\myrowXiii.\par
.\myrowXiv.\par
COLUMNS: \myrowTABcells\par
---
\verb|\readMANYrows| Input: \verb|{a&b&c&d\\b\\\\ c}|
\readMANYrows{mydata}{a&b&c&d\\b\\\\ c}\par
.\detokenize\expandafter{\mydataXi}.(detokenized)\par
.\mydataXii.\par
.\mydataXiii.\par
.\mydataXiv.\par
ROWS: \mydataROWs\par
---
\verb|\readTABrow| Input: \verb|\mydataXi|
\readTABrow{mydataXi}{\mydataXi}\par
.\detokenize\expandafter{\mydataXiXi}.(detokenized)\par
COLUMNS:\mydataXiTABcells\par
\end{document}
下面给出的输出显示了\readTABrow
和的个别情况\readMANYrows
,它们都按我的预期运行,而最后一个例子表明\readTABrow
,当传递由生成的令牌时\readMANYrows
,会错误地将该令牌确定为&
免费的,由单个单元组成,而不是由 4 个单元组成
请注意, 的使用\detokenize
仅作为调试辅助手段,用于显示包含字符的各种宏的内容&
。 没有\detokenize
d 数据传递给\readTABrow
。
相关的补充说明一下,在解析&
和\\
标记时,是否传统上会忽略它们后面的空格?正如您从我的 MWE 中看到的那样,我不会忽略它们。
更新:
David Carlisle 解决了这个问题,尽管他仍然不太清楚我想要什么。所以,让我展示一下。如果 MWE 的最后几行被替换为
\verb|\readTABrow| Input: \verb|\mydataXi|
\def\foo{\readTABrow{mydataXi}}
\expandafter\foo\expandafter{\mydataXi}\par
.\mydataXiXi.\par
.\mydataXiXii.\par
.\mydataXiXiii.\par
.\mydataXiXiv.\par
COLUMNS:\mydataXiTABcells\par
最终输出变成
因此,在 David 的帮助下,我可以使用我的两个例程来获取类似表格的参数,例如
{a&b&c\\d&e&f\\g&h&i}
并存储表组件,就像我输入了数组元素一样:
\edef\mydataXiXi{a}
\edef\mydataXiXii{b}
\edef\mydataXiXiii{c}
\edef\mydataXiiXi{d}
\edef\mydataXiiXii{e}
\edef\mydataXiiXiii{f}
\edef\mydataXiiiXi{g}
\edef\mydataXiiiXii{h}
\edef\mydataXiiiXiii{i}
可以通过以下方式访问\csname mydataX\roman{row}X\roman{col}\endcsname
当与参数相关的输出必须拆分(跨页面水平)或拼接(与其他数据合并),或者稍后需要调用单个单元格时,这将很有用。
答案1
我仍然不清楚您需要什么输出,但在最后一次调用中,您没有传递一个&
单独的列表,而是传递了一个扩展到这样一个列表的宏,因此您需要先扩展它。
\def\foo{\readTABrow{mydataXi}}
\expandafter\foo\expandafter{\mydataXi}\par
.\detokenize\expandafter{\mydataXiXi}.(detokenized)\par
COLUMNS:\mydataXiTABcells\par
生成 COLUMNS:4 而不是 COLUMNS:1
至于相关问题,如果您试图模拟正常的 TeX\halign
行为,解析器不会删除空格,因此会生成空格标记,但 LaTeX 始终避免排版由此产生的任何空白,例如每个单元前导码以 开头\ignorespaces
并以 结尾\unskip
(如果处于 hmode)。
答案2
我不喜欢重新发明轮子;使用 LaTeX3 宏很容易。请注意,如果将参数作为控制序列扩展为所需的标记列表,则 *-variant 用于扩展参数。如果您可以完全控制输入中可能出现的标记,即如果它们只是不可扩展的标记(但当然必须小心使用前导\\
),则可以省去它。
有一些代码重复,因此宏可以简化。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\readTABrow}{ s m m }
{
\IfBooleanTF{#1}
{ \segle_read_tab_row:no { #2 } { #3 } }
{ \segle_read_tab_row:nn { #2 } { #3 } }
}
\NewDocumentCommand{\readMANYrows}{ s m m }
{
\IfBooleanTF{#1}
{ \segle_read_many_rows:no { #2 } { #3 } }
{ \segle_read_many_rows:nn { #2 } { #3 } }
}
\seq_new:N \l_segle_row_seq
\seq_new:N \l_segle_manyrows_seq
\int_new:N \l_segle_row_int
\int_new:N \l_segle_manyrows_int
\cs_new_protected:Npn \segle_read_tab_row:nn #1 #2
{
\seq_set_split:Nnn \l_segle_row_seq { & } { #2 }
\int_zero:N \l_segle_row_int
\seq_map_inline:Nn \l_segle_row_seq
{
\int_incr:N \l_segle_row_int
\cs_set:cpn { #1 X \int_to_roman:n { \l_segle_row_int } } { ##1 }
}
\cs_set:cpx { #1 TABcells } { \int_to_arabic:n { \l_segle_row_int } }
}
\cs_generate_variant:Nn \segle_read_tab_row:nn { no }
\cs_new_protected:Npn \segle_read_many_rows:nn #1 #2
{
\seq_set_split:Nnn \l_segle_manyrows_seq { \\ } { #2 }
\int_zero:N \l_segle_manyrows_int
\seq_map_inline:Nn \l_segle_manyrows_seq
{
\int_incr:N \l_segle_manyrows_int
\cs_set:cpn { #1 X \int_to_roman:n { \l_segle_manyrows_int } } { ##1 }
}
\cs_set:cpx { #1 ROWs } { \int_to_arabic:n { \l_segle_manyrows_int } }
}
\cs_generate_variant:Nn \segle_read_many_rows:nn { no }
\ExplSyntaxOff
\begin{document}
\verb|\readTABrow| Input: \verb|{a&b&c&d}|
\readTABrow{myrow}{a&b&c&d}\par
.\myrowXi.\par
.\myrowXii.\par
.\myrowXiii.\par
.\myrowXiv.\par
COLUMNS: \myrowTABcells\par
\verb|\readMANYrows| Input: \verb|{a&b&c&d\\b\\\\ c}|
\readMANYrows{mydata}{a&b&c&d\\b\\\\ c}\par
.\detokenize\expandafter{\mydataXi}.\par
.\mydataXii.\par
.\mydataXiii.\par
.\mydataXiv.\par
ROWS: \mydataROWs\par
---
\verb|\readTABrow| Input: \verb|\mydataXi|
\readTABrow*{mydataXi}{\mydataXi}\par
.\detokenize\expandafter{\mydataXiXi}.(detokenized)\par
COLUMNS:\mydataXiTABcells\par
\end{document}