How to read the contents of a file into a SAS macro variable

9

I've been working on a SAS program that can add content to the SAS Support Communities (more on that in a future post). Despite my 20+ years of SAS experience, there are a lot of SAS programming tricks that I don't know. Or that I use so infrequently that I always need to remind myself how to accomplish them.

Here's one. I needed to read the contents of an external text file into a SAS macro variable, so that I could then use that value (a very long text string) as part of an API call. In searching for a technique that would work for me, I came across a similar question on SAS Support Communities -- one that had been solved by our resident SASJedi, Mark Jordan. Perfect!

Here's the solution that worked for me:

FILENAME msghtml "path-to-text-file" ;
data _null_;
   length text $32767;
   retain text '';
   infile msghtml flowover dlmstr='//' end=last;
   input;
   text=cats(text,_infile_);
   if last then call symput('MSGBODY',text);
run;
/* file contents is now in &MSGBODY macro var */

The RETAIN statement allows me to build up the "text" variable as the DATA step processes multiple lines. The END=last on the INFILE statement sets a flag when we hit end-of-file, so I know that we're done and I can CALL SYMPUT the macro value. The FLOWOVER option tells the INPUT statement to keep reading even if no input values are found in the current record. (FLOWOVER is the default behavior, so the option probably isn't needed here.) DLMSTR allows you to specify a multichar delimiter string that's different than the default delimiter (a space character). We're using the CATS function to concatenate a trimmed version of the input buffer (_INFILE_) to the RETAINed "text" variable.

For my project I needed to URL-encode the text value for use in an HTTP-based REST API. So for me, the last line is really:

if last then call symput('MSGBODY',urlencode(trim(text)));

The SAS Support Communities has been a big help to me during this project -- a project that is designed to improve the communities even more. It's a virtuous cycle! I hope that this helps some of you out there, too.

Share

About Author

Chris Hemedinger

Director, SAS User Engagement

+Chris Hemedinger is the Director of SAS User Engagement, which includes our SAS Communities and SAS User Groups. Since 1993, Chris has worked for SAS as an author, a software developer, an R&D manager and a consultant. Inexplicably, Chris is still coasting on the limited fame he earned as an author of SAS For Dummies

9 Comments

  1. I love your post Chris - so meta!!! Using the SAS Support Community to get SAS coding tips to produce reports on SAS community usage and then to also provide a tip with urlencode. Nice! I now look forward to your future post on what content is planned to be added...

    A big thanks to you and the SAS Support Community team for being active managers in the community!

  2. Thanks for the tip! Works great for medium-sized files. Have you thought about what approach to take for very long files? (you mention in the post that the input is a very long text string) When we hit the macro variable length limit (max 65534 characters), this code seems to drop the first part of the file and only saves the last part of the file in the macro variable.

    • Chris Hemedinger
      Chris Hemedinger on

      Rok, good question! You could allocate a series of macro variables based on the file size, then read chunks of the file into each until the entire file contents are in memory.

      How to find the file size? The FINFO function can help.

      filename in "path-to-input-file"; %let fid=%sysfunc(fopen(in)); %let Bytes=%sysfunc(finfo(&fid,File Size (bytes))); %put &Bytes; %let fidc=%sysfunc(fclose(&fid)); filename in clear;

  3. A great SAS tip!
    Wondering if it can be used for EXCEL file to list of variable names before opening it?

    • Chris Hemedinger
      Chris Hemedinger on

      Since Excel docs are binary, I'd say NO -- not without a bunch of other work. But maybe you can use LIBNAME EXCEL or LIBNAME PCFILES or even LIBNAME XLSX to take a pass through the Excel file, pull the field names/types as well as sheet names, and then use that to inform your PROC IMPORT step.

  4. Interesting piece of code, thanks Chris. Passing by value a long text string using a macro variable is limited to 32k in length obviously. To go beyond, paging can be used as mentioned above with Rok; that might also be a case in point for the elusive Proc Stream which has the ability to process multiple macro variables (more generally, any string of arbitrary length) ?

    https://hcsbi.blogspot.fr/2011/09/proc-stream-extending-macro-language-to.html

    https://support.sas.com/documentation/cdl/en/proc/68954/HTML/default/viewer.htm#p06pqn7v5nkz02n0zkpq7832j1yp.htm

Back to Top