How to know the number of columns declared in a tabular environment?
you can count the &
in the generated \halign
preamble
\documentclass{article}
\usepackage{array,xparse}
\NewDocumentEnvironment{TikzTable}{md<>O{c}m}{%
\IfNoValueTF{#2}{%
\begin{#1}[#3]{#4}}{%
\begin{#1}{#2}[#3]{#4}}
}{\end{#1}}
\makeatletter
\def\zzz#1#2{%
\ifx\zzz#3#1\expandafter\@gobble\else
\expandafter\@firstofone
\fi
{\zzz{\the\numexpr#1+1\relax}#3}}
\def\zz{\noalign{%
\xdef\nocols{\expandafter\zzz\expandafter1\@preamble&\zzz}%
}}
\makeatother
\begin{document}
\begin{TikzTable}{tabular}[b]{clrm{3cm}}
\zz
\hline
a&b&c& \nocols\ columns \\% At this point
\hline
1&1&1&1\\
\end{TikzTable}bob
\end{document}
Using the same idea as David, but with fancier expl3
code; this makes \nocols
available in every TikzTable
environment, but the name is customizable in a trailing optional argument in case you need to nest those tables.
\documentclass{article}
\usepackage{array,xparse}
\NewDocumentEnvironment{TikzTable}{md<>O{c}mO{\nocols}}
{%
\IfNoValueTF{#2}
{%
\begin{#1}[#3]{#4}
}{%
\begin{#1}{#2}[#3]{#4}
}%
\noalign{\CountColumns{#5}}
}
{\end{#1}}
\ExplSyntaxOn
\NewDocumentCommand{\CountColumns}{m}
{% count the number of & tokens in \@preamble
\regex_count:nvN { \cT\& } { @preamble } \l_tmpa_int
% they're one less than the columns
\cs_gset:Npx #1 { \int_eval:n { 1 + \l_tmpa_int } }
}
\cs_generate_variant:Nn \regex_count:nnN { nv }
\ExplSyntaxOff
\begin{document}
\begin{TikzTable}{tabular}[b]{clrm{3cm}}
\hline
a&b&c& This table has \nocols\ columns \\
\hline
1&1&1&1\\
\end{TikzTable}bob
\begin{TikzTable}{tabular}[b]{clrm{3cm}}[\foo]
\hline
a&b&c& This table has \foo\ columns \\
\hline
1&1&1&1\\
\end{TikzTable}bob
\end{document}
Here's a LuaLaTeX-based solution. It can handle tabular
, array
, tabular*
, tabularx
, and tabulary
environments, as well as the TikzTable
environment you proposed in your posting.
The number of declared columns of the most recently encountered tabular
-like environment is stored in a Lua variable called num_cols
. (Note that it's not necessary to do anything manually to obtain the number of declared columns for each tabular
-like environment -- the calculations are done fully automatically.) To access this number from the LaTeX side, the code provides a LaTeX macro named \numcols
.
The only input requirement is that the \begin{tabular}...{<columnspec>}
material all has to be on one and the same line.
Note that the code is designed to handle cases such as \begin{tabular} {@{} c >{$}c<{$} >{\raggedleft\arraybackslash\tt}p{1cm} @{}} \\
The column count will be "3". The solution method can also handle more-complicated column definitions, such as *{6}{l}
.
% !TEX TS-program = lualatex
\documentclass{article}
%% next few lines from the OP's query
\usepackage{array,xparse}
\NewDocumentEnvironment{TikzTable}{md<>O{c}m}{%
\IfNoValueTF{#2}{%
\begin{#1}[#3]{#4}}{%
\begin{#1}{#2}[#3]{#4}}}{%
\end{#1}}
\usepackage{tabularx,tabulary}
\usepackage{luacode} % for 'luacode' environment
\begin{luacode}
-- Define a global variable (num_cols), set up two
-- Lua functions, and assign one of them to the
-- 'process_input_buffer' callback.
-- Initialize counter variable
num_cols = 0
-- The next function computes the number of columns from
-- contents of string 'zz'
function calc_nums ( zz )
-- retain the first "{....}" substring
zz = zz:gsub ( "^.-(%b{}).-$", "%1" )
-- remove outermost pair of curly braces
zz = zz:sub ( 2 , -2 )
-- expand expressions such as "*{5}{l}" to "lllll"
zz = zz:gsub ( "%*%s-{(%d-)}%s-(%b{})" ,
function ( y , z )
z = z:sub ( 2 , -2 )
return string.rep ( z , y )
end )
-- omit all stuff in curly braces
zz = zz:gsub ( "%b{}" , "" )
-- some more characters to ignore
zz = zz:gsub ( "[@!|><%s%*]" , "" )
-- Number of remaining chars in "zz" is the number
-- of columns in the tabular-like environment.
num_cols = string.len ( zz )
end
-- Next the main Lua function. Scan all input lines.
-- If we detect start of a tabular-like env., lop off
-- the prefix and pass the remainder of the input line
-- to the 'calc_nums' function to determine number of cols.
-- ("%s-" means "0 or more instances of whitespace")
function get_col_spec ( s )
local zz
if string.find ( s , "\\begin.-{tabular}" ) then
zz = s:gsub ( "^.-\\begin.-{tabular}" , "" )
calc_nums ( zz )
elseif string.find ( s , "\\begin%s-{array}" ) then
zz = s:gsub ( "^.-\\begin%s-{array}" , "" )
calc_nums ( zz )
elseif string.find ( s , "\\begin%s-{tabular.}" ) then
zz = s:gsub ( "^.-\\begin%s-{tabular.}%s-%b{}" , "" )
calc_nums ( zz )
end
-- Do nothing if no match occurred.
end
-- Finally, assign 'get_col_spec' to the LuaTeX
-- 'process_input_buffer' callback.
luatexbase.add_to_callback( "process_input_buffer",
get_col_spec, "get_cols" )
\end{luacode}
%% LaTeX-side code: a macro to print out the number of columns
\newcommand\numcols{\directlua{tex.sprint(num_cols)}}
\begin{document}
%% Example 1
\begin{tabular} [b] { | * {6} { m{4mm }|} p { 12mm }|} \multicolumn{3}{@{}l}{\numcols\ columns}
\\ \hline a & b & c & d & e & f & g \\ \hline
\end{tabular}
\bigskip
%% Example 2
\begin{tabular} {@{} c *{2}{>{$}c<{$}} >{\raggedleft\arraybackslash\tt}p{3cm} @{}}
\hline
a & b & c & \numcols\ columns\\
\hline
\end{tabular}
\bigskip
%% Example 3: the 'TikzTable' case
aaa\begin{TikzTable}{tabular}[b]{@{}cllrm{1.55cm}@{}}\hline
a&b&c&d& \numcols\ columns \\
\hline
1&1&1&1&1\\
\hline
\end{TikzTable}zzz
\bigskip
%% Examples 4 to 6: array, tabular*, and tabularx
$\begin{array}{@{}*{6}{c}@{}} \hline a & b & c & d & e & \numcols \\ \hline \end{array}$
\bigskip
\begin{tabular*}{0.5\textwidth}{@{\extracolsep{\fill}}*{7}{l}@{}} \hline
z & y & x & w & v & u & \numcols\\ \hline
\end{tabular*}
\bigskip
\begin{tabularx}{0.7\textwidth}{@{}l*{8}{>{\centering\arraybackslash}X}r@{}} \hline
z & y & x & w & v & u & t & s & r & \numcols\\ \hline
\end{tabularx}
\end{document}