BookmarkSubscribeRSS Feed
acordes
Rhodochrosite | Level 12

I always struggle with the macro quoting.

 

Why does this work and the following modification does not???

 


%let kept=Arne Bert Hansi;

%macro quote_names(name_list);
    %local i name;
	%GLOBAL quoted_names;
    %let quoted_names = ;
    %let i = 1;
    %do %while(%scan(&name_list, &i) ne );
        %let name = %scan(&name_list, &i);
        %let quoted_names = &quoted_names %str(%')&name%str(%')%str(, );
        %let i = %eval(&i + 1);
    %end;
    %let quoted_names = %substr(&quoted_names, 1, %length(&quoted_names) - 2);

%mend quote_names;

%quote_names(%str(&kept));

%put %STR(&quoted_names);

'Arne', 'Bert', 'Hansi'

 

But if I want to leave only the spaces without the comma, then it fails stating 

 

ERROR: Literal contains unmatched quote.
ERROR: The macro QUOTE_NAMES will stop executing.

 

%let kept=Arne Bert Hansi;

%macro quote_names(name_list);
    %local i name;
	%GLOBAL quoted_names;
    %let quoted_names = ;
    %let i = 1;
    %do %while(%scan(&name_list, &i) ne );
        %let name = %scan(&name_list, &i);
        %let quoted_names = &quoted_names %str(%')&name%str(%')%str( );
        %let i = %eval(&i + 1);
    %end;
    %let quoted_names = %substr(&quoted_names, 1, %length(&quoted_names) - 2);

%mend quote_names;

%quote_names(%str(&kept));

%put %STR(&quoted_names);

 

 

15 REPLIES 15
PaigeMiller
Diamond | Level 26

You might want to use the %QLIST macro, which is much easier than writing your own macro. I have used %QLIST for years and it works as desired.

 

Your particular problem can be solved by using %UNQUOTE

 

    %let quoted_names = %unquote(%substr(&quoted_names, 1, %length(&quoted_names) - 2));

 

Why does %UNQUOTE work here? I have trouble explaining it other than to say when it looks like you have coded everything properly and you still get errors, and it usually involves a case where you created a quoted string in the macro, try %UNQUOTE. Or perhaps Mr. @Astounding can explain, you can search for his explanation of this in previous threads, or buy his book (the name of which I have forgotten) which also explains.

--
Paige Miller
Astounding
PROC Star

@PaigeMiller , thanks for the mention.  Unfortunately the book "Macro Language Magic" is now out of print, and I have been retired and without SAS for more than 5 years now.

Off the top of my head, the most common "mysterious" need for %unquote is when macro language creates a list of quoted items for use in a WHERE clause within SQL code.  Many times the software figures out to unquote a list, but not when SQL is involved.  

FreelanceReinh
Jade | Level 19

Hello @acordes,

 


@acordes wrote:

Why does this work and the following modification does not???

This is because you left the final %LET statement in the macro, which had the purpose of deleting the unwanted %str(, ) after the last word, but which in the modified macro deletes the unwanted trailing blank plus the important closing quotation mark.

 

Solution: Delete that final %LET statement entirely and also the trailing %str( ) earlier in the code. (Note that you already get the separating blanks by inserting them before the newly appended name -- and the %LET statement automatically avoids creating a leading blank before the first name.)

yabwon
Onyx | Level 15

do you need macro for the process?

how about data step?

 

%let kept=Arne Bert Hansi;

data _null_;
length str result $ 32767 word $ 128;
  str=symget('kept');
  sep = " "; /* space, but you can set whatever you like */

  do i=1 by 1 while(NOT quit);
    word = scan(str, i, " ");
    if word NE " " then result = catX(sep, result, quote(strip(word)) );
                   else quit=1;
  end;
  call symputX("quoted_names", result, "G");
run;

%put &quoted_names.;

 

Bart

_______________
Polish SAS Users Group: www.polsug.com and communities.sas.com/polsug

"SAS Packages: the way to share" at SGF2020 Proceedings (the latest version), GitHub Repository, and YouTube Video.
Hands-on-Workshop: "Share your code with SAS Packages"
"My First SAS Package: A How-To" at SGF2021 Proceedings

SAS Ballot Ideas: one: SPF in SAS, two, and three
SAS Documentation



yabwon
Onyx | Level 15

O course DoSubL() function can help in wrapping it into a macro:

%let kept=Arne Bert Hansi;

%macro addQuotes(kept);
%local rc quoted_names;
%let rc=%sysfunc(dosubl(%str(
  data _null_;
    length str result $ 32767 word $ 128;
    str=symget("kept");
    sep = " "; /* space */

    do i=1 by 1 while(NOT quit);
      word = scan(str, i, " ");
      if word NE " " then result = catX(sep, result, quote(strip(word)) );
                     else quit=1;
    end;
    call symputX("quoted_names", result, "L");
  run;
)));
&quoted_names.
%mend addQuotes;

%put %addQuotes(&kept.);

Bart

_______________
Polish SAS Users Group: www.polsug.com and communities.sas.com/polsug

"SAS Packages: the way to share" at SGF2020 Proceedings (the latest version), GitHub Repository, and YouTube Video.
Hands-on-Workshop: "Share your code with SAS Packages"
"My First SAS Package: A How-To" at SGF2021 Proceedings

SAS Ballot Ideas: one: SPF in SAS, two, and three
SAS Documentation



Tom
Super User Tom
Super User

I find it usually works better to only add the separator before the next word.

Also an iterative DO loop are much,much easier to code and understand than a DO WHILE loop where you have to manually increment the counter.

%macro quote_names(name_list);
  %local i name sep;
  %GLOBAL quoted_names;
  %let quoted_names = ;
  %let sep=;
  %do i=1 %to %sysfunc(countw(&name_list,%str( )));
    %let name = %scan(&name_list, &i,%str( ));
    %let quoted_names = &quoted_names.&sep.%str(%')&name%str(%');
    %let sep = %str(, );
  %end;
  %let quoted_names = %unquote(&quoted_names);
%mend quote_names;

Let's try it out.

14   %quote_names(a b);
15   %put |&quoted_names|;
|'a', 'b'|
16   %quote_names( );
17   %put |&quoted_names|;
||

 

Stu_SAS
SAS Employee

Hey @acordes! I've got a macro and a function called cquote that was greatly inspired by @Tom's work and it has served me well over the years:

https://github.com/stu-code/sas/blob/master/utility-macros/cquote.sas

 

%macro cquote(strlist, quote);
    %if(%upcase(&quote) = SINGLE) %then %let q = %str(%');
        %else %let q = %str(%");

    %unquote(%bquote(&q)%qsysfunc(tranwrd(%qsysfunc(compbl(%superq(strlist))),%bquote( ),%bquote(&q,&q)))%bquote(&q))
%mend;
proc fcmp outlib=work.funcs.str;

    /* Double quote version */
    function cquote(str$) $200;
        return (cats('"', tranwrd(compbl(str),' ','","'), '"'));
    endfunc;

    /* Single quote version */ 
    function scquote(str$) $200;
        return (cats("'", tranwrd(compbl(str),' ',"','"), "'"));
    endfunc;
run;

You can give these a try as well.

yabwon
Onyx | Level 15

One more option just for fun, with FCMP function called in %SYSFUNC()

proc fcmp outlib=work.f.p;
function addQuotes(str $,s $, q) $ 32767;
    length result $ 32767 sep word Usep $ 128 qt $ 1;
    sep = s;
    Usep = upcase(sep); 

    select;
      when (Usep=" ")  sep=" ";
      when (Usep="C")  sep=",";
      when (Usep="SC") sep=";";
      when (Usep="P")  sep=".";
      when (Usep=:"S") sep=substr(sep,2); /* string up to 127 bytes */
      otherwise sep = " ";
    end;

    select(q);
      when(2)   qt='"';
      when(1)   qt="'";
      otherwise qt=" ";
    end;

    quit=0;
    do i = 1 by 1 while(NOT quit);
      word = scan(str, i, " ");
      if NOT missing(word) then 
        do;
          if NOT (qt=" ") then word=quote(strip(word),qt);
          result = catX(ifc(sep=" "," ",strip(sep)), result, strip(word));
        end;
      else quit=1;
    end;
    return(result);
endfunc;
quit;

options cmplib=work.f;


%let kept=Arne Bert Hansi;

%put %sysfunc(addQuotes(&kept.,C,2));
%put %sysfunc(addQuotes(&kept.,S!,0));

data _null_;
  kept="Arne Bert Hansi";
  str=addQuotes(kept,"C",2);
  put kept= / str=;

  str=addQuotes(kept,"SC",0);
  put kept= / str=;
run;

 

Bart

_______________
Polish SAS Users Group: www.polsug.com and communities.sas.com/polsug

"SAS Packages: the way to share" at SGF2020 Proceedings (the latest version), GitHub Repository, and YouTube Video.
Hands-on-Workshop: "Share your code with SAS Packages"
"My First SAS Package: A How-To" at SGF2021 Proceedings

SAS Ballot Ideas: one: SPF in SAS, two, and three
SAS Documentation



Quentin
Super User

Yet another approach: see Richard DeVenezia's %seplist() macro.  It takes a list of items, and allows you to add quotes, or delimiters, or prefixes/suffixes, and more.

https://www.devenezia.com/downloads/sas/macros/index.php?m=seplist

 

The Boston Area SAS Users Group (BASUG) is hosting an in person Meeting & Training on June 27!
Full details and registration info at https://www.basug.org/events.
quickbluefish
Barite | Level 11

You could also do this, for with and without commas, respectively:

data _null_;
call symputx("w_commas",tranwrd(quote(tranwrd("&kept",' ',quote(', '))),'""','"'));
call symputx("wo_commas",tranwrd(quote(tranwrd("&kept",' ',quote(' '))),'""','"'));
run;

%put &=w_commas;
%put &=wo_commas;

Results:

W_COMMAS="Arne", "Bert", "Hansi"
WO_COMMAS="Arne" "Bert" "Hansi"
yabwon
Onyx | Level 15

Multiple spaces can be a bit of a problem here:

 

%let kept=Arne    Bert  Hansi;

gives

46   data _null_;
47   call symputx("w_commas",tranwrd(quote(tranwrd("&kept",' ',quote(', '))),'""','"'));
48   call symputx("wo_commas",tranwrd(quote(tranwrd("&kept",' ',quote(' '))),'""','"'));
49   run;

NOTE: DATA statement used (Total process time):
      real time           0.01 seconds
      cpu time            0.01 seconds


50
51   %put &=w_commas;
W_COMMAS="Arne", "", "", "", "Bert", "", "Hansi"
52   %put &=wo_commas;
WO_COMMAS="Arne" "" "" "" "Bert" "" "Hansi"

 

Bart

_______________
Polish SAS Users Group: www.polsug.com and communities.sas.com/polsug

"SAS Packages: the way to share" at SGF2020 Proceedings (the latest version), GitHub Repository, and YouTube Video.
Hands-on-Workshop: "Share your code with SAS Packages"
"My First SAS Package: A How-To" at SGF2021 Proceedings

SAS Ballot Ideas: one: SPF in SAS, two, and three
SAS Documentation



quickbluefish
Barite | Level 11
Sure, so just wrap &kept in %cmpres( ) within the syntax above.
yabwon
Onyx | Level 15

or compbl() function

_______________
Polish SAS Users Group: www.polsug.com and communities.sas.com/polsug

"SAS Packages: the way to share" at SGF2020 Proceedings (the latest version), GitHub Repository, and YouTube Video.
Hands-on-Workshop: "Share your code with SAS Packages"
"My First SAS Package: A How-To" at SGF2021 Proceedings

SAS Ballot Ideas: one: SPF in SAS, two, and three
SAS Documentation



hackathon24-white-horiz.png

The 2025 SAS Hackathon Kicks Off on June 11!

Watch the live Hackathon Kickoff to get all the essential information about the SAS Hackathon—including how to join, how to participate, and expert tips for success.

YouTube LinkedIn

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 15 replies
  • 1558 views
  • 8 likes
  • 10 in conversation
OSZAR »