Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

This question is related to (but distinct from) this question: Parsing a \$ as part of an improved \getargs command, which I asked earlier.

I am experiencing a residual space in a string result from which I am trying to remove all spaces, both regular spaces and hardspaces. The routine works great when the argument contains just regular spaces, it works when the argument contains hardspaces not in the leading position. But it fails to remove a leading hardspace. If I lead the argument with multiple hardspaces, it removes all but one.

I would like, if possible, to retain this logical approach (recursive) to the problem, since it is fast and it fits into a larger algorithm being developed. I'm not sure what makes a leading hardspace different from other hardspaces, or if my coding merely is not structured to catch a leading space (I thought it was).

Here is input (note: re-edited to minimize content for MWE):

\documentclass{article}
\usepackage{ifnextok}
\usepackage{ifthen}
\makeatletter
\def\stringend{$}
\def\add@space{\protected@edef\thestring{\thestring\@sptoken}}
\newcommand\noblanksF[2][v]{%
  \def\thestring{}\expandafter\eat@Block#2\stringend%
  \if v#1\thestring\fi} 
\def\eat@Block{\IfNextToken\stringend%
  {\@gobble}%
  {\add@tostring{\eat@Block}}%
}
\def\add@tostring#1#2{%
  \ifthenelse{\equal{#2}{~}}%
    {}%
    {\if\@sptoken#2\else\protected@edef\thestring{\thestring#2}\fi}%
  #1}
\makeatother
\parindent 0in\def\bl{\rule{1ex}{1ex}}
\begin{document}
Testing noblanks: \\
\bl\noblanksF{This is a test with 0 leading spaces}\bl\\
\bl\noblanksF{ This is a test with 1 leading space}\bl\\
\bl\noblanksF{  This is a test with 2 leading spaces}\bl\\
\bl\noblanksF{  This  is  a  test  with  2  spaces  (lead+everywhere)}\bl\\
FROM HERE OUT, RESULTS LEAVE ONE LEADING SPACE:\\
\bl\noblanksF{~This is a test with 1 leading hardspace}\bl\\
\bl\noblanksF{~~This is a test with 2 leading hardspaces}\bl\\
\bl\noblanksF{~~~This is a test with 3 leading hardspaces}\bl\\
\bl\noblanksF{~~~~This is a test with 4 leading hardspaces}\bl\\
\bl\noblanksF{~~This~~is~~a~~test~~with~~2~~hardspaces~~(lead+everywhere)}\bl
\end{document}
share|improve this question
    
Update, if I change the "~" to another letter in \add@tostring, it will remove that letter successfully...including the leading T, whether or not the "T" is the in position 1 or following several hardspaces. So the problem is unique to hardspaces, rather than a case of the code logic skipping the first character. –  Steven B. Segletes Mar 7 '13 at 12:35
2  
When you pass ~ through \protected@edef it becomes \protect \nobreakspace {}. –  egreg Mar 7 '13 at 13:22
    
@egreg: That is helpful to know. I found if, in \noblanks, I performed a \protected@edef on #2 and then passed that new variable to \eat@Block, it wouldn't gobble up any of the hardspaces, consistent with your comment. So maybe the clue is to find out how to do an \ifthenelse on a \protect \nobrakspace {}... –  Steven B. Segletes Mar 7 '13 at 13:33
    
This is a good question. You can make it better by trimming down your MWE even further. You don't need two files; just take the contents of your mwe.sty and put it between \makeatletter...\makeatother in mwe.tex. (Also BTW: there is already a mwe package on CTAN). And the comments about things you deleted are distracting from the problem. See this answer to a question on meta about MWEs. –  Matthew Leingang Mar 7 '13 at 14:35
1  
@MatthewLeingang: Done. –  Steven B. Segletes Mar 7 '13 at 16:26

2 Answers 2

up vote 8 down vote accepted

Classic TeX doesn't really have a "hard space", in the sense of say U+00a0 (nbsp in html) ~ is a macro that expands to some commands that make a space and prevent linebreaking, but that isn't really quite the same thing. Since ~ is a macro the easiest way to make it go away is to define it to expand to nothing. I suggest some code below which produces

[Thisisatestwith2hardspaces(lead+everywhere)]

you should be able to combine it with code to remove .

\documentclass{article}

\makeatletter

\def\remtilde#1{{%
\let~\@empty
\protected@xdef\thestring{#1}%
\typeout{[\thestring]}}}


\remtilde{~~This~~is~~a~~test~~with~~2~~hardspaces~~(lead+everywhere)}

\stop
share|improve this answer
    
I knew this had something to do with the fact that ~ isn't a string. Very elegant solution. –  Matthew Leingang Mar 7 '13 at 14:31
    
As a final note, I used a variant of David Carlisle's solution. I did a \def~{ } at the beginning of the routine to treat ~ as a normal space, since my algortithm was just fine with detecting normal spaces. This will allow me (eventually) to get rid of some, but not necessarily all hardspaces depending on the task. Then when the task was complete, I \let~\SavedHardspace which I had previously saved. Thanks for all the ideas, everyone. –  Steven B. Segletes Mar 8 '13 at 1:56

If it is just to remove spaces and if you want to save time, why not changing their catcode just when reading the argument? No loop, no recursion and above all, no \edef and no global to store the argument in \thestring

\documentclass{article}
\usepackage[T1]{fontenc}
\makeatletter
\newcommand\noblanksF[1][v]{%
    \edef\opt@arg{\expandafter\@car\detokenize{#1}\@nil}%
    \begingroup
        \ifnum\catcode`\~=\active\catcode`\~9 \fi
        \catcode32=9
        \afterassignment\noblankF@i
        \def\thestring}

\newcommand\noblankF@i{%
        \expandafter
    \endgroup
    \expandafter\def\expandafter\thestring\expandafter{\thestring}%
    \if\string v\opt@arg\expandafter\thestring\fi}
\makeatother
\def\bl{\vrule height1ex width1ex depth0pt }
\begin{document}
\parindent 0pt
Testing noblanks: \par
\bl\noblanksF{This is a test with 0 leading spaces}\bl\par
\bl\noblanksF{ This is a test with 1 leading space}\bl\par
\bl\noblanksF{  This is a test with 2 leading spaces}\bl\par
\bl\noblanksF{  This  is  a  test  with  2  spaces  (lead+everywhere)}\bl\par
FROM HERE OUT, RESULTS LEAVE ONE LEADING SPACE:\par
\bl\noblanksF{~This is a test with 1 leading hardspace}\bl\par
\bl\noblanksF{~~This is a test with 2 leading hardspaces}\bl\par
\bl\noblanksF{~~~This is a test with 3 leading hardspaces}\bl\par
\bl\noblanksF{~~~~This is a test with 4 leading hardspaces}\bl\par
\bl\noblanksF{~~This~~is~~a~~test~~with~~hardspaces~~~and~maths$1+1=2$!}\bl\par
\catcode`\~12 % "~" become a "other" char
HERE, "~" IS A NORMAL CHAR:\par
\bl\noblanksF{~~This~~is~~a~~test~~with~NO~hardspaces~~~and~maths$1+1=2$!}\bl
\end{document}
share|improve this answer
    
I like the catcode approach and I think I can use it. But I do need to retain the loop method because, for other applications, I won't be removing all the spaces, but just selected spaces or other characters (e.g., leading, trailing, change case, etc.) My eventual goal is to replace stringstrings package with a faster method. –  Steven B. Segletes Mar 7 '13 at 19:42
    
If you want to rewrite stringstrings and if you don't copy xstring method ;) I wish you good luck. If I can suggest you something and ask you a question: why to edef the argument of your macros? Wouldn't it be much more simple if you read them without expansion? –  unbonpetit Mar 7 '13 at 19:58
    
The reason I did everything "in the clear" (via edef) in stringstrings was to allow a form of nesting (composite string manipulation). The nesting takes the form of \opA[q]{argument}\opB[q]{\thestring}\opC[v]{\thestring} where [q] is quiet and [v] is verbose. In this way the results of complex multistage manipulations can be had, which was not possible by using a def. –  Steven B. Segletes Mar 7 '13 at 20:07
    
I am pershaps missing something but a 1-expansion of \thestring in the arguments of \opB and \opC wouldn't be enough? –  unbonpetit Mar 7 '13 at 20:31
    
I am not sure I understand your comment. But I will add that, in stringstrings, the edef was essential because of the approach I took, in which strings were rotated through character by character and operated on by way of an exhaustive \if \else \if \else ... \fi \fi construct which blew up if you tried to \def it. It may be possible that using the recursive technique being explored in this question that I can go back to \def, avoiding the \edef altogether. That would be nice, but too early to tell. –  Steven B. Segletes Mar 8 '13 at 2:00

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.