BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
Tom
Super User Tom
Super User

I created a simple utility to generate a data step for sharing a dataset.  Currently it will generate the SAS code into a file and dump it to the SAS log.  Someone suggested for SAS/Studio and EG users it would be better to show it in the Results.

 

Does anyone know enough about ODS styles to make it work well?

 

You can run this step to get a program to recreate SASHELP.CLASS.

filename ds2post url 'http://raw.githubusercontent.com/sasutils/macros/master/ds2post.sas';
%include ds2post ;
%ds2post(sashelp.class);

So I then tried just generating a PROC ODSTEXT step from the file. And it sort of worked, but there were two problems. One the font is proportional and I would rather see code using a fixed font.  The other is that leading spaces were removed.

Title 'Code to recreate SASHELP.CLASS' ;
filename _ods_ temp;
data _null_;
  infile _code_ truncover;
  input line $char100. ;
  file _ods_ ;
  put 'P ' line :$quote. ';' ;
run;

proc odstext;
%include _ods_;
run;

image.png

I then tried adding style=DataFixed option and it seemed to help a little.

filename _ods_ temp;
data _null_;
  infile _code_ truncover;
  input line $char100. ;
  file _ods_ ;
  put 'P ' line :$quote. '/ style=DataFixed ;' ;
run;

image.png

But now it has added lines under the lines of text.

 

Any ODS style experts who can fix this?

1 ACCEPTED SOLUTION

Accepted Solutions
Tom
Super User Tom
Super User

Thanks. @Cynthia_sas

I ended up using those style settings with PROC ODSTEXT.

filename _ods_ temp;
data _null_;
  infile _code_ truncover;
  input line $char100. ;
  file _ods_ ;
  put 'P ' line :$quote. '/ style={asis=on font_face="Courier New" fontsize=10pt} ;' ;
run;

proc odstext;
%include _ods_;
run;

image.png

View solution in original post

9 REPLIES 9
RW9
Diamond | Level 26 RW9
Diamond | Level 26

Why not create a dataset with one long character string to hold each row of the output, then proc print that dataset to the results with style=minimal and nobs?

Tom
Super User Tom
Super User

That might be easier than using PROC ODSTEXT, but still needs more style options. 

It is still eating the leading spaces and adding rules (borders?).

data code ;
  infile _code_ truncover ;
  input CodeLine $char100.;
  format codeline $char100.;
run;

proc print data=code noobs style=minimal;
  var CodeLine ;
run;

image.png

RW9
Diamond | Level 26 RW9
Diamond | Level 26

For the rules, you can set style options (and I am going to switch to proc report here as don't know how much proc print implements:

proc report data=code nowd 
  style(column)={bordertopcolor=white borderbottomcolor=white...};
  column codeline;
  define codeline / style(column)={asis=on};
run;

The asis=on will preserve preceeding spaces.

Cynthia_sas
SAS Super FREQ

Hi:

  Sorry, Tom, the little wheel spun for 5 minutes and nothing happened. Don't know what's in your macro, but ,eventually, I had to kill the session.  I'd try something like this (again, using PROC REPORT) and just use ODS HTML -- then they could copy and paste out of their browser window:

for_tom.png

My .02,

Cynthia

 

 

Tom
Super User Tom
Super User

To goal of writing to Results window is to avoid making it hard for user to find it.

Would that HTML file appear in the Results window in SAS/Studio or EG?

 

Thanks. That formatting does look better.  It is still showing the rulers in the default SAS/Studio results window.  But you can copy and paste the values.

 

proc report data=code nowd 
  style(report)={rules=none frame=void cellpadding=2px }
  style(column)={asis=on font_face="Courier New" fontsize=10pt}
  ;
  column codeline;
  define codeline / ' ';
run;

image.png

 

Cynthia_sas
SAS Super FREQ

Hi: I don't know what your default style is for your Studio. There are some styles where you can't get rid of the lines. Did you change to preferences--> Results --> HTML -->Journal before you ran your code?

 

Interestingly enough, in my test, with trying that (changing the default HTML to Journal, I got fewer lines -- just the top and bottom line:

tom2.png

 

but I got the best results using the tried and true ODS "sandwich" technique. On the other hand, when I did a Ctrl+A and a Ctrl+V from the Results Window, I did NOT get the top and bottom lines. For SAS Studio, a simpler way to code the Journal style might be to try this:

 
 ods html style=journal;
proc report data=text noheader nocenter
  style(report)={rules=none frame=void cellpadding=2px}
  style(column)={font_face="Courier New" fontsize=10pt};
  column ord tline;
  define ord / order noprint;
  define tline / f=$char150.;
run;

but you have to be careful NOT to put any FILE= on the ODS HTML line. This will cause the output to go the Results window and the overrides in the code will impact the JOURNAL style on the ODS HTML statement.

 

Cynthia

Tom
Super User Tom
Super User

Thanks. @Cynthia_sas

I ended up using those style settings with PROC ODSTEXT.

filename _ods_ temp;
data _null_;
  infile _code_ truncover;
  input line $char100. ;
  file _ods_ ;
  put 'P ' line :$quote. '/ style={asis=on font_face="Courier New" fontsize=10pt} ;' ;
run;

proc odstext;
%include _ods_;
run;

image.png

Cynthia_sas
SAS Super FREQ
Tom:
Nice. Just curious -- do you pick up labels and formats if the dataset has those too?
cynthia
Tom
Super User Tom
Super User

@Cynthia_sas wrote:
Tom:
Nice. Just curious -- do you pick up labels and formats if the dataset has those too?
cynthia

It should work for member label, variable labels and formats and informats.  

Note that the macro has a FORMATS option to let you override the formats used when dumping the data for situations where the FORMAT and INFORMAT applied to a variable are incompatible with each other.  For anything more complex I would suggest modifying the dataset and exporting the modified dataset using the macro.

 

Note that after all of your formatting help on this question I ended up not modifying the macro at all.  Users can just call it with FILE=PRINT and it works fine to dump the code to the result window, even when the text only listing destination is turned off.

 

Here is copy of macro as of 2018-08-10 for those having trouble with accessing the github link.

%macro ds2post
/*----------------------------------------------------------------------------
Generate data step to post content of dataset on SAS Communities
----------------------------------------------------------------------------*/
(data     /* Name of dataset to post *REQ*  */
,target=  /* Name to use in generated data step. (default is memname of &DATA) */
,obs=20   /* Number of observations to generate */
,file=log /* Fileref or quoted physical name of file to hold generated code */
,format=  /* Optional format list to use when generating data lines */
);
%local _error noformats;
%*---------------------------------------------------------------------------
Check user parameters.
----------------------------------------------------------------------------;
%let _error=0;
%if "%upcase(%qsubstr(&data,1,2))" = "-H" %then %let _error=1;
%else %if %length(&data) %then %do;
  %if not (%sysfunc(exist(%qscan(&data,1,())))
        or %sysfunc(exist(%qscan(&data,1,()),view))) %then %do;
    %let _error = 1;
    %put ERROR: "&data" is not a valid value for the DATA parameter.;
    %put ERROR: Unable to find the dataset. ;
  %end;
%end;
%else %do;
  %let _error = 1;
  %put ERROR: The DATA parameter is required.;
%end;
%if not %length(&target) %then %let target=%qscan(%qscan(&data,1,()),-1,.);
%if not %length(&obs) %then %let obs=20;
%else %let obs=%upcase(&obs);
%if "&obs" ne "MAX" %then %if %sysfunc(verify(&obs,0123456789)) %then %do;
  %let _error = 1;
  %put ERROR: "&obs" is not a valid value for the OBS parameter.;
  %put ERROR: Valid values are MAX or non-negative integer. ;
%end;
%if not %length(&file) %then %let file=log;

%if (&_error) %then %do;
*----------------------------------------------------------------------------;
* When there are parameter issues then write instructions to the log. ;
*----------------------------------------------------------------------------;
data _null_;
  put
  '%DS2POST'
//'SAS macro to copy data into a SAS Data Step in a '
  'form which you can post to on-line forums.'
//'Syntax:'
/ ' %ds2post(data=,target=,obs=,format=,file=)'
//' data   = Name of SAS dataset (or view) that you want to output.'
//' target = Name to use for generated dataset.'
  ' Default is to use name of the input.'
//' obs    = Number of observations to output. Use MAX to copy complete dataset.'
  ' Default is 20.'
//' file   = Fileref or quoted physical filename for code.'
  ' Default is the SAS log.'
//' format = Optional list of <var_list> <format spec> pairs to use when writing'
  ' data lines.' ' Setting format=_all_ will clear all formats so raw data'
  ' values are written.'
//'Note that this macro will NOT work well for really long data lines.'
 /'If your data has really long variables or a lot of variables then consider'
  ' splitting your dataset in order to post it.'
  ;
run;
%end;
%else %do;
*----------------------------------------------------------------------------;
* Get contents information and sort by VARNUM ;
*----------------------------------------------------------------------------;
proc contents noprint data=&data
  out=_contents_(keep=name varnum type length format: inform: memlabel label)
;
run;
proc sort data=_contents_ ;
  by varnum;
run;
*----------------------------------------------------------------------------;
* Generate top of data step ;
*----------------------------------------------------------------------------;
filename _code_ temp;
data _null_;
  length firstvar name $60 string $300 ;
  retain firstvar ;
  file _code_ column=cc ;
  set _contents_ end=eof ;
  if _n_=1 then do;
    put "data &target" @;
    if not missing(memlabel) then do;
      string=quote(trim(memlabel),"'");
      put '(label=' string ')' @;
    end;
    put ';';
  end;
  name=nliteral(name) ;
  if _n_=1 then firstvar=name;
  string=cats(ifc(type=2,'$',' '),length);
  put '  attrib ' name 'length=' string @;
  if formatl or not missing(format) then do;
     string=cats(format,ifc(formatl,cats(formatl),''),'.',ifc(formatd,cats(formatd),''));
     put 'format=' string @ ;
  end;
  if informl or not missing(informat) then do;
     string=cats(informat,ifc(informl,cats(informl),''),'.',ifc(informd,cats(informd),''));
     if cc+9+length(string)>80 then put / @4 @ ;
     put 'informat=' string @ ;
  end;
  if not missing(label) then do;
     string=quote(trim(label),"'");
     if cc+7>80 then put / @4 'label=' string @ ;
     else if cc+7+length(string)>80 then put 'label=' / @4 string @ ;
     else  put 'label=' string @;
  end;
  put ';' ;
  if eof then do;
     put "  infile datalines dsd dlm='|' truncover;" ;
     put '  input ' firstvar '-- ' name ';';
     put 'datalines4;' ;
  end;
run;
*----------------------------------------------------------------------------;
* Generate list of variables that do not have attached informats. ;
*----------------------------------------------------------------------------;
proc sql noprint;
  select name into :noformats separated by ' '
    from _contents_ where missing(informat)
  ;
quit;
*----------------------------------------------------------------------------;
* Generate data lines ;
*----------------------------------------------------------------------------;
data _null_;
  file _code_ mod dsd dlm='|';
%if (&obs ne MAX) %then %do;
  if _n_ > &obs then stop;
%end;
  set &data ;
%if %length(&noformats) %then %do;
  format &noformats ;
%end;
%if %length(&format) %then %do;
  format &format ;
%end;
  put (_all_) (+0) ;
run;
data _null_;
  file _code_ mod ;
  put ';;;;';
run;
  %if "%qupcase(&file)" ne "_CODE_" %then %do;
*----------------------------------------------------------------------------;
* Copy generated code to target file name ;
*----------------------------------------------------------------------------;
data _null_ ;
  infile _code_;
  file &file ;
  input;
  put _infile_;
run;
  %end;
%end;
%mend ds2post ;

 

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
  • 9 replies
  • 2784 views
  • 2 likes
  • 3 in conversation