Java, C# and other languages have functions that essentially allow for the insertion of one or more variables into a character string template:

var x = 23;
var name = "Jim Goodnight";

var FormattedVar = String.format("My name is {0} and I have {1} billion dollars.", name, x);

This yields the following result for FormattedVar:

"My name is Jim Goodnight and I have 23 billion dollars."

Basically its a string replacement using {} as the delimited for a 0-based array. Is there any SAS-native function for doing this kind of string/character formatting?

asked 22 Dec '11, 15:32

jay.l.stevens's gravatar image

jay.l.stevens ♦
accept rate: 36%

My shot at it. Hope you find it useful, at least for comparison purposes.

%macro StringFormat(String, Vars);
    %*Variable definition;
    %local  _nLeftBrace _nRightBrace _i _currentVar _outString _nUniqueTokens _nTokens _nWords _nParticularTokenFound;          
    %*Check that string is balanced in terms of curly braces;
    %let _nLeftBrace = %eval(%sysfunc(count(&String, {)));
    %let _nRightBrace = %eval(%sysfunc(count(&String, })));
    %*If no braces, nothing to replace - output as is;
    %if &_nLeftBrace = 0 and &_nRightBrace = 0 %then %do;
    %*If unbalanced braces, output error;
    %else %if &_nLeftBrace ne &_nRightBrace %then %do;
        %put ERROR: Invalid string passed to function StringFormat: &String;
    %else %do;
        %*Check that number of distinct tokens (different sets of curly brace definitions) match number of provided words;
        %let _nUniqueTokens = 0;
        %let _nTokens = 0;
        %let _nWords = %eval(%sysfunc(count(&Vars,%str(,)))+1);
        %let _nParticularTokenFound = %sysfunc(count(&String, {&_nUniqueTokens}));

        %do %while (&_nParticularTokenFound > 0);
            %if (&_nParticularTokenFound > 0) %then %do;
                %let _nTokens = %eval(&_nTokens + &_nParticularTokenFound );
                %let _nUniqueTokens = %eval(&_nUniqueTokens + 1);
            %let _nParticularTokenFound = %sysfunc(count(&String, {&_nUniqueTokens}));

        %*If number of tokens does not match number of words, output error;
        %if &_nUniqueTokens ne &_nWords %then %do;      
            %put ERROR: Number of variables does not match number of tokens for string in function StringFormat: &Vars;
        %else %do;
            %*Perform replacement and output string;
            %let _outString = &String;
            %do _i = 0 %to %eval(&_nLeftBrace-1);
                %let _currentVar = %sysfunc(scan(&Vars, %eval(&_i+1), %str(,)));
                %let _outString = %sysfunc(tranwrd(&_outString, {&_i}, &_currentVar));
%mend StringFormat;

answered 14 Feb '12, 01:33

biyectivo's gravatar image

accept rate: 20%

As far as I'm aware, no such native function exists (nothing I can find in 9.1.3 anyway). It's a quite interesting macro to make though:

%macro stringformat(instr=,vars=,curdepth=0);
/*  %put;*/
/*  %put Starting string: &instr.;*/
/*  %put curdepth: &curdepth.;*/
    %local curdepth;
    %local p1;
    %local p2;
    %local numvars;
    /* Take the 1st argument in vars */
    %let cur_var = &vars.;
/*  %put %quote(%str(#)&vars.;*/
/*  %put %eval(%sysfunc(find(%quote(&vars.),%str(,)))-1);*/
    %if (%sysfunc(find(%str(&vars.),%str(,))) > 0) %then 
        %let cur_var = %quote(%substr(%quote(&vars.),1,%eval(%sysfunc(find(%quote(&vars.),%str(,)))-1)));
/*  %put cur_var: &cur_var.;*/
    /* Perform the function */
    %let token_pos = %sysfunc(find(%str(&instr.),{&curdepth.}));
/*  %put token_pos: &token_pos.;*/
    %let p1=;
    %if %eval(&token_pos.-1)>0 %then 
        %let p1=%quote(%substr(&instr.,1,%eval(&token_pos.-1)));
/*  %put p1: &p1.;*/
    %let p2=;
    %if %eval(%eval(&token_pos.+3)<%length(&instr.)) %then
        %let p2=%quote(%substr(&instr.,%eval(&token_pos.+2+%length(&curdepth.))));
/*  %put p2: &p2.;*/
    %if &token_pos. > 0 %then
        %let instr= &p1.&cur_var.&p2.;
    %let token_pos = %sysfunc(find(%str(&instr.),{&curdepth.}));
/*  %put token_pos (second): &token_pos.;*/
    %let again = 1;
    %if (&token_pos <= 0) %then %do;
        %if (&cur_var. = &vars.) %then %let again=0;
        %else %do;
            %let vars=%quote(%substr(&vars.,%eval(%length(&cur_var.)+2)));
/*          %put new vars: &vars.;*/
            %let curdepth=%eval(&curdepth.+1);
    %if (&again. = 1) %then
        %let instr = %stringformat(instr=%quote(&instr.),vars=%quote(&vars.) ,curdepth=&curdepth.);

%mend stringformat;

data _NULL_; 
    /* Basic test */ put "%stringformat(instr=%str(Firstly {0} then {1}),vars=%str(x,y))"; 
    /* Test slightly more robust end-of-string detection */ put "%stringformat(instr=%str(Firstly {0} then {1} and then {2} and then {3} and finally something unreplaced),vars=%str(a,b,c,d,e,f))";
    /* Test multichar length "array" identifiers */ put "%stringformat(instr={0}{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11},vars=%str(a,b,c,d,e,f,g,h,i,j,k,l))";
    /* Test re-use of tokens */ put "%stringformat(instr={0}{0},vars=%str(a))";
    /* Slightly more complex token re-use test */ put "%stringformat(instr=%str(Firstly {0} then {1} then {2} and now {0} again, and now {0} again, and now {1} once more. Finally {3}.),vars=%str(a,b,c,d))";
    /* Why not? Test everything else, and also the use of spaces as the replacement character */ put "%stringformat(instr=%str({0}{1}{2}{3}{1}{2}{3}{1}{2}{4}{5}{6}{2}{4}{2}{7}{1}{8}{8}{9}{10}{2}{1}{11}{2}{12}{13}{14}),vars=%str(y,o, ,h,a,n,d,b,t,l,e,f,r,u,m))";
    /* How about a replacement word? */ put "%stringformat(instr=%str(Firstly {0}),vars=%str(Boom!))"; 
    /* How about a replacement phrase with spaces in it? */ put "%stringformat(instr=%str(Firstly {0}),vars=%str(Boom! Again!))"; 
    /* How about a phrase with a comma in it! */ put "%stringformat(instr=%str(Firstly {0}),vars=%str(%str(Pop, Again!)))";
    /* OP example */ put "%stringformat(instr=%str(My name is {0} and I have {1} billion dollars.),vars=%str(Jim Goodnight,23))";

It doesn't quite work like the above, since it doesn't resolve variable names in place of {0} etc.


answered 03 Jan '12, 07:42

WilliamDobson's gravatar image

accept rate: 36%

I am not convinced that a specific function is necessary. What about simply using CATS()?

FormattedVar = cats("My name is ", name, " and I have", x, " billion dollars.");


answered 24 Aug '12, 16:04

djnordlund's gravatar image

accept rate: 0%

Your answer
toggle preview

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here



Answers and Comments

Markdown Basics

  • *italic* or _italic_
  • **bold** or __bold__
  • link:[text]( "title")
  • image?![alt text](/path/img.jpg "title")
  • numbered list: 1. Foo 2. Bar
  • to add a line break simply add two spaces to where you would like the new line to be.
  • basic HTML tags are also supported



Asked: 22 Dec '11, 15:32

Seen: 3,008 times

Last updated: 24 Aug '12, 16:04

powered by OSQA