So I need to write a macro that replicates this when executed with the appropriate arguments:
data_null_ ;
set books.list;
file "list3.data";
put @1 bookid 4. @5 author $30. @35 isbn isbn18. @43 yearpub comma6.;
run;
The macro is %macro writeoutC (dataset,datafile,variables,formatlist);
Appropriate arguments are %writeoutC (books.list,list3.data,bookid author isbn yearpub,4. $30. isbn18. comma6.);
As you can see the numbers after the @ are a running total of the numbers in the formatting << this is what is giving me trouble.
Here is what I have at the moment
%macro writeoutC (dataset,datafile,variables,formatlist);
%let newput= ;
%let newpointer= ;
%do i=1 %to 4;
%let newpointer=%sysfunc(scan(&formatlist,&i,' $',ap));
%let newput=&newput @%sysfunc(sum(&newpointer+%sysfunc(scan(&formatlist,&i,' $',ap)))) %sysfunc(scan(&variables,&i,'
')):%sysfunc(scan(&formatlist,&i,' '));
%end;
data _null_;
set &dataset;
file "&datafile";
put &newput;
run;
%mend writeoutC;
Which is obviously not working...
OK I fixed your program, so that it produces this confusing mixture of pointer controlled put and list put that would be more easily accomplished almost any other way like adding -L format modifier to each format so that you don't need the : or @col.
@1 bookid:4. @5 author:$30. @35 isbn:isbn18. @53 yearpub:comma6.
If you use formatted output, and you have no whitespace/delimiter, you don't need the position indicator.
And consider to use a data_null step for the creation of your newput macro variable. You then have the full flexibility of the data step at hand. Also use "do until" logic for your list of variables/formats (flexibilty!) and check that both lists have the same number of items.
Darn, ninja'd. Was just writing this, same thing as KurtBremser mentioned, use datasets rather than parameter lists (i.e. imagine having to deal with datasets with hundreds of variables):
data template;
length var_name pos var_fmt $200;
infile datalines;
input var_name pos var_fmt;
datalines;
NAME @1 $8.
SEX @9 $1.
;
run;
data _null_;
set template end=last;
if _n_=1 then call execute('data _null_; set sashelp.classfit; file="s:\temp\rob\classfit.txt"; put ');
call execute(strip(pos)||' '||strip(var_name)||' '||strip(var_fmt));
if last then call execute('; run;');
run;
You don't need to parse anything as long as the format list and name list have the same number of words.
The syntax you would use is.
put (variable-list)(format-list);
put (bookid author isbn yearpub)(4. $30. isbn18. comma6.);
put (&vars)(&formats);
As mentioned by you don't need to use column position @n syntax.
Perhaps a mildly interesting twist on the problem (using my second favorite CALL routine) would be to let SAS do most of the work. You could have a parameter (VARS) for the variable list and another to modify any formats (FMTS) a parameter that has the same syntax as the FORMAT statement. Then there is no need for synchronized lists. You can also create a SAS data set that contains information that describes the record layout of the file. The result with default file attributes are fixed length delimited records.
I appreciate all these creative responses, but I really need to write a macro that produces exactly what is shown at the beginning and none of you have addressed the biggest problem I'm having which is getting the column pointer value to work. Since I posted I've gotten really close, but I'm getting an extra column pointer value after the last variable/format that I don't want...any ideas?
/*Part 9*/
%macro writeoutC (dataset,datafile,variables,formatlist);
%let newput= ;
%let newpointer=1;
%do i=1 %to 4;
%let newpointer=&newpointer+%sysfunc(scan(&formatlist,&i,' $',ap));
%let newput=&newput %sysfunc(scan(&variables,&i,'
')):%sysfunc(scan(&formatlist,&i,' ')) @%eval(&newpointer);
%end;
data _null_;
set &dataset;
file "&datafile";
put @1 &newput;
run;
%mend writeoutC;
/*Part 10*/
%writeoutC (books.list,list2.txt,bookid author isbn yearpub,4. $30. isbn18. comma6.);
I think what the other posters have been trying to tell you is this. You don't need a complicated PUT statement. This combination would work equally well. Use the macro variables to generate:
format bookid 4. author $30. isbn isbn18. yearpub comma6.;
Then your PUT statement can be simple:
put bookid author isbn yearpub;
Good luck.
Sorry ... I didn't notice the colon snuck into the middle to left-hand justify the written messages. Here's what you asked for originally:
%do i=1 %to 4;
%if i=1 %then %let newpointer=1;
%else %let newpointer = %eval(&newpointer + ... same as before but using &i-1 instead of &i ...);
%let newput = &newput @&newpointer ... same as before but omit final @%eval ... ;
%end;
Good luck.
I did not notice the colon either but then I did not look at the code. :smileysilly: However using the colon changes the PUTting from FORMATTED to LIST which doesn't make sense to me. Seems like the -L format modified would be a more logical approach unless there is more the OP is not disclosing.
I tried this:
/*Part 9*/
%macro writeoutC (dataset,datafile,variables,formatlist);
%do i=1 %to 4;
%if &i=1 %then %let newpointer=1;
%else %let newpointer=%eval(&newpointer+&sysfunc(scan(&formatlist,%eval(&i-1),' $',ap)));
%let newput=&newput @&newpointer %sysfunc(scan(&variables,&i,' ')):
%sysfunc(scan(&formatlist,&i,' '));
%end;
data _null_;
set &dataset;
file "&datafile";
put @1 &newput;
run;
%mend writeoutC;
/*Part 10*/
%writeoutC (books.list,list2.txt,bookid author isbn yearpub,4. $30. isbn18. comma6.);
And again, I'm trying to exactly produce the following:
data_null_ ;
set books.list;
file "list3.data";
put @1 bookid 4. @5 author $30. @35 isbn isbn18. @43 yearpub comma6.;
run;
But I'm getting the following errors:
WARNING: Apparent symbolic reference NEWPOINTER not resolved.
WARNING: Apparent symbolic reference SYSFUNC not resolved.
WARNING: Apparent symbolic reference NEWPOINTER not resolved.
WARNING: Apparent symbolic reference SYSFUNC not resolved.
ERROR: A character operand was found in the %EVAL function or %IF condition
where a numeric operand is required. The condition was:
&newpointer+&sysfunc(scan(4. $30. isbn18. comma6.,0,' $',ap))
ERROR: The macro WRITEOUTC will stop executing.
71
%if i=1 should be %if &i=1
%sysfunc not &sysfunc in the %else
Those may be the cause of the %eval error.
Made the change..now I'm getting the following errors:
WARNING: Apparent symbolic reference NEWPUT not resolved.
ERROR: The text expression &NEWPUT @1 BOOKID: 4. contains a recursive
reference to the macro variable NEWPUT. The macro variable will be
assigned the null value.
WARNING: Apparent symbolic reference SYSFUNC not resolved.
WARNING: Apparent symbolic reference SYSFUNC not resolved.
ERROR: A character operand was found in the %EVAL function or %IF condition
where a numeric operand is required. The condition was:
1+&sysfunc(scan(4. $30. isbn18. comma6.,1,' $',ap))
OK I fixed your program, so that it produces this confusing mixture of pointer controlled put and list put that would be more easily accomplished almost any other way like adding -L format modifier to each format so that you don't need the : or @col.
@1 bookid:4. @5 author:$30. @35 isbn:isbn18. @53 yearpub:comma6.
Thank you so much, works perfectly!
Oddly enough I think you made a mistake typing:
%eval(&i-1),$str( $),ap)));
I'm guessing it should be:
%eval(&i-1),%str( $),ap)));
but, it works both ways...
Mistakes are not odd at all for me. :smileysilly:
Join us for SAS Innovate April 16-19 at the Aria in Las Vegas. Bring the team and save big with our group pricing for a limited time only.
Pre-conference courses and tutorials are filling up fast and are always a sellout. Register today to reserve your seat.
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.