파일 내에서 일치하는 항목 확인

파일 내에서 일치하는 항목 확인

다음과 같은 목록이 포함된 파일이 있습니다.

apple
orange
pear
pineapple

항목이 해당 목록에 있는지 확인하는 매크로를 어떻게 만들 수 있습니까? 예:

  • \isfruit{pear}"1"을 반환합니다.
  • \isfruit{carrot}"0"을 반환합니다.

답변1

파일이 지나치게 길지 않으면 다음과 같이 할 수 있습니다.

\documentclass{article}

\makeatletter
\newread\@readisfruit
\newcommand\isfruit[3][]{%
  \begingroup\endlinechar=\m@ne
  \openin\@readisfruit=#2
  \def\@tempa{#3}%
  \def\@result{0}%
  \loop\unless\ifeof\@readisfruit
    \read\@readisfruit to \@tempb
    \ifx\@tempa\@tempb\def\@result{1}\fi
  \repeat             
  \closein\@readisfruit
  \edef\x{\endgroup\if!\noexpand#1!\@result\else\edef\noexpand#1{\@result}\fi}\x}
\makeatother

\begin{document}
\isfruit{village.dat}{pear}

\isfruit[\result]{village.dat}{carrot}\show\result

\end{document}

하지만 확장이 불가능하므로 선택적 인수(결과를 저장할 제어 시퀀스 이름)를 제공했습니다.

대체 정의

Ahmed Musa가 관찰한 바와 같이 파일이 길면 파일을 읽는 데 시간이 많이 걸릴 수 있으며, 특히 테스트가 여러 번 수행되는 경우 더욱 그렇습니다. 우리는 악용할 수 있다캐치파일:

\usepackage{catchfile}

\CatchFileDef{\village}{village.dat}{\endlinechar=`| }
\makeatletter
\newcommand{\isfruitA}[3][]{%
  \ifcsname loaded@#2\endcsname\else
    \expandafter\CatchFileDef\csname loaded@#2\endcsname{#2}{\endlinechar=`| }%
  \fi
  \begingroup\expandafter\let\expandafter\@tempa\csname loaded@#2\endcsname
  \edef\x{\endgroup\noexpand\in@{\unexpanded{#3}|}{\unexpanded\expandafter{\@tempa}}}\x
  \ifin@ \def\@result{1}\else \def\@result{0}\fi
  \if!\noexpand#1!\@result\else\edef#1{\@result}\fi}
\makeatother

이제 1을 인쇄합니다( 문자열에 나타나지 않는다고 가정하고 행을 구분하여 \isfruitA{village.dat}{pear}의 내용으로 확장하는 매크로를 정의한 후 ). 우리가 전화하면village.dat|

\isfruitA[\result]{village.dat}{orange}

테스트 결과(0 또는 1)가 매크로에 저장됩니다 \result. 파일은 한 번만 읽혀집니다. 다음과 같은 일부 패키지X문자열를 사용하면 번거로운 테스트를 피할 수 있습니다 \ifin@.

답변2

나는 이 질문에 대답하려고 노력할 것입니다. 어쩌면 누군가가 내 대답을 향상시킬 수 있을 것입니다.

로 설정된 \isfruit동안 매크로가 파일을 읽는 방식으로 매크로를 만들었습니다 . 모든 줄을 읽고 목록에 저장합니다. 그런 다음 목록을 인수와 비교합니다.enlinechar-1

\documentclass{article}
\usepackage{filecontents}
\begin{filecontents*}{fruit.tex}
apple
orange
pear
pineapple
\end{filecontents*}
\usepackage{etoolbox}
\newread\InputFruit
\newcommand*\isfruit[2]{%
     \begingroup%
      \def\MyList{}
      \openin\InputFruit=#1
       \endlinechar=-1%
       \loop\unless\ifeof\InputFruit
        \read\InputFruit to \reserveda
         \listxadd\MyList{\reserveda}
        \repeat
        \closein\InputFruit
      \xifinlist{#2}{\MyList}{in list}{not in list}
    \endgroup%
     }%
\begin{document}
\isfruit{fruit.tex}{apple}

\isfruit{fruit.tex}{foo}
\end{document}

답변3

@Marco Daniel: 매크로에 내부에 의해 무효화되지 않는 공백이 있습니다 \endlinechar=-1. 문서에 가로 모드로 표시됩니다. 게다가 임무는 \endlinechar=-1%였어야 했어 \endlinechar=-1 %. 또한 사용자에게 두 콜백을 변경할 수 있는 기회를 제공하기 위해 명령 \xifinlist외부 분기를 수행하겠습니다 . 그리고 목록 구분 기호( 에서 기본적으로 사용됨 )가 테스트할 원래 목록에 있을 수 있습니다.\isfruit\isfruit\xifinlist

\documentclass{article}
\usepackage{filecontents}
\begin{filecontents*}{fruit.tex}
apple
orange
pear
pineapple
\end{filecontents*}
\begingroup
\catcode`\|=3
\endlinechar=-1
\makeatletter
\gdef\ifinfruitlist#1#2{%
  \begingroup
  \endlinechar=-1
  \def\MyList{}
  \openin\@inputcheck=#1 %
  \loop\unless\ifeof\@inputcheck
    \read\@inputcheck to \reserveda
    \edef\MyList{\ifx\MyList\@empty\else
      \unexpanded\expandafter{\MyList}|
      \fi\unexpanded\expandafter{\reserveda}}
  \repeat
  \closein\@inputcheck
  \@expandtwoargs\in@{|#2|}{|\unexpanded\expandafter{\MyList}|}
  \expandafter\endgroup
  \ifin@\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
}
\endgroup

\begin{document}
\ifinfruitlist{fruit.tex}{apple}{in list}{not in list}
\ifinfruitlist{fruit.tex}{foo}{in list}{not in list}
\end{document

위의 솔루션은 받아들일 수 없을 정도로 비효율적입니다. 목록의 길이가 10페이지이고 관심 토큰이 목록의 첫 번째 토큰인 경우 테스트 문자열이 있는지 확인하기 전에 먼저 전체 문서를 읽어야 합니다. 보다 효율적인 구현은 다음과 같습니다.

\documentclass{article}
\begin{filecontents*}{fruit.tex}
apple
orange
pear
pineapple
\end{filecontents*}

\makeatletter
\gdef\ifinfruitlist#1#2{%
  \begingroup
  \def\reserved@b{#2}%
  \endlinechar=-1 %
  \openin\@inputcheck=#1 %
  \@tempswafalse\@testfalse
  \def\do{%
    \if@tempswa
      \closein\@inputcheck
    \else
      \ifeof\@inputcheck
        \@tempswatrue
      \else
        \read\@inputcheck to\reserved@a
        \ifx\reserved@a\reserved@b
          \@testtrue\@tempswatrue
        \fi
      \fi
      \expandafter\do
    \fi
  }%
  \do
  \expandafter\endgroup
  \if@test\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
}
\makeatother

\begin{document}
\ifinfruitlist{fruit.tex}{pear}{in list}{not in list}
\ifinfruitlist{fruit.tex}{foo}{in list}{not in list}
\end{document}

답변4

LuaTeX는 이러한 작업에 이상적으로 적합합니다. 다음은 ConTeXt의 luatex 기반 솔루션입니다.

\startbuffer[fruits]
apple
orange
pear
pineapple
\stopbuffer

%% Save the contents of fruits buffer in \jobname-fruits.tmp
\savebuffer[fruits][fruits] 

\startluacode
  local data = {}
  local find = string.find
  function commands.doiffruitelse(file, fruit)
      if not data[file] then
          data[file] = io.loaddata(file) or ""
      end
      return commands.testcase(find(data[file], "%f[%a]"..fruit.."%f[%A]") ~= nil)
  end
\stopluacode

\def\doiffruitelse#1%
    {\ctxcommand{doiffruitelse("\jobname-fruits.tmp", "#1")}}

\def\isFruit#1%
    {\doiffruitelse{#1}{1}{0}}

\starttext
\startlines
\isFruit{pear}
\isFruit{carrot}
\stoplines
\stoptext

이는 io:loaddata()from 함수를 사용 l-io.lua하여 파일의 내용, commandslua 명령의 네임스페이스 분리를 ​​위한 테이블, commands.testcasedo-if-else 기능을 제공하는 함수를 사용합니다. 실제 일치는 string.find함수를 사용하여 수행됩니다. 나는프론티어 패턴단어 경계를 일치시킵니다.

관련 정보