SAS print text from file to spreadsheet - sas

Is there a way to print the contents of a .txt file to a tab using ods tagsets.excelxp? I have a script that creates a .xml file with several tabs, and on one of the tabs I'd like to print the lines of the code itself, so that I can send the .xml file to someone and they can have the code that I used to produce the output. I have the code saved separately as a .txt. Any help or suggestions would be appreciated.

Easiest way would be to read the text file lines into a Data Set. Then use PROC PRINT to print to your output destination;
data lines;
infile "path/to/file.txt";
format code $2000.;
input;
code = _infile_;
run;
proc print data=lines noobs;
run;

Related

Reading and extracting files from a directory using SAS

I have a directory /run/return/files/archives/prep/share/ that contains both .txt and .csv files.
For example IA_PROD.txt and retour_PROD.csv
I want to read both types of files and extract only their names (IA_PROD and retour_PROD) to store in an excel file named FILE_NAMES.xlsx. I have the code below that extracts .txt and .csv files though two separate data sets (file_list1 and file_list2) and I finally concatenate the two data sets to export in an excel sheet. I wanted to be able to optimise my code to make it one single data step where I read both csv, txt and extract both of them together.
Thanks for your generous help
%let REP_BLOCTEL_ALLER = /run/return/files/archives/prep/share/;
filename result pipe "ls &rep_bloctel_aller./*txt";
filename result2 pipe "ls &rep_bloctel_aller./*csv";
data file_list1;
infile result lrecl=200 truncover;
input rep $120.;
file_name = tranwrd(substr(rep, length("&rep_bloctel_aller./")+1),'.txt','');
call symput(compress('txt_'!!put(_n_,2.)),file_name);
call symput('n_obs',put(_n_,2.));
run;
data file_list2;
infile result2 lrecl=200 truncover;
input rep $120.;
file_name = tranwrd(substr(rep, length("&rep_bloctel_aller./")+1),'.csv','');
call symput(compress('csv_'!!put(_n_,2.)),file_name);
call symput('n_obs',put(_n_,2.));
run;
DATA file_list;
SET file_list1 file_list2;
RUN;
proc export data = file_list
(keep = file_name)
outfile="&rep_bloctel_aller./FILE_NAME_BLOCTEL.xlsx"
dbms=xlsx
replace;
sheet= "FILE_NAME_BLOCTEL";
run;
Not sure if it answers your question, but I think this answers your problem :)
What I propose it to filter your input files with ls :
%let REP_BLOCTEL_ALLER = /run/return/files/archives/prep/share/;
filename result pipe "ls &file_path. | egrep -i '\.csv$|\.txt$'";
data file_list;
infile result lrecl=200 truncover;
input filename $120.;
run;
I don't think we can make it shorter!
Some explanations:
ls lists your directory
the pipe | forwards the result of ls to the egrep command
egrpe is used to search for text value in the result sent by ls
the -i option indicates to egrep that text lookup must be case insensitive (will detect TXT, txt, TxT files and so on)
The '.csv$|.txt$' indicates to search for either '.csv' or '.txt' at the end of a line ($), which corresponds to the file the extension
If the goal is to just generate the list into an XLSX file then you just need:
libname out xlsx "&rep_bloctel_aller./FILE_NAME_BLOCTEL.xlsx";
data out.FILE_NAME_BLOCTEL;
infile "cd &rep_bloctel_aller.; ls *.txt *.csv" pipe truncover;
input file_name $256.;
run;

Carrige return, tabs with the put statement

I want to use the data step put statement to write some html and javascript code. While I can have the resulting html file look the way I want in the browser, I don't know how to have the code be easy to read both in SAS EG and the resulting file.
I would like the resulting file to have carrige returns, tabs etc, but would like to avoid adding quotation marks to every line. Also, I need it to run in a macro. I've included some attempts below. Is it an easy way to combine the readable results of ex. 2 and ex. 3 with the coidng ease of ex. 1, like an option to set?
/* Ex 1: Easy to read in SAS EG, but no tabs or carrige returns in html-file*/
data _null_;
file "C:\Test\test1.html" encoding="utf-8" ;
put " Some code
Some code on a new line
Some indented code";
run;
/* Ex 2: Tabs and line breaks in html file, but far more cumbersome to write in SAS EG.*/
data _null_;
file "C:\Test\test2.html" encoding="utf-8";
put #4 "Some code" /
#4 "Some code on a new line" /
#8 "Some indented code";
run;
/* Ex 3: Easy to read and write in SAS EG, reads well in html file. But won't run in a macro, and resolving macro variables is more trouble than with the methods above.*/
data _null_;
input ;
file "C:\Test\test3.html" encoding="utf-8";
put _infile_;
datalines;
Some code
Some code on a new line
Some indented code
;
run;
For number 3 you can used PARMCARDS and RESOLVE function.
filename FT15F001 temp;
parmcards4;
Some code
Some code on a new line &sysdate
Some indented code
;;;;
%macro main;
data _null_;
infile FT15F001;
input;
file "C:\Test\test3.html" encoding="utf-8";
_infile_ = resolve(_infile_);
put _infile_;
run;
%mend;
%main;
A comment from Tom suggested I should use PROC STREAM. This proved way easier than variations on the use of put-statements. A simplified example doesn't quite do it justice, but this proc makes it possible to write this kind of code without the tedium of getting quoting right. I still need to use &streamDelim newline; for line breaks, but that's a small price to pay. Example of current setup;
/* Ex 4. Proc stream*/
%macro chartx();
%let cr=%str(&streamDelim newline;);
Some code &cr
Some code on a new line &cr
Some code on a new line, with resolved macro variable &sysdate &cr
some indented code &cr
%do i=1 %to 1;
Some code from a loop
%end;
%mend;
%macro makepage();
filename testfile "C:\Test\test4.html";
proc stream outfile=testfile;
BEGIN
%chartx
;;;;
%mend;
%makepage

Read a file line by line for every observation in a dataset

I'm trying to create a program that takes a text file, replaces any macro references within it, and appends it to a single output file. The macro references are generated as I iterate over the observations in a dataset.
I'm having trouble trying to get it to read the entire text file for each observation in my source table. I think there's an implicit stop instruction related to my use of the end= option on the infile statement that is preventing my set statement from iterating over each record.
I've simplified the template and code, examples below:
Here is the template that I'm trying to populate:
INSERT INTO some_table (name,age)
VALUES (&name,&age);
Here is the SAS code:
filename dest "%sysfunc(pathname(work))\backfill.sql";
data _null_;
attrib line length=$1000;
set sashelp.class;
file dest;
infile "sql_template.sas" end=template_eof;
call symput('name', quote(cats(name)));
call symput('age' , cats(age));
do while (not template_eof);
input;
line = resolve(_infile_);
put line;
end;
run;
Running the above code produces the desired output file but only for the first observation in the dataset.
You cannot do it that way since after the first observation you are already at the end of the input text file. So your DO WHILE loop only runs for the first observation.
Here is a trick that I learned a long time ago on SAS-L. Toggle between two input files so that you can start at the top of the input file again.
First let's create your example template program and an empty dummy file.
filename template temp;
filename dummy temp;
data _null_;
file template;
put 'INSERT INTO some_table (name,age)'
/ ' VALUES (&name,&age)'
/ ';'
;
file dummy ;
run;
Now let's write a data step to read the input data and use RESOLVE() function to convert the text.
filename result temp;
data _null_;
length filename $256 ;
file result ;
set sashelp.class;
call symputx('name', catq('1at',name));
call symputx('age' , age);
do filename=pathname('template'),pathname('dummy');
infile in filevar=filename end=eof ;
do while (not eof);
input;
_infile_ = resolve(_infile_);
put _infile_;
end;
end;
run;
The resulting file will look like this:
INSERT INTO some_table (name,age)
VALUES ('Alfred',14)
;
INSERT INTO some_table (name,age)
VALUES ('Alice',13)
;
...

Read file without delimiters (as a single column) in SAS

I have an xml file that I need to read as a single column table. Now, to achieve the result I embed following line into INFILE statement:
dlmstr='nodlmstr'
I have not found any appropriate option that would let me do it more accurately.
I don't think you need an option at all. Just create your 1 variable with enough length to hold the line.
data _null_;
file "c:\temp\test.xml";
put "<a>";
put " <aa>1 </aa>";
put " <bb>2</bb>";
put "</a>";
run;
data test;
infile "c:\temp\test.xml";
format line $2000.;
input;
line = _infile_;
run;

Exporting to a text file in SAS with a double delimitter

I'm trying to use a double pipe delimiter "||" when I export a file from SAS to txt. Unfortunately, it only seems to correctly delimit the header row and uses the single version for the data.
The code is:
proc export data=notes3 outfile='/file_location/notes3.txt'
dbms = dlm;
delimiter = '||';
run;
Which results in:
ID||VAR1||VAR2
1|0|STRING1
2|1|STRING2
3|1|STRING3
If you want to use a two character delimiter, you need to use dlmstr instead of dlm in the file statement in data step file creation. You can't use proc export, unfortunately, as that doesn't support dlmstr.
You can create your own proc export fairly easily, by using dictionary.columns or sashelp.vcolumn to construct the put statement. Feel free to ask more specific questions on that side if you need help with it, but search around for data driven output and you'll most likely find what you need.
The reason proc export won't use a double pipe is because it generates a data step to do the export, which uses a file statement. This is a known limitation - quoting the help file:
Restriction: Even though a character string or character variable is
accepted, only the first character of the string or variable is used
as the output delimiter. This differs from INFILE DELIMITER=
processing.
The header row || works because SAS constructs it as a string constant rather than using a file statement.
So I don't think you can fix the proc export code, but here's a quick and dirty data step that will transform the output into the desired format, provided that your dataset has no missing values and doesn't contain any pipe characters:
/*Export as before to temporary file, using non-printing TAB character as delimiter*/
proc export
data=sashelp.class
outfile="%sysfunc(pathname(work))\temp.txt"
dbms = dlm;
delimiter = '09'x;
run;
/*Replace TAB with double pipe for all rows beyond the 1st*/
data _null_;
infile "%sysfunc(pathname(work))\temp.txt" lrecl = 32767;
file "%sysfunc(pathname(work))\class.txt";
input;
length text $32767;
text = _infile_;
if _n_ > 1 then text = tranwrd(text,'09'x,'||');
put text;
run;
/*View the resulting file in the log*/
data _null_;
infile "%sysfunc(pathname(work))\class.txt";
input;
put _infile_;
run;
As Joe suggested, you could alternatively write your own delimiter logic in a dynamically generated data step, e.g.
/*More efficient option - write your own delimiter logic in a data step*/
proc sql noprint;
select name into :VNAMES separated by ','
from sashelp.vcolumn
where libname = "SASHELP" and memname = "CLASS";
quit;
data _null_;
file "%sysfunc(pathname(work))\class.txt";
set sashelp.class;
length text $32767;
text = catx('||',&VNAMES);
put text;
run;