我有一个从表中读取的 2 个字符串列表,例如
\begin{filecontents*}{attributetools.dat}
Attribute
SL
SK
MC
SL
AA
PT
SK
UP
MC
\end{filecontents*}
我想创建一个包含唯一 2 个字符的字符串。结果应该是
SLSKMCAAPTUP
这是我迄今为止想到的办法。
\documentclass[10pt,a4paper]{article}
\usepackage{xparse}
\usepackage{xstring}
\usepackage{filecontents}
\usepackage{pgfplotstable}
\usepackage{ifthen}
\def\mystring{} % initialize
\def\extendmystring#1#2{\edef\mystring{#1\mystring#2}}
\begin{filecontents*}{attributetools.dat}
Attribute
SL
SK
MC
SL
AA
PT
SK
UP
MC
\end{filecontents*}
\begin{document}
\mystring{}
\pgfplotstableread[columns={Attribute}]{attributetools.dat}\datatableA
\pgfplotstablegetrowsof{\datatableA}
\pgfmathtruncatemacro{\RowsInTable}{\pgfplotsretval-1}
\foreach \k in {0,...,\RowsInTable}{
\pgfplotstablegetelem{\k}{Attribute}\of{\datatableA}
\def\tempstring{\pgfplotsretval}
\extendmystring{}{\tempstring}
}
Display concatenated string
\mystring
\end{document}
但每次迭代后,mystring 值都为空。
答案1
和expl3
:
\begin{filecontents*}{\jobname.dat}
Attribute
SL
SK
MC
SL
AA
PT
SK
UP
MC
\end{filecontents*}
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\makestring}{om}
{% #1 = macro to store the string in, #2 = file name
\almic_makestring:n { #2 }
\IfNoValueTF { #1 }
{ \str_use:N \l__almic_makestring_str }
{ \str_new:N #1 \str_set_eq:NN #1 \l__almic_makestring_str }
}
\ior_new:N \g__almic_makestring_stream
\str_new:N \l__almic_makestring_str
\seq_new:N \l__almic_makestring_seq
\cs_new_protected:Nn \almic_makestring:n
{
% clear the auxiliary sequence
\seq_clear:N \l__almic_makestring_seq
% read the file and store the items
\ior_open:Nn \g__almic_makestring_stream { #1 }
\ior_str_map_inline:Nn \g__almic_makestring_stream
{
\seq_put_right:Nn \l__almic_makestring_seq { ##1 }
}
\ior_close:N \g__almic_makestring_stream
% remove the first item
\seq_pop_left:NN \l__almic_makestring_seq \l_tmpa_tl
% remove the duplicates
\seq_remove_duplicates:N \l__almic_makestring_seq
%
\str_set:Nx \l__almic_makestring_str { \seq_use:Nn \l__almic_makestring_seq {} }
}
\ExplSyntaxOff
\begin{document}
\makestring{\jobname.dat}
\makestring[\test]{\jobname.dat}
\texttt{\meaning\test}
\end{document}
一个“经典”的实现:
\begin{filecontents*}{\jobname.dat}
Attribute
SL
SK
MC
SL
AA
PT
SK
UP
MC
\end{filecontents*}
\documentclass{article}
\makeatletter
\newread\almic@read
\newcommand{\makestring}[2][]{% #1 = macro to store the string in, #2 = file name
\begingroup\endlinechar=-1
\def\almic@out{#1}%
\let\almic@temp\@empty
\openin\almic@read=#2\relax
\ifeof\almic@read\else
\read\almic@read to \almic@line % throw away the first line
\expandafter\almic@make
\fi
}
\newcommand{\almic@make}{%
\loop\unless\ifeof\almic@read
\read\almic@read to \almic@line
\ifcsname almic@\almic@line\endcsname\else
\expandafter\let\csname almic@\almic@line\endcsname\@empty
\edef\almic@temp{\almic@temp\almic@line}%
\fi
\repeat
\ifx\almic@out\@empty
\almic@temp\expandafter\endgroup
\else
\edef\x{\endgroup\noexpand\newcommand*\expandafter\noexpand\almic@out{\almic@temp}}%
\expandafter\x
\fi
}
\makeatother
\begin{document}
\makestring{\jobname.dat}
\makestring[\test]{\jobname.dat}
\texttt{\meaning\test}
\end{document}
使用-shell-escape
Unix 工具:
\begin{filecontents*}{\jobname.dat}
Attribute
SL
SK
MC
SL
AA
PT
SK
UP
MC
\end{filecontents*}
\documentclass{article}
\usepackage{catchfile}
\makeatletter
\newcommand{\makestring}[2][]{%
\CatchFileDef\almic@temp{|"tail -n +2 #2 | awk '!x[$0]++' | tr -d '\string\n'"}{}%
\if\relax\detokenize{#1}\relax
\almic@temp
\else
\@ifdefinable{#1}{\let#1\almic@temp}%
\fi
}
\makeatother
\begin{document}
\makestring{\jobname.dat}
\makestring[\test]{\jobname.dat}
\texttt{\meaning\test}
\end{document}
看https://stackoverflow.com/a/11532197/923955对于awk
和https://unix.stackexchange.com/a/180902为了tr
。
答案2
为了实现这一点,您不需要 pgfplotstable 以外的任何东西。
\documentclass[10pt,a4paper]{article}
\usepackage{pgfplotstable}
\begin{filecontents*}{attributetools.dat}
Attribute
SL
SK
MC
SL
AA
PT
SK
UP
MC
\end{filecontents*}
\begin{document}
\pgfplotstableread[columns={Attribute}]{attributetools.dat}\datatableA
\pgfplotstablegetrowsof{\datatableA}
\pgfmathtruncatemacro{\RowsInTable}{\pgfplotsretval-1}
\foreach \k in {0,...,\RowsInTable}{
\pgfplotstablegetelem{\k}{Attribute}\of{\datatableA}
\ifnum\k=0
\xdef\myLst{\pgfplotsretval}
\xdef\mystring{\pgfplotsretval}
\else
\xdef\tmpElOld{0}
\edef\newelement{\pgfplotsretval}
\foreach \oldelement in \myLst
{\ifx\newelement\oldelement
% \typeout{duplicate}
\xdef\tmpElOld{1}
\fi}
\ifnum\tmpElOld=0
\xdef\myLst{\myLst,\pgfplotsretval}
\xdef\mystring{\mystring\pgfplotsretval}
\fi
\fi
}
\mystring
\end{document}
或者借助这个答案
\documentclass[10pt,a4paper]{article}
\usepackage{pgfplotstable}
\begin{filecontents*}{attributetools.dat}
Attribute
SL
SK
MC
SL
AA
PT
SK
UP
MC
\end{filecontents*}
\makeatletter% from https://tex.stackexchange.com/a/260921/121799
\newcommand*{\IfStringInList}[2]{%
\in@{,#1,}{,#2,}%
\ifin@
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
}
\makeatother
\begin{document}
\pgfplotstableread[columns={Attribute}]{attributetools.dat}\datatableA
\pgfplotstablegetrowsof{\datatableA}
\pgfmathtruncatemacro{\RowsInTable}{\pgfplotsretval-1}
\foreach \k in {0,...,\RowsInTable}{
\pgfplotstablegetelem{\k}{Attribute}\of{\datatableA}
\ifnum\k=0
\xdef\myLst{\pgfplotsretval}
\xdef\mystring{\pgfplotsretval}
\else
\xdef\tmpElOld{0}
\edef\newelement{\pgfplotsretval}
\edef\tmp{\noexpand\IfStringInList{\pgfplotsretval}{\myLst}{%
%\noexpand\typeout{duplicate-1}%
}{%
\noexpand\xdef\noexpand\myLst{\myLst,\pgfplotsretval}%
\noexpand\xdef\noexpand\mystring{\mystring\pgfplotsretval}}}
\tmp
\fi
}
\mystring\myLst
\end{document}
答案3
如果 2 个字符的字符串既不包含不平衡的花括号({
和/或}
不包含井号( )也不包含应该可见的#
百分比( ),则可以进行经典的实现。%
请注意,经典 TeX 的\read
-routine 仅在没有从包含不平衡/不匹配的花括号的标记的情况下才读取一行。否则,它会读取尽可能多的行,以获得一组没有不平衡/不匹配的花括号的标记。
因此,在下面的例子中,我以一种方式实现了一些东西,如果您愿意的话,您可以轻松切换到在 verbatim-catcode-régime 下读取东西。
\documentclass{article}
\begin{filecontents*}{attributetools.dat}
Attribute
SL
SK
MC
SL
AA
PT
SK
UP
MC
\end{filecontents*}
\makeatletter
%%-----------------------------------------------------------------------------
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@exchange[2]{#2#1}%
\newcommand\UD@tempa{}%
\newcommand\UD@tempb{}%
\newread\UD@read
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%.............................................................................
%% \UD@ExtractFirstArg{ABCDE} yields {A}
%%
%% \UD@ExtractFirstArg{{AB}CDE} yields {AB}
%%
%% !!! The argument of \UD@ExtractFirstArg must not be empty. !!!
%% You can check for emptiness via \UD@CheckWhetherNull before applying
%% \UD@ExtractFirstArg.
%%.............................................................................
\newcommand\UD@RemoveTillUD@SelDOm{}%
\long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}%
\newcommand\UD@ExtractFirstArg[1]{%
\romannumeral0%
\UD@ExtractFirstArgLoop{#1\UD@SelDOm}%
}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
{ #1}%
{\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
\UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
% \UD@GatherIfNotAlreadyPresentLoop{<write-handle>}%
% {<action to perform to collection without braces when loop is finished>}%
% {<strings collected so far, each string nested in braces>}%
% {<strings collected/concatenated so far, each string not nested in braces>}%
%%.............................................................................
\newcommand\UD@GatherIfNotAlreadyPresentLoop[4]{%
\ifeof#1%
\expandafter\UD@firstoftwo
\else
\expandafter\UD@secondoftwo
\fi
{#2{#4}}%
{%
\immediate\read#1 to\UD@tempa
\UD@CheckWhetherTempaInListLoop{#3}{%
\UD@exchange{{#3}{#4}}%
}{%
\expandafter\UD@exchange\expandafter{%
\romannumeral0%
\UD@exchange{ }%
{\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter}%
\expandafter\UD@exchange\expandafter{\expandafter{\romannumeral0%
\expandafter\UD@exchange\expandafter{\UD@tempa}{ #4}}}%
{\expandafter{\romannumeral0\expandafter\UD@exchange\expandafter{\expandafter{\UD@tempa}}{ #3}}}%
}%
}%
{\UD@GatherIfNotAlreadyPresentLoop{#1}{#2}}%
}%
}%
% \UD@CheckWhetherTempaInListLoop{<list>}
\newcommand\UD@CheckWhetherTempaInListLoop[1]{%
\UD@CheckWhetherNull{#1}{\UD@secondoftwo}{%
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\UD@tempb\UD@ExtractFirstArg{#1}%
\ifx\UD@tempa\UD@tempb
\expandafter\UD@firstoftwo
\else
\expandafter\UD@secondoftwo
\fi
{\UD@firstoftwo}%
{\expandafter\UD@CheckWhetherTempaInListLoop\expandafter{\UD@firstoftwo{}#1}}%
}%
}%
%%-----------------------------------------------------------------------------
%%
\immediate\openin\UD@read attributetools.dat %
\begingroup
\endlinechar=-1 %
\UD@exchange{%
% read away the first line:
\ifeof\UD@read\else\immediate\read\UD@read to\UD@tempa\fi
% do the loop:
\UD@GatherIfNotAlreadyPresentLoop{\UD@read}%
{\endgroup\newcommand\mystring}%
{}%
{}%
}{%
% In case you wish to have things read from file under verbatim-catcode-régime,
% uncomment the following three lines:
%\let\do\@makeother
%\dospecials
%\do\^^I% <- verbatimize horizontal tabs also.
}%
\immediate\closein\UD@read
\makeatother
\begin{document}
\show\mystring
\texttt{\string\mystring: \meaning\mystring}
\mystring
\end{document}