6

I'm trying to follow the example given here

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\seq_new:N \l_ae_insertQuotedMatter_seq
\tl_new:N  \l_ae_insertQuotedMatter_trash_tl
\NewDocumentCommand{\removeQuotes}{m}
    {
      \seq_set_split:Nnn  \l_ae_insertQuotedMatter_seq {} {#1}
      \seq_pop_left:NN   \l_ae_insertQuotedMatter_seq \l_ae_insertQuotedMatter_trash_tl
      \seq_pop_right:NN  \l_ae_insertQuotedMatter_seq \l_ae_insertQuotedMatter_trash_tl
      \use:x  { \seq_map_function:NN \l_ae_insertQuotedMatter_seq \exp_not:n }
    }
\ExplSyntaxOff
\pagestyle{empty}
\begin{document}

\removeQuotes{"Hello World"}

\end{document}

But this does more than I want. The white space is lost.

Two provisos: (1) I would prefer not to have to write \removeQuotes{"Hello\ World"}. Also, (2) I would prefer not to have to load an entire new package (such as xstring) just to accomplish this one task.

UPDATE

I've also tried something along the lines of:

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\removeQuotes}{m}
    {
      \tl_reverse:n { \tl_tail:n { \tl_reverse:n { \tl_tail:n { #1 } } } }
    }
\ExplSyntaxOff
\pagestyle{empty}
\begin{document}

\removeQuotes{"Hello World"}

\removeQuotes{"Hello\ World"}

\end{document}

But I get the error:

! Argument of \tl_tail:n has an extra }.
<inserted text> 
                \par 
l.12     \removeQuotes{"Hello World"}

? 

Any suggestions?

A.Ellett
  • 50,533

3 Answers3

7

Your second approach can be solved by generating a variant of the used commands:

\ExplSyntaxOn
\cs_generate_variant:Nn \tl_reverse:n { f }
\cs_generate_variant:Nn \tl_tail:n { f }
\NewDocumentCommand{\removeQuotes}{m}
    {
      \tl_reverse:f { \tl_tail:f { \tl_reverse:f { \tl_tail:f { #1 } } } }
    }
\ExplSyntaxOff

There are also another approach:

\ExplSyntaxOn
\NewDocumentCommand{\removeQuotes}{m}
    {
     \tl_set:Nn \tl_tmpa_tl { #1 } 
     \tl_remove_all:Nn \tl_tmpa_tl { " }
     \tl_use:N \tl_tmpa_tl
    }
\ExplSyntaxOff

Heres the approach suggest by cgnieder:

\ExplSyntaxOn
\NewDocumentCommand{\removeQuotes}{m}
    {
     \__ellett_remove_quotes_aux:n { #1 }
    }
\cs_set:Npn \__ellett_remove_quotes_aux:n #1
 {
   \__ellett_remove_quotes_aux_i:w #1 \q_stop
 }
\cs_new:Npn \__ellett_remove_quotes_aux_i:w  "#1" \q_stop
 {
  #1
 }
\ExplSyntaxOff
Marco Daniel
  • 95,681
6

The reason that the seq function behaves as-observed is that it is designed to strip out spaces 'around' items, and for an item which consists only of spaces that means removing it entirely. Thus an alternative approach is required.

While the token list functions can do this, I would probably use a more traditional TeX approach based around delimited arguments. The question doesn't specify behaviour if the first and last tokens are not quotes or if quotes are nested within the text. Thus the following may not be 'desired' in all cases, but is a start:

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\removeQuotes}{m}
    { \ae_remove_quotes:n {#1} }
\cs_new:Npn \ae_remove_quotes:n #1
  { \__ae_remove_quotes:w #1 " " \q_stop }
\cs_new:Npn \__ae_remove_quotes:w #1 " #2 " #3 \q_stop
  {
    \tl_if_empty:nTF {#1}
      {#2}
      {#1}
  }
\ExplSyntaxOff
\pagestyle{empty}
\begin{document}

\removeQuotes{"Hello World"}

\removeQuotes{"Hello\ World"}

\end{document}
Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036
  • I'm confused about how you defined the third function. Yours is so much more elaborate than @MarcoDaniel 's. What's going here? – A.Ellett Jul 06 '13 at 18:12
  • Similar to what I asked @MarcoDaniel. Why the middle function? Why not just define \removeQuotes through a direct call to \__ae_remove_quotes:w? – A.Ellett Jul 06 '13 at 18:17
  • @A.Ellett The LaTeX3 approach is that document commands such as \removeQuotes should call 'well-behaved' code level commands. Something like \__ae_remove_quotes:w is not 'well-behaved' as it needs a particular set of tokens in its input (at least two " and a trailing \q_stop), so I've wrapped it up inside a function which does include that. – Joseph Wright Jul 06 '13 at 18:20
  • I see what's going on with your third function now. Also, thank you for the explanation about the LaTeX3 approach: it explains something that I've seen in other's code and where I haven't understood the call to an apparently superfluous function. – A.Ellett Jul 06 '13 at 18:32
5

The mandatory solution with l3regex. Unquoted material passes through unchanged.

\documentclass{article}
\usepackage{xparse,l3regex}
\ExplSyntaxOn
\NewDocumentCommand{\removeQuotes}{m}
 {
  \ae_remove_quotes:n { #1 }
 }
\tl_new:N \l__ae_string_temp_tl

\cs_new_protected:Npn \ae_remove_quotes:n #1
 {
  \tl_set:Nn \l__ae_string_temp_tl { #1 }
  \regex_replace_once:nnN { \A \" (.*) \" \Z } { \1 } \l__ae_string_temp_tl
  \tl_use:N \l__ae_string_temp_tl
 }
\ExplSyntaxOff

\pagestyle{empty}
\begin{document}

\removeQuotes{"Hello World"}

\removeQuotes{"Hello\ World"}

\removeQuotes{Hello World}

\end{document}

enter image description here

egreg
  • 1,121,712