我想引用非 TeX 文本文件中包含固定字符串的一行。我只想获取其行号。我需要对同一个文件执行多次此操作,因此如果有解决方案不必在每次使用时读取该文件,那就太好了。
我发现了另一个与列表有关的 SE 问题,它几乎可以满足我的要求。它使用转义符并将链接嵌入到源文件中。我几乎可以使用它,但有两点我不喜欢它。1. 它要求我编辑文本文件并添加转义符和一些 TeX 代码。我真的不想这样做。2. 它似乎在最终结果(pdf 等)中包含列表,而我不需要或不想要它。
答案1
我将提出两种解决方案:简单和更复杂。
如果文件纯粹由文本材料组成,那么这种更简单的方法应该可行。我使用readarray
将文件导入到添加了行分隔符的文件中\def
。\\
然后我使用嵌套\listofitems
解析来查找包含定义的行\matchstring
。结果是一个宏\lineindex
,其中包含文件中匹配字符串的(逗号分隔)行号。A-1
表示未找到字符串匹配。
如果文件和搜索词是简单文本,不包含特殊字符,则使用简单代码。因此,此版本将遇到 4 种会破坏文件读取的情况,因为它是使用默认 LaTeX 代码读取的。这 4 种情况是:
#
%
将作为注释并遮挡输入行的剩余部分。\...
形成未定义的宏名。不平衡
{
或}
。
即使使用平衡括号,也存在其他限制:在括号组内不会找到搜索项;如果括号组跨越输入文件的多行,则从 到 的行将{
被}
视为单行输入(即,该组将被强制视为在一行上)。
因此,第一种方法仅适用于简单的文本搜索。
妇女权利委员会:
\documentclass{article}
\usepackage{readarray,listofitems,pgffor}
\begin{filecontents*}[overwrite]{mydata.txt}
There came from Nantucket a lemur,
who texted his doctor in beamer.
a misplaced italic
made his problem seem phallic
but in fact he had bruised just his femur.
\end{filecontents*}
% THE STRING OF INTEREST IS PLACED IN \matchstring
% WHICH IS THE ARGUMENT FOR \prep
\def\setmatchstring#1{\def\matchstring{#1}}
\def\searchformatch#1{%
\readarraysepchar{\\}
\expandafter\setsepchar\expandafter{\expandafter
\\\expandafter/\matchstring}
\readdef{#1}\mydatafile
\def\lineindex{}%
\readlist\mydata{\mydatafile}%
\foreachitem\z\in\mydata[]{%
\ifnum\listlen\mydata[\zcnt]>1\relax
\if\relax\lineindex\relax
\xdef\lineindex{\zcnt}\else
\xdef\lineindex{\lineindex, \zcnt}%
\fi
\fi
}%
\if\relax\lineindex\relax\gdef\lineindex{-1}\fi
}
\begin{document}
\setmatchstring{misplaced italic}
\searchformatch{mydata.txt}
The line containing the ``\matchstring'' is \lineindex.
\end{document}
对于更复杂的解决方案,所有四个限制都已通过 catcode 技巧克服。\setmatchstring
此修订版中的宏不使用大括号输入,而是遵循类似于的方法\verb
,其中用户指定不属于搜索字符串的分隔符:例如,使用@
分隔符
\setmatchstring @f}f}#@
将搜索字符串设置为f}f}#
。以下是 MWE:
\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\usepackage{readarray,listofitems,pgffor}
\begin{filecontents*}[overwrite]{mydata.txt}
Account: Framistat{$#@}
Password: \gY^&$%\{x#
Encrypted Hash Code: ef}f}#$_+{
Expiration: \today
\end{filecontents*}
% THE STRING OF INTEREST IS PLACED IN \matchstring
\def\setmatchstring#1{%
\begingroup
\def~##1#1{\gdef\matchstring{##1}\endgroup
\catcode`\\=0
\catcode`\{=1
\catcode`\}=2
\catcode`\%=14
\catcode`\#=6
}%
\catcode`\#=12
\catcode`\%=12
\catcode`\\=12
\catcode`\{=12
\catcode`\}=12
~%
}
\def\searchformatch#1{%
\readarraysepchar{\SFLINEEND}%
\expandafter\setsepchar\expandafter{\expandafter
\SFLINEEND\expandafter/\matchstring}%
\def\tmpA{%
\catcode`\#=12
\catcode`\%=12
\catcode`\\=12
\catcode`\{=12
\catcode`\}=12 }%
\def\tmpB{%
\readdef{#1}\mydatafile
\catcode`\\=0
\catcode`\}=2
\catcode`\{=1
\catcode`\%=14
\catcode`\#=6 }%
\expandafter
\tmpA\tmpB
\def\lineindex{}%
\readlist\mydata{\mydatafile}%
\foreachitem\z\in\mydata[]{%
\ifnum\listlen\mydata[\zcnt]>1\relax
\if\relax\lineindex\relax
\xdef\lineindex{\zcnt}\else
\xdef\lineindex{\lineindex, \zcnt}%
\fi
\fi
}%
\if\relax\lineindex\relax\gdef\lineindex{-1}\fi
}
\begin{document}
\setmatchstring @f}f}#@
\searchformatch{mydata.txt}
The line index containing ``\matchstring'' is \lineindex.
\setmatchstring !%\{x#!
\searchformatch{mydata.txt}
The line index containing ``\matchstring'' is \lineindex.
\end{document}