I want find a certain line of text in a data file cities.txt:
A;London
B;Berlin
C;Paris
with the xstring
and [xifthen
][2] packages:
\documentclass[12pt, a4paper]{article}
\usepackage{xstring}
\usepackage{xifthen}
\begin{document}
\newread\dbcities
\openin\dbcities=cities.txt
We start here:
\loop\unless\ifeof\dbcities
\read\dbcities to \dbline
\ifthenelse{\isin{A}{\dbline}} % <-- doesn't work
{\StrCut{\dbline}{;}{\colA}{\colB}
\colA: \colB \\}
{}
\repeat
\closein\dbcities
\end{document}
But the string comparison doesn't work if the line of text to be compared is stored in the macro variable \dbline
, although if I print \dbline
it expands perfectly. That makes me crazy … Any help?
Edit: Based on jfbu's answer (thanks!) I tried to put the search code into a command and pass the searchstring by Argument. Seems it works!
Here is how I use the function now. The extra whitespace is a problem (see discussion)
% <-*- coding: utf-8 -*-
% ---------------------------------------------------
\documentclass[a4paper,12pt]{article}
\usepackage{filecontents}
\begin{filecontents*}{verses.dat}
§
§1§~{Mich respektvoll verneigend vor den Sugatas, vor dem Körper der Wahrheit, den sie besitzen, sowie vor ihren Kindern und ebenso vor allen, die der Verehrung wert sind, will ich die Ausübung der Disziplin der Sugata-Kinder entsprechend den Schriften in zusammengefasster Form darlegen.}
§
§2§~{Etwas, das es zuvor noch nicht gegeben hat, habe auch ich hier nicht zu sagen; und ich besitze nicht die Kunstfertigkeit in der Komposition von Schriften: Deshalb habe ich auch keine Absicht, anderen zu nutzen; vielmehr schreibe ich dieses, um meinen eigenen Geist daran zu gewöhnen.}
§
§3§~{Aufgrund der Gewöhnung an das Heilsame mag die Kraft meines Vertrauens durch diese Kontemplationen zeitweilig anwachsen; und wenn andere, die mir in ihren Veranlagungen ähnlich sind, diese sehen, so mag es auch für sie von Bedeutung sein.}
§
\end{filecontents*}
\usepackage{fontspec}
\defaultfontfeatures{Mapping=tex-text} %
\usepackage{polyglossia} % the multilingual support package
\setdefaultlanguage[spelling=new, babelshorthands=true]{german}
% ------ Routine für Einfügung Grundtext: ---------
% !!roots-db.txt dar keine Leerzeile am Ende haben!!
\usepackage{xstring}
\usepackage{xifthen}
\newcommand*{\isinxp}[2]{\expandafter\isinxpp\expandafter{#2}{#1}}
\newcommand*{\isinxpp}[2]{\isin {#2}{#1}}
\newread\dbroot
\newcommand{\myprintverse}[1]{%
\openin\dbroot=verses.dat
\loop
\read\dbroot to \dbline
\unless\ifeof\dbroot
\ifthenelse{\isinxp{#1}{\dbline}}
{\StrCut{\dbline}{~}{\colA}{\colB}
(\StrBetween[1,2]{\colA}{§}{§})~{\colB}}
{}
\repeat
\closein\dbroot%
}
\begin{document}
Each line of data printed in a seperate paragraph:
\medskip{}
\myprintverse{§1§} \par
\myprintverse{§2§} \par
\myprintverse{§3§} \par
\bigskip{}
Two lines in one paragraph results in ugly whitespace in between, and I don't know how to get rid of it:
\medskip{}
\myprintverse{§1§} \myprintverse{§2§} \par
\myprintverse{§3§}\footnote{This is also a problem if I want to add a footnote.} \par
\end{document}
Edit: This is now the flawless version of the command that works without adding space:
\newcommand{\myprintverse}[1]{%
\openin\dbroot=verses.dat
\begingroup
\loop
\endlinechar=-1
\read\dbroot to \dbline
\unless\ifeof\dbroot
\ifthenelse{\isinxp{#1}{\dbline}}
{\StrCut{\dbline}{~}{\colA}{\colB}
(\StrBetween[1,2]{\colA}{§}{§})~{\colB}}%
{}%
\repeat
\endgroup
\closein\dbroot
}
Edit: Now I will pass the filename by argument, and it works perfectly for me:
\documentclass[a4paper,12pt]{article}
\usepackage{filecontents}
\begin{filecontents*}{verses.dat}
§
§1§~{Mich respektvoll verneigend vor den Sugatas, vor dem Körper der Wahrheit, den sie besitzen, sowie vor ihren Kindern und ebenso vor allen, die der Verehrung wert sind, will ich die Ausübung der Disziplin der Sugata-Kinder entsprechend den Schriften in zusammengefasster Form darlegen.}
§
§2§~{Etwas, das es zuvor noch nicht gegeben hat, habe auch ich hier nicht zu sagen; und ich besitze nicht die Kunstfertigkeit in der Komposition von Schriften: Deshalb habe ich auch keine Absicht, anderen zu nutzen; vielmehr schreibe ich dieses, um meinen eigenen Geist daran zu gewöhnen.}
§
§3§~{Aufgrund der Gewöhnung an das Heilsame mag die Kraft meines Vertrauens durch diese Kontemplationen zeitweilig anwachsen; und wenn andere, die mir in ihren Veranlagungen ähnlich sind, diese sehen, so mag es auch für sie von Bedeutung sein.}
§
\end{filecontents*}
\usepackage{fontspec}
\defaultfontfeatures{Mapping=tex-text} %
\usepackage{polyglossia}
\setdefaultlanguage[spelling=new, babelshorthands=true]{german}
\usepackage{xstring}
\usepackage{xifthen}
\newcommand*{\isinxp}[2]{\expandafter\isinxpp\expandafter{#2}{#1}}
\newcommand*{\isinxpp}[2]{\isin {#2}{#1}}
\newread\dbroot
\newcommand{\myprintverse}[2]{%
\openin\dbroot=#1
\begingroup
\loop
\endlinechar=-1
\read\dbroot to \dbline
\unless\ifeof\dbroot
\ifthenelse{\isinxp{#2}{\dbline}}
{\StrCut{\dbline}{~}{\colA}{\colB}
(\StrBetween[1,2]{\colA}{§}{§})~{\colB}}%
{}%
\repeat
\endgroup
\closein\dbroot
}
\begin{document}
\myprintverse{verses.dat}{§1§} \par
\end{document}
Edit / Question: The above version works great unless I enter some LaTeX-Code, e.g. if I want to emphasize text:
\documentclass[a4paper,12pt]{article}
\usepackage{filecontents}
\begin{filecontents*}{verses.dat}
§
§1§~{Mich respektvoll verneigend vor den \emph{Sugatas}, vor dem Körper der Wahrheit, den sie besitzen, sowie vor ihren Kindern und ebenso vor allen, die der Verehrung wert sind, will ich die Ausübung der Disziplin der Sugata-Kinder entsprechend den Schriften in zusammengefasster Form darlegen.}
§
\end{filecontents*}
\usepackage{fontspec}
\defaultfontfeatures{Mapping=tex-text} %
\usepackage{polyglossia}
\setdefaultlanguage[spelling=new, babelshorthands=true]{german}
\usepackage{xstring}
\usepackage{xifthen}
\newcommand*{\isinxp}[2]{\expandafter\isinxpp\expandafter{#2}{#1}}
\newcommand*{\isinxpp}[2]{\isin {#2}{#1}}
\newread\dbroot
\newcommand{\myprintverse}[2]{%
\openin\dbroot=#1
\begingroup
\loop
\endlinechar=-1
\read\dbroot to \dbline
\unless\ifeof\dbroot
\ifthenelse{\isinxp{#2}{\dbline}}
{\StrCut{\dbline}{~}{\colA}{\colB}
(\StrBetween[1,2]{\colA}{§}{§})~{\colB}}%
{}%
\repeat
\endgroup
\closein\dbroot
}
\begin{document}
\myprintverse{verses.dat}{§1§} \par
\end{document}
Compilation stops with an error:
! Use of \@xs@StrCut@@ doesn't match its definition.
\text@command #1->\def \reserved@a {#1
}\ifx \reserved@a \@empty \let \check@...
l.43 \myprintverse{verses.dat}{§1§}
Any idea how this could be solved?
Best Answer
update: the mandatory xinttools approach has been added
There are two problems. The first one is that
\isin
from packagexifthen
does not expand its second argument, here\dbline
. The second problem is that\isin
uses the LaTeX kernel\in@
which can not handle a\par
token; but TeX will always append a\par
token at the end of file input via\read
, except if \endlinechar is set to -1.The first problem can be handled by defining a wrapper
\isinxp
which expands its second argument. The second problem (assuming\endlinechar
is not -1) by testing the end of file after having read the line and before the test.The mandatory xinttools approach:
(I have corrected the key matching, as it stood a search for
BB
would have returned a false positive for a data line withB
as key. Notice that the fact that a data line containingBB
as key returns positive when looking forB
is the expected thing, as the OP used\isin
.)