BookmarkSubscribeRSS Feed
☑ This topic is solved. Need further help from the community? Please sign in and ask a new question.
Kc2
Quartz | Level 8 Kc2
Quartz | Level 8

Hi,

 

I am using a macro to combine RTF outputs. The macro runs without errors but the final file has only the first output.

The intermediate RTF files look correct.

Any suggestions?

 

thanks

KC

1 ACCEPTED SOLUTION

Accepted Solutions
ballardw
Super User

First a generic comment about debugging macros. When a macro doesn't do what you expect you can set

options mprint;

and if you are using complex macro variable constructions, like the indirect reference &&file&i you may want the SYMBOLGEN option as well.

Convoluted macro logic may call for the MLOGIC option.

Then re-run your macro to read more details of the generated code in the Log. Plus any messages will be closer to the actual generated code.

 

Second: Habitual use of code with the same data set as the input and output sets such as

         data rtf&i;
            set rtf&i end=eof;
            if eof then line="\pard}}";
         run;

Are extremely difficult to debug because the output set completely replaces in the input set. So you can't trace data related issues very well. Better to have a different name on the output side so you can see if the output matches what you expect for a given input at any given step.

 

If your data set RTF only has one file's worth of content then look to whether any of the Rtf&i were actually created and what their content looks like. But keep in mind the warning about about input/output sets with the same name. You no longer have, with your existing code, any idea of what the original Rtf&i sets looked like.

 

Instead of sticking that N and File& information into macro variables with this step:

proc sql noprint;
select count(*) into:n from dirlist;
select filename into:file1-:file%cmpres(&n) from dirlist;
quit;

You can use the Dirlist data set directly, testing the _n_ variable instead of &n, and either use PUT statements to write a .SAS program file that you can read and verify for accuracy before %include it for execution or use the CALL Execute statement to write statements direct to the macro execution buffer.

 

I am little concerned about use of Macro functions in data step code such as in the following. How carefully did you test this before including it?

         data rtf&i;
            set rtf&i end=eof;
            if _n_>29;
            if _n_=30 then line="\sect"||trim(left(line));
            if eof then line=trim(left(line))||"%sysfunc(repeat(\pard,%eval(&n-1)))}";
         run;

If you look at this example you can see no macro functions required:

%let n=3;

data example;
   length x $ 50;
   x='Sometext';
   x=strip(x)||repeat('\pard',&n-1);
run;

Strip is equivalent to Trim(left(<text value>)) and has been around for like 20 years. And if you use the CAT functions such as CATS would not even be needed as CATS strips all the values involved.

%let n=3;

data example;
   length x $ 50;
   x='Sometext';
   x=cats(x,repeat('\pard',&n-1) );
run;

 

View solution in original post

3 REPLIES 3
ballardw
Super User

NO code attached so have no clue as to what you did or why it may not have all the output expected.

 

Generally I would say combining already created RTF files is a Word processor program job due to the volume of non-data elements that are in a typical RTF file.

 

 

 


@Kc2 wrote:

Hi,

 

I am using a macro to combine RTF outputs. The macro runs without errors but the final file has only the first output.

The intermediate RTF files look correct.

Any suggestions?

 

thanks

KC


 

Kc2
Quartz | Level 8 Kc2
Quartz | Level 8

%macro combine(pathin=, pathout=, fileout=);


filename dirlist pipe "dir ""&pathin*.rtf"" /b ";

data dirlist;
infile dirlist length=reclen ;
input filename_ $varying1024. reclen ;
if scan(filename_,2,'.') in ('rtf');
path="&pathin";
if filename_ ne "&fileout..rtf";
filename=strip(path)||'\'||strip(filename_);
run;

proc sql noprint;
select count(*) into:n from dirlist;
select filename into:file1-:file%cmpres(&n) from dirlist;
quit;

data rtf;set _null_;run;

%do i=1 %to &n;
filename rtf "&&file&i" ;

data rtf&i;
infile rtf missover length = l end = lastobs lrecl = 2000;
input line $varying2000. l;
run;

%if &i=1 %then %do;
data rtf&i;
set rtf&i end=eof;
if eof then line="\pard}}";
run;
%end;

%else %if &i=&n %then %do;
data rtf&i;
set rtf&i end=eof;
if _n_>29;
if _n_=30 then line="\sect"||trim(left(line));
if eof then line=trim(left(line))||"%sysfunc(repeat(\pard,%eval(&n-1)))}";
run;
%end;

%else %do;
data rtf&i;
set rtf&i end=eof;
if _n_>29;
if _n_=30 then line="\sect"||trim(left(line));
if eof then line="\pard}}";
run;
%end;

data rtf;set rtf rtf&i;run;
%end;

filename rtf "&pathout.&fileout..rtf" ;

data _null_;
set rtf;
file rtf lrecl=2000;
put line;
run;

%mend;

ballardw
Super User

First a generic comment about debugging macros. When a macro doesn't do what you expect you can set

options mprint;

and if you are using complex macro variable constructions, like the indirect reference &&file&i you may want the SYMBOLGEN option as well.

Convoluted macro logic may call for the MLOGIC option.

Then re-run your macro to read more details of the generated code in the Log. Plus any messages will be closer to the actual generated code.

 

Second: Habitual use of code with the same data set as the input and output sets such as

         data rtf&i;
            set rtf&i end=eof;
            if eof then line="\pard}}";
         run;

Are extremely difficult to debug because the output set completely replaces in the input set. So you can't trace data related issues very well. Better to have a different name on the output side so you can see if the output matches what you expect for a given input at any given step.

 

If your data set RTF only has one file's worth of content then look to whether any of the Rtf&i were actually created and what their content looks like. But keep in mind the warning about about input/output sets with the same name. You no longer have, with your existing code, any idea of what the original Rtf&i sets looked like.

 

Instead of sticking that N and File& information into macro variables with this step:

proc sql noprint;
select count(*) into:n from dirlist;
select filename into:file1-:file%cmpres(&n) from dirlist;
quit;

You can use the Dirlist data set directly, testing the _n_ variable instead of &n, and either use PUT statements to write a .SAS program file that you can read and verify for accuracy before %include it for execution or use the CALL Execute statement to write statements direct to the macro execution buffer.

 

I am little concerned about use of Macro functions in data step code such as in the following. How carefully did you test this before including it?

         data rtf&i;
            set rtf&i end=eof;
            if _n_>29;
            if _n_=30 then line="\sect"||trim(left(line));
            if eof then line=trim(left(line))||"%sysfunc(repeat(\pard,%eval(&n-1)))}";
         run;

If you look at this example you can see no macro functions required:

%let n=3;

data example;
   length x $ 50;
   x='Sometext';
   x=strip(x)||repeat('\pard',&n-1);
run;

Strip is equivalent to Trim(left(<text value>)) and has been around for like 20 years. And if you use the CAT functions such as CATS would not even be needed as CATS strips all the values involved.

%let n=3;

data example;
   length x $ 50;
   x='Sometext';
   x=cats(x,repeat('\pard',&n-1) );
run;

 

SAS Innovate 2025: Save the Date

 SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!

Save the date!

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
  • 3 replies
  • 1126 views
  • 1 like
  • 2 in conversation