The SAS Output Delivery System and reporting techniques

Dumping Text file to ODS output

Accepted Solution Solved
Reply
Super User
Super User
Posts: 8,279
Accepted Solution

Dumping Text file to ODS output

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?


Accepted Solutions
Solution
a week ago
Super User
Super User
Posts: 8,279

Re: Dumping Text file to ODS output

Posted in reply to Cynthia_sas

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


All Replies
Super User
Super User
Posts: 9,840

Re: Dumping Text file to ODS output

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?

Super User
Super User
Posts: 8,279

Re: Dumping Text file to ODS output

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

Super User
Super User
Posts: 9,840

Re: Dumping Text file to ODS output

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.

SAS Super FREQ
Posts: 9,431

Re: Dumping Text file to ODS output

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

 

 

Super User
Super User
Posts: 8,279

Re: Dumping Text file to ODS output

[ Edited ]
Posted in reply to Cynthia_sas

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

 

SAS Super FREQ
Posts: 9,431

Re: Dumping Text file to ODS output

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

Solution
a week ago
Super User
Super User
Posts: 8,279

Re: Dumping Text file to ODS output

Posted in reply to Cynthia_sas

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

SAS Super FREQ
Posts: 9,431

Re: Dumping Text file to ODS output

Tom:
Nice. Just curious -- do you pick up labels and formats if the dataset has those too?
cynthia
Super User
Super User
Posts: 8,279

Re: Dumping Text file to ODS output

[ Edited ]
Posted in reply to Cynthia_sas

@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 ;

 

☑ This topic is solved.

Need further help from the community? Please ask a new question.

Discussion stats
  • 9 replies
  • 156 views
  • 2 likes
  • 3 in conversation