Leerzeichen um Text herum entfernen (wie LTRIM, RTRIM und TRIM)

Leerzeichen um Text herum entfernen (wie LTRIM, RTRIM und TRIM)

Wenn Sie in Excel mit Textzeichenfolgen arbeiten, ist es praktisch, denLTRIM-, RTRIM- und TRIM-Funktionenwodurch Leerzeichen um Textzeichenfolgen entfernt werden. Wie könnte man dies effizient in LaTeX duplizieren?

Angenommen, Sie erzeugen programmgesteuert Variablen

\def\firstname{FirstName}% First name
\def\lastname{LastName}% Last name
\edef\fullname{\firstname\ \lastname}% Full name
\fullname% Display full name

sollte aber auch berücksichtigen, wenn\firstname oder \lastnamekönnen leer sein. Ohne zu testen, ob sie leer sind, so etwas wie

\def\firstname{FirstName}% First name
\def\lastname{}% Last name (none)
\edef\fullname{\firstname\ \lastname}% Full name
\trim{\fullname}% Display full name

würde mich darum kümmern. Mein erster Gedanke war, zu definieren

\def\trim#1{\ignorespaces#1\unskip}

aber das würde in einer allgemeinen Einstellung mit Sicherheit nicht funktionieren, da eine leere Gruppe dabei nicht berücksichtigt wird. Außerdem \unskipwürde nur der letzte Sprung berücksichtigt, von dem es mehr als einen geben kann.

Ist es insbesondere möglich, eine Definition zu erstellen, \trimdie sich um

  • \hspaces und \hskips?
  • \'S?
  • leere Gruppen {}und vielleicht nicht druckbare Token wie \relax?

Bildbeschreibung hier eingeben

\documentclass{article}
\begin{document}
Without \verb|\trim|:\par\medskip
\def\firstname{FirstName}\def\lastname{LastName}
\edef\fullname{\firstname\ \lastname}\fbox{\fullname}

\def\firstname{FirstName}\def\lastname{}
\edef\fullname{\firstname\ \lastname}\fbox{\fullname}

\def\firstname{}\def\lastname{LastName}
\edef\fullname{\firstname\ \lastname}\fbox{\fullname}

\bigskip

With \verb|\trim|:\par\medskip
\def\trim#1{\ignorespaces#1\unskip}
\def\firstname{FirstName}\def\lastname{LastName}
\edef\fullname{\firstname\ \lastname}\fbox{\trim{\fullname}}

\def\firstname{FirstName}\def\lastname{}
\edef\fullname{\firstname\ \lastname}\fbox{\trim{\fullname}}

\def\firstname{}\def\lastname{LastName}
\edef\fullname{\firstname\ \lastname}\fbox{\trim{\fullname}}

\end{document}

Antwort1

Das Entfernen aller expliziten Leerzeichen um die Eingabe ist sicherlich machbar. Es gibt mehrere Ansätze für dieses Problem: Ich würde den Ansatz wählen, den Bruno Le Floch für expl3as geschrieben hat \tl_trim_spaces:n. Dies kann verwendet werden, indem

\usepackage{expl3}
\ExplSyntaxOn
\cs_new_eq:NN \trimspaces \tl_trim_spaces:n
\ExplSyntaxOff

Alternativ kann die Implementierung auch direkt in den Quellcode eingebunden und so jegliche Abhängigkeit vermieden werden:

\documentclass{article}
\makeatletter
\long\def\trim@spaces#1{%
  \@@trim@spaces{\q@mark#1}%
}
\def\@tempa#1{%
  \long\def\@@trim@spaces##1{%
    \@@trim@spaces@i##1\q@nil\q@mark#1{}\q@mark
      \@@trim@spaces@ii
      \@@trim@spaces@iii
      #1\q@nil
      \@@trim@spaces@iv
      \q@stop
  }%
  \long\def\@@trim@spaces@i##1\q@mark#1##2\q@mark##3{%
    ##3%
    \@@trim@spaces@i
    \q@mark
    ##2%
    \q@mark#1{##1}%
  }%
  \long\def\@@trim@spaces@ii\@@trim@spaces@i\q@mark\q@mark##1{%
    \@@trim@spaces@iii
    ##1%
  }%
  \long\def\@@trim@spaces@iii##1#1\q@nil##2{%
    ##2%
    ##1\q@nil
    \@@trim@spaces@iii
  }%
  \long\def\@@trim@spaces@iv##1\q@nil##2\q@stop{%
    \unexpanded\expandafter{\@gobble##1}%
  }%  
}
\@tempa{ }
\def\test{ foo }
\edef\test{\expandafter\trim@spaces\expandafter{\test}}
\show\test

Dadurch werden alle Leerzeichen am Ende der Eingabe entfernt, selbst wenn Sie etwas Schwieriges tun, wie z. B. \edef\test{ \space foo \space}mit zu beginnen (so dass an beiden Enden mehrere Leerzeichen vorhanden sind). (Wenn Sie sich auf diesen Fall beschränken möchten, dannxparsebietet den \TrimSpacesPostprozessor für Argumente mit dieser Methode an.)

Das obige funktioniert so, dass es zwei Schleifen gibt: eine für Leerzeichen am Anfang der Eingabe ( \@@trim@spaces@i), eine zweite für diejenigen am Ende ( \@@trim@spaces@iii). Zuerst \@@trim@spaceswird alles so eingerichtet, dass die richtigen Markierungen vorhanden sind. Im „führenden“ Schritt \@@trim@spaces@ientspricht einem Argument, das aus \q@markgefolgt von einem Leerzeichen besteht (das Leerzeichen selbst wird verworfen). Wenn mehr Leerzeichen vorhanden sind, dann sind #1und leer und sind die verbleibende Eingabe, was bedeutet, dass mit der verbleibenden Eingabe erneut aufgerufen wird. Wenn andererseits keine Leerzeichen mehr in der Eingabe vorhanden sind, dann entspricht der leeren Eingabe, die durch eingerichtet wurde , ist die Benutzereingabe, bei der alle führenden Leerzeichen entfernt wurden und ist . Letzteres stoppt die Schleife und übergibt an (ein bleibt vor der Benutzereingabe, um den Verlust von Klammern zu verhindern: siehe später). In dieser zweiten Schleife erscheinen und Leerzeichen am Ende der Eingabe direkt vor . Dieses Muster wird durch das Argument mit abgeglichen . Wenn die Eingabe ein nachstehendes Leerzeichen enthielt, dann ist die Benutzereingabe, bei der das Leerzeichen entfernt wurde (aber immer noch mit einem führenden ) und ist , was zu einer Schleife führt. Wenn jedoch alle nachstehenden Leerzeichen erschöpft sind, ist und ist das . Das wird durch das Argumentmuster für entfernt, bevor das führende durch entfernt wird (wobei das weitere Erweitern verhindert).#3#2\@@trim@spaces@i#2\@@trim@spaces#1#3\@@trim@spaces@ii\@@trim@spaces@iii\q@mark\q@nil\@@trim@spaces@iii#1\q@mark#2\@@trim@spaces@iii#2\@@trim@spaces@iv#1\q@mark <user input>\q@nil\@@trim@spaces@iii\q@nil\@@trim@spaces@iii\@@trim@spaces@iv\q@amrk\@gobble\unexpanded

Beachten Sie, dass oben e-TeX verwendet wird, um weitere Erweiterungen innerhalb von an oder ähnlichem zu verhindern \edef. Wenn die Erweiterungen nicht verfügbar sind, ändern Sie das letzte Hilfsverb in

  \long\def\@@trim@spaces@iv##1\q@nil##2\q@stop{%
    \@gobble##1%
  }%

mit der Maßgabe, dass dies bedeutet, dass Sie vorsichtig sein müssen, was durchgeleitet wird.

Zweitens ist zu beachten, dass es im obigen Beispiel einige „spezielle“ Token gibt \q@nil, die verwendet werden, um die Makroargumentmuster abzugleichen, und daher nicht in der Eingabe enthalten sein können. Das sollte mit „Text“ eigentlich in Ordnung sein, aber Sie könnten etwas noch Obskureres wie \catcode`\Q=3then Q(math shift catcode) verwenden, wenn Sie möchten.

Das Entfernen der anderen angeforderten Elemente würde bedeuten, nach allen einzeln zu suchen. Das klingt im Fall von \hspace/ ziemlich schwierig \hskip, da der Abstand vermutlich in allen gültigen Einheiten angegeben werden könnte, noch bevor wir uns um Dinge wie

\def\foo{10 pt }
\hskip\foo

Wie Sie vielleicht wissen, ist der Umgang mit Gruppentokens schon im besten Fall schwierig, daher kann es auch schwierig sein, eine leere Gruppe zu finden. (Ich schätze, Sie müssten eine Schleife verwenden: Greifen Sie jedes Token in der Eingabe, prüfen Sie, ob es leer ist, und fügen Sie es andernfalls dem „Behalten“-Stapel hinzu.)

Darüber hinaus halte ich diese Art von Eingabe bei realen Eingaben für ziemlich unwahrscheinlich. Das Kürzen expliziter Leerzeichen ist sinnvoll, aber von den anderen Elementen bin ich nicht überzeugt (es sei denn, es gibt hier einen besonderen Fall, in dem eine gute Chance besteht, die anderen Elemente zu erfassen).

Antwort2

Ich rate Ihnen unbedingt dazu, in praktischen Anwendungsfällen die Antwort von Joseph zu verwenden, auch wenn hierdurch nur explizite Leerzeichen und keine Dinge wie oder entfernt werden \hskip.

Das Abschneiden solcher Leerzeichen von rechts ist (bis zu einem gewissen Grad) unkompliziert: \unskip, dann wiederholen, wenn der Wert \lastskipungleich Null ist. Dies kann jedoch ausgetrickst werden, wenn ein Sprung der Größe auftritt 0pt.

Trimmen \hspaceund Co. von links, auch innerhalb von Makros, zwingt uns, alle Makroerweiterungen manuell durchzuführen. Noch schlimmer: Da \hspaceverwendet wird \@ifnextchar, müssen wir auch Zuweisungen durchführen. Siehe Code unten.

Beachten Sie, dass \hspace*die Grundelemente von TeX \vruleund verwendet werden, \penaltyfür die ich keine Unterstützung implementiert habe. Sie stoppen sowohl \trimleftals auch \trimright. Ich weiß, wie man das für beheben kann \trimleft(mit hohen Kosten), aber nicht für \trimright, da TeX kein hat \lastrule. LuaTeX könnte helfen.

\begingroup
  %
  % This plain TeX code uses the prefix "tsp", and defines
  % \trim, \trimleft, and \trimright.
  %
  \catcode`@=11
  \long\gdef\trim#1{\trimleft{\trimright{#1}}}
  %
  % Trimming spaces on the right is done by repeatedly calling \unskip
  % until \lastskip is zero.  We start with \hskip0pt\relax to stop
  % \trimright from trimming spaces _before_ #1 in case this only
  % contains spaces.
  %
  \long\gdef\trimright#1{\hskip0pt\relax #1\tsp@right}
  \gdef\tsp@right
    {\unskip\ifdim0pt=\lastskip\else\expandafter\tsp@right\fi}
  %
  % Trimming spaces on the left is done by repeatedly using \futurelet
  % to test the first token, and dispatching depending on what is found.
  % Expandable tokens are expanded; most assignments are performed;
  % spaces are ignored; groups are entered.  The loop ends when
  % encountering \tsp@left@end.
  %
  \long\gdef\trimleft#1{\tsp@left#1\tsp@left@end}
  \global\let\tsp@left@end\relax
  \gdef\tsp@left{\expandafter\tsp@left@look}
  \gdef\tsp@left@look{\futurelet\tsp@token\tsp@left@test}
  \gdef\tsp@left@test
    {%
      \typeout{\meaning\tsp@token}%
      \expandafter\ifx\noexpand\tsp@token\tsp@token
        \expandafter\@secondoftwo
      \else
        \expandafter\@firstoftwo
      \fi
      {% Expandable token => expand again.
        \let\tsp@next\tsp@left
      }%
      {%
        \ifcat\tsp@token\relax
          % Non-expandable primitive: build \tsp@<meaning>.
          % Note that primitives for which I haven't defined
          % \tsp@<meaning> just give \relax, which stops
          % trimming cleanly.
          \begingroup
            \escapechar-1%
            \global\expandafter\let\expandafter\tsp@next
              \csname tsp@\meaning\tsp@token\endcsname
          \endgroup
        \else
          % Character token.
          \ifcat\tsp@token\bgroup % Begin-group: do; continue trimming
            \bgroup\let\tsp@next\tsp@gobble@token
          \else
            \ifcat\tsp@token\egroup % End-group: do; continue trimming
              \egroup\let\tsp@next\tsp@gobble@token
            \else
              \ifcat\tsp@token\space % Space: remove; continue trimming
                \let\tsp@next\tsp@gobble@token
              \else % Anything else: stop trimming
                \let\tsp@next\relax
              \fi
            \fi
          \fi
        \fi
      }%
      \tsp@next
    }%
  \gdef\tsp@gobble@token{\afterassignment\tsp@left\let\tsp@token= }
  %
  % Helpers for defining primitives.
  %
  \long\gdef\tsp@swap#1{#1\tsp@gobble@token}
  \gdef\tsp@assignment{\afterassignment\tsp@left}
  %
  % Various primitives
  %
  \global \let \tsp@unskip     \tsp@gobble@token
  \global \expandafter \let \csname tsp@ \endcsname \tsp@gobble@token
  \global \let \tsp@begingroup \tsp@swap
  \global \let \tsp@endgroup   \tsp@swap
  \global \let \tsp@def        \tsp@assignment
  \global \let \tsp@edef       \tsp@assignment
  \global \let \tsp@gdef       \tsp@assignment
  \global \let \tsp@xdef       \tsp@assignment
  \global \let \tsp@let        \tsp@assignment
  \global \let \tsp@futurelet  \tsp@assignment
  \global \let \tsp@global     \tsp@assignment
  \global \let \tsp@long       \tsp@assignment
  \global \let \tsp@protected  \tsp@assignment
  \gdef\tsp@hskip#1{\begingroup\afterassignment\tsp@hskip@\skip0= }
  \gdef\tsp@hskip@{\endgroup\tsp@left}
  %
  % We must end when seeing \tsp@left@end (normally \relax)
  %
  \long\gdef\tsp@relax#1%
    {%
      \begingroup
        \def\tsp@left@end{\tsp@left@end}%
        \expandafter
      \endgroup
      \ifx#1\tsp@left@end
      \else
        \expandafter\tsp@left
      \fi
    }
\endgroup

\documentclass{article}
\begin{document}
Without \verb|\trim|:\par\medskip
\def\firstname{FirstName}\def\lastname{LastName}
\edef\fullname{\firstname\ \lastname}\fbox{\fullname}

\def\firstname{FirstName}\def\lastname{}
\edef\fullname{\firstname\ \lastname}\fbox{\fullname}

\def\firstname{}\def\lastname{LastName}
\edef\fullname{\firstname\ \lastname}\fbox{\fullname}

\bigskip

With \verb|\trim|:\par\medskip
\def\firstname{FirstName}\def\lastname{LastName}
\edef\fullname{\firstname\ \lastname}\fbox{\trim{\fullname}}

\def\firstname{FirstName}\def\lastname{}
\edef\fullname{\firstname\ \lastname}\fbox{\trim{\fullname}}

\def\firstname{}\def\lastname{LastName}
\edef\fullname{\firstname\ \lastname}\fbox{\trim{\fullname}}

\end{document}

verwandte Informationen