Carrige return, tabs with the put statement - sas

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

Related

check whether %include statement is exist in sas code or not

I have SAS code in which there is following line of code is included
% include "/sas/dev/compare.sas";
it is at location on server
/sas/cpm/dev/code
So, at present what I am doing manually go to above path and open each .sas code(in a Folder, you can say there are 4 codes location at /sas/cpm/dev/code) and check whether %include "/sas/dev/compare.sas" line of code is present or not
Can anyone help me without checking/open manually .sas code how to check whether %include "/sas/dev/compare.sas"; is exist or not
Can anyone , please help me how to write in sas,
We can pass each code through macro right
Thanks
You can do this by:
Listing all the .sas files in each folder,
Create a datasetp / infile statement for each file,
Search each line in the file for % include "/sas/dev/compare.sas";
If line is found, print to the log the file name and line number
Code:
%let extension=sas;
%let FolderPath=\\sas\SASDATA\momo\;
%macro check_file_path(f=);
DATA _null_;
infile "&f" dsd ;
length string $200.;
input string $;
if string=&check. then put "&f. " "includes # line " _N_= ;
run;
%mend;
Data List_files;
rc=FILENAME('FMyRep',"&FolderPath");
did=DOPEN('FMyRep');
memcnt=DNUM(did);
*count number of members - including subfolders;
DO i=1 TO memcnt;
*for each member, test extension and store filename;
filevar=LOWCASE(DREAD(did,i));
file_path=cats("&FolderPath.",filevar);
IF LOWCASE(SCAN(filevar,-1,".")) EQ LOWCASE("&extension") THEN
OUTPUT;
END;
rc=DCLOSE(did);
rc=FILENAME('FMyRep');
KEEP filevar file_path;
RUN;
data _null_;
set List_files;
call execute('%check_file_path(f='||file_path||')');
put file_path=;
run;
Output:
file_path=\\sas\SASDATA\momo\file1.sas
file_path=\\sas\SASDATA\momo\file2.sas
file_path=\\sas\SASDATA\momo\file3.sas
file_path=\\sas\SASDATA\momo\file4.sas
Log: file4.sas is the only one that doesn't have this % include "/sas/dev/compare.sas";
\\sas\SASDATA\momo\file1.sas includes # line _N_=1
\\sas\SASDATA\momo\file2.sas includes # line _N_=1
\\sas\SASDATA\momo\file4.sas includes # line _N_=1
you can do it by using fileexist function
%let myfile = "/sas/dev/compare.sas";
%macro filecheck;
%if %sysfunc(fileexist(&myfile))
%then % include "/sas/dev/compare.sas";
%else %put The external file &myfile does not exist.;
%mend;
%filecheck;

Append string to text file in SAS

I wanna append string to a text file at beginning and end of proc sql statment, I tried like below
libname DXZ 'libpath';
%macro processlogger(msg);
filename logfile '../Processlog/processlog.txt';
data _null_;
file logfile;
put "%superq(message)";
run;
%mend;
%processlogger ('Begin');
proc sql;
select * from DZ.NoofDaysin_Reje /* Mispelled name */
run;
%processlogger('End');
I seems to messing up in macro variable, is there any other way I can do this, Thanks
If you want to use a data step to append to a text file then you need to add the MOD keyword to the FILE statement.
If you want to print the value of a macro variable that might have quotes and other strange characters in a data step then it is probably best to use symget() to retrieve the value into a datastep variable and print that.
Make sure to reference the macro variable that you created msg and not some other macro variable message.
If you don't want quotes to be included in the value of a macro variable then do not add them.
%macro processlogger(msg);
data _null_;
file '../Processlog/processlog.txt' mod;
length message $32767 ;
message=symget('msg');
put message ;
run;
%mend;
%processlogger(Starting at %sysfunc(datetime(),datetime24.3));
%processlogger(Ending at %sysfunc(datetime(),datetime24.3));
You can also use PRINTTO to redirect your log to a text file. And you have the option to either append to the original file or replace. Example here.
/*Redirect your log file*/
proc printto log='../Processlog/processlog.txt';
run;
/* Your Code Here */
/* Reset log path to default */
proc printto;
run;

ERROR: No logical assign for filename FNAME

I was running the following code:
%LET TIME_INTERVAL='MINUTE15';
/*
* Get the file names of a specific location
*/
%MACRO get_filenames(location,filenames);
filename _dir_ "%bquote(&location.)";
data &filenames(keep=fname);
handle=dopen( '_dir_' );
if handle > 0 then do;
count=dnum(handle);
do i=1 to count;
fname=dread(handle,i);
output &filenames;
end;
end;
rc=dclose(handle);
run;
filename _dir_ clear;
%MEND;
%MACRO NBBO (fname);
DATA TICKERS_NBBO;
INFILE &fname;
INPUT SYMBOL $;
RUN;
%mend;
%MACRO CALCU(DATE_VAR);
%get_filenames('./groups',filenames);
data _null_;
set filenames;
by fname;
if fname =: "&TIME_INTERVAL";
%NBBO(fname);
run;
%mend;
However, I got the error: ERROR: No logical assign for filename FNAME.
I was wondering what is the reason that caused this?
There are lots of csv files in the folder groups. I was trying to run the NBBO macro on each of the files and load each of the files into a dataset with infile statement.
You're mixing up data step code and macro code in a manner that's not permitted. You can't extract the contents of the variable fname and use it to populate a macro variable in this way. Instead you're passing the text fname to that macro variable, which then doesn't work.
You also can't run another data step inside a data step in this manner. You need to use one of the various methods for executing it after the data step terminates or during execution.
Finally, it's entirely unclear to me what you're really doing here: because you're going to end up with just one of those files in the dataset.
I think you may want something like this, which is much easier. I use the FILEVAR option in infile, which uses the value of a variable; I have to make some changes to how you calculate fname (I think you have to do this anyway, it has no directory name in it).
%MACRO get_filenames(location,filenames);
filename _dir_ "%bquote(&location.)";
data &filenames(keep=fname);
length fname $512;
handle=dopen( '_dir_' );
if handle > 0 then do;
count=dnum(handle);
do i=1 to count;
fname=catx('\',"%bquote(&location.)",dread(handle,i));
output &filenames;
end;
end;
rc=dclose(handle);
run;
filename _dir_ clear;
%MEND;
%get_filenames(location=c:\temp\cars, filenames=fnam);
data tickers_nbbo;
set fnam;
infile a filevar=fname dlm=',';
input symbol $;
run;
If you really do need to call a data step separately, you either need to use CALL EXECUTE, DOSUBL, or construct macro calls in another way (PROC SQL SELECT INTO, %INCLUDE, etc.).

Using %PUT to correctly format the dynamic file name

I have a SAS script that reads in a CSV file and stores it in a SAS data set:
LIBNAME IN '\\path\Data';
FILENAME CSV '\\path\Data\DT.csv';
DATA IN.DT;
INFILE CSV DLM=',' DSD FIRSTOBS=1;
INPUT KEY VALUE1 VALUE2;
RUN;
I want to change it such that instead of expecting the input to be named DT.csv, it would accept an input named DT-2016-03-03-TEST.csv, or whatever the current date is. In other words, I need to use a dynamic value in my FILENAME statement.
Here is what I have so far:
%LET curday = %SYSFUNC(day("&sysdate"d));
%LET curmonth = %SYSFUNC(month("&sysdate"d));
%LET curyear = %SYSFUNC(year("&sysdate"d));
%PUT %SYSFUNC(PUTN(&curday, z2.));
FILENAME CSV "\\path\Data\DT-&curyear-&curmonth-&curday-TEST.csv";
But the string it generates is like Data\DT-2016-3-3-TEST.csv rather than Data\DT-2016-03-03-TEST.csv
In other words, the trailing zeros are not there. What am I doing incorrectly?
You'll need to use either a macro variable or a big group of macro functions (whichever you'd like). We'll go with creating macro variables for readability purposes. Based upon what you've said, we know a few things about the pattern:
It starts with DT-
It has today's date in a yyyy-mm-dd format
It ends in .csv
Two of these are static values, and one needs to be dynamic in a specific format. Let's get crackin'.
Start off by storing the path in its own macro variable. This makes the code more generalizable to other applications (i.e. you can copy/paste old code for new programs! It's good to be lazy in the programming world).
%let path = \\path\data;
Next, let's build our dynamic pattern using a %let statement. We know it starts with DT-:
___________________________________________
%let file = DT-
___________________________________________
We can now cross #1 off the list! Let's knock out #2.
Two functions will help us get this in the order that we want:
%sysfunc()
today()
We'll encapsulate today() with %sysfunc(). %sysfunc() lets us run most non-macro-language SAS functions, and also has the added benefit of returning the value in a format that you desire using an additional argument. This is really helpful for us here.
So, let's grab today's date as a numeric SAS date, then convert it to yymmddx10 format, where x is some delimiter keyword. We'll use yymmddd10. - that is, a format that specifies yyyy-mm-dd. The extra d means dash.
___________________________________________
%let file = DT-%sysfunc(today(), yymmddd10.)
___________________________________________
2 is now out of the way. Hard part's over! All we need to do is append .csv to it, and we'll be all set.
___________________________________________
%let file = DT-%sysfunc(today(), yymmddd10.).csv;
___________________________________________
You can confirm the macro variable file's value with a %put statement:
%put NOTE: This is my filename: &file;
You should see in green text in the log NOTE: This is my filename: DT-2016-03-03.csv
Now, we'll just put it all together:
%let path = \\path\data;
%let file = DT-%sysfunc(today(), yymmddd10.).csv;
libname IN "&path";
filename CSV "&path\&file";
data in.DT;
infile csv dlm=',' dsd firstobs=1;
input key value1 value2;
run;
You've now got a dynamic way to read in these CSVs, and you can adapt this code elsewhere. Awesomesauce. I think you've earned yourself a celebratory coffee, and maybe a biscotti or two; don't go too crazy.
Stu's answer is absolutely correct. For the tl;dr version.
%put echos stuff to the log. All you are doing is "putting" the result of %SYSFUNC(PUTN(&curday, z2.)) to the log. You are not updating the value in &curday.
Try
%LET curday = %SYSFUNC(PUTN(&curday, z2.));
Do that for the other curmonth, too.
Take the time and read Stu's answer.

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;