I'm creating a text file with SAS and I'm using a macro variable with a date in my text file's name to make it distinct from other similar files.
The problem I'm experiencing:
SAS is adding two unwanted spaces in the middle of the file name. The unwanted spaces are placed directly before the text generated by my macro variable
I'm certain this has everything to do with my macro variable being used, but on its own, the variable doesn't contain any spaces. Below is my code:
proc format;
picture dateFormat
other = '%Y%0m%0d%0H%0M' (datatype=datetime);
run;
data _null_;
dateTime=datetime();
call symput('dateTime', put(dateTime,dateFormat.));
run;
%LET FILE = text_text_abc_&dateTime..txt;
filename out "/location/here/&FILE" termstr=crlf;
data _null_; set flatfile;
/*file content is created in here*/
run;
The exported file name will look like this:
NOTE: The file OUT is:
Filename=/location/here/text_text_abc_ 201702010855.txt
If it helps, I'm using SAS E-Guide 7.1.
Any help is appreciated! Thanks, all!
You need to assign an appropriate default length to your picture format. SAS is applying a default default length of 14 but you need 12, e.g.
proc format;
picture dateFormat (default=12)
other = '%Y%0m%0d%0H%0M' (datatype=datetime);
run;
Use call symputx() instead of call symput(), then SAS will automatically strip the leading and trailing blanks from the value written to the macro variable. You should really only use call symput() in the rare cases where you want the macro variable value to have leading or trailing blanks.
Run this little program to see the difference.
data _null_;
str=' XX ';
call symput('var1',str);
call symputX('var2',str);
run;
%put |&var1|;
%put |&var2|;
Related
I have to scan a target directory, and import every CSV file there into SAS. I am to use separated macros to fix names of those files (they are intentionaly problematic for SAS) and actually import them. I have XCMD disabled, so pipe solution isn't usable for me (the only solution my course provided me, so several exercises are a bit problematic for me). My attempt so far is this:
%let path=my_dir
%macro fixname(badname);
%if %datatyp(%qsubstr(&badname,1,1))=NUMERIC
%then %let badname=_&badname;
%let badname=
%sysfunc(compress(
%sysfunc(translate(&badname,_,%str( ))),,kn));
%substr(&badname,1,%sysfunc(min(%length(&badname),32)))
%mend fixname;
%macro importcsv(file);
options nonotes nosource;
proc import
datafile="%superq(file)"
out=%fixname(%scan(&file,-2,.\)) replace
dbms=csv;
run;
options notes source;
%mend importcsv;
filename folder "&path";
data FilesInFolder;
length Line 8 File $300;
List = dopen('folder');
do Line = 1 to dnum(List);
File = trim(dread(List,Line));
output;
end;
drop list line;
run;
proc sql noprint;
select * into :file1-
from FilesInFolder
where File like '%.csv'
;
quit;
%macro loop;
%do i=1 %to &sqlobs;
call execute(cats('%importcsv('&path.\&&file&i')'));
%end;
%mend loop;
%loop;
Looking at logs there seems to be something wrong with call execute line, but for the life of me I cannot fix it.
As regards the requested call execute line, this will not work.
call execute(cats('%importcsv('&path.\&&file&i')'));
The cats is not properly constructed. It's unclear why you're even using cats, as you aren't really concatenating anything. It's clear why you need to use it, but not why you're currently using it.
First: you have single quotes around the string, which means it won't resolve the macro variables. That is a problem for you. In particular, you want &i to resolve. You also, though, don't want %importcsv itself to resolve.
Second: you have single quotes inside single quotes, or else you have unquoted text.
The right way to do this is:
call execute(cats('%importcsv(',"&path.\&&file&i.",')'));
The goal here is to make sure %importcsv doesn't resolve - so we wrap it in single quotes - but get the macro variables to resolve - wrapped in double quotes. It would be okay for &path not to resolve (as it's a global variable), but &&file&i absolutely needs to, since it's what you are looping over.
The final ')' is unnecessary (it could be included inside the double quotes), but I like it for readability (it makes it obvious that it's the end of the macro invocation). In this case it's unimportant, but sometimes the middle bits have more parentheses in them, and it becomes unclear when you have the right number.
All this aside, you're making this much too complicated. Here:
proc sql noprint;
select * into :file1-
from FilesInFolder
where File like '%.csv'
;
quit;
Why not simply put the macro invocation itself?
proc sql noprint;
select cats('%importcsv(',"&path.\",file,')')
into :filelist separated by ' '
from FilesInFolder
where File like '%.csv'
;
quit;
&filelist
Now you have &filelist which contains all of the macro invocations, and you can just invoke them, no macro loop needed.
You can check out my presentation on Writing Code With Your Data if you want to know more about how that works.
Of course, if this is homework and you have to do the first way, it's fine, but in the real world the second way is superior unless you have > 60k characters worth of macro calls.
I obtain an error in SAS while creating a new field in a dataset recalling a numeric macro variable. Here there's an example.
data input;
input cutoff_1 cutoff_2;
datalines;
30 50
;
data db;
input outstanding;
datalines;
1000.34
2000.45
3000.90
5000.98
8000.02
;
data _null_;
set input;
call symput("perc1",cutoff_1);
call symput("perc2",cutoff_2);
run;
proc univariate data=db noprint;
var outstanding;
output out=test
pctlpts = &perc1. &perc2.
pctlpre = P_;
run;
data test2;
set test;
p_&perc1._round=round(P_&perc1.,1);
p_&perc2._round=round(P_&perc2.,1);
run;
From the log it seems that the macros &perc. are solved, but that it's not possible to use those results (30, 50) to name a new variable in the dataset test2. What am I missing?
When you ask normal SAS code to use a numeric value in a place where a character value is required (both arguments to CALL SYMPUT() require character values) then SAS will convert the number into a string using the BEST12. If the value does not require all 12 characters it will be right aligned. So you created macro variables with leading spaces. The leading spaces make no difference for generating pctlpts= option values as extra spaces will not matter there. But having the leading spaces mean you are generating code like:
p_ 30_round=round(P_ 30,1);
You should use the modern (probably over 20 years old) CALL SYMPUTX() function instead. That function will remove leading/trailing spaces from the second argument when creating the macro variable. It also accepts a numeric value as the second argument converting the number into a character string using a format more like BEST32 instead of BEST12.
call symputx("perc1",cutoff_1);
call symputx("perc2",cutoff_2);
The only cases where you should ever use the ancient CALL SYMPUT() function is when you actually need to create macro variables that contain leading and/or trailing spaces.
Other solutions are to remove the spaces in the function call
call symput("perc1",strip(put(cutoff_1,best32.)));
or after generating the macro variables.
%let perc1=&perc1;
One possible solution is to use call symputx and not call symput. With call symputx the variables created are globally defined.
I'm trying to create a folder with the character "&" in the name
I've tried the %superq function which seems to interpret correctly the macro variable
x "cd c:\Users\avibe_000\Dropbox\htdocs\Landa2018\LDPdepartments\";
data _null_;
call symput('mv1','Smith&Jones');
run;
%let testmv1=%superq(mv1);
%put &testmv1 ;
x "mkdir &testmv1";
The code works but creates a folder "Smith" and not "Smith&Jones" as expected
The DCREATE function will create a sub-folder. In macro use nrstr to pass a non-resolved value to the function. %SUPERQ is not, pre se, for value assignment, but for as-is value retrieval based on macro variable name.
filename parent 'C:\Temp';
%put NOTE: parent(path)=%qsysfunc(pathname(parent));
%let rc = %qsysfunc(DCREATE(%nrstr(Smith&Jones),parent));
filename sj 'C:\Temp\Smith&Jones';
%put NOTE: sj(path)=%qsysfunc(pathname(sj));
The macro processor tries to process macro triggers inside of double quotes. But it doesn't inside of single quotes. Also the Windows command line will need quotes around the value with an & in it.
>mkdir a&b
'b' is not recognized as an internal or external command,
operable program or batch file.
>mkdir "a&b"
So change your process to enclose the command in single quotes and the directory name in double quotes.
data _null_;
call symputX('command',quote('mkdir "Smith&Jones"',"'"));
run;
x &command ;
#Tom Nice answer.
And you just been so closed to your own way:
data _null_;
call symput('mv1','d:\Users\avibe_000\Dropbox\htdocs\Landa2018\LDPdepartments\Smith&Jones');
run;
%let testmv1=%str(%")%superq(mv1)%str(%");
%put &testmv1;
x mkdir &testmv1;
You can use the mf_mkdir macro in the SASjs macrocore library - this also uses dcreate() and recursively creates any subdirectories that are needed. It is also delivered as a macro function (so can be used pretty much anywhere in your code).
/* import and compile (or go there and copy paste into your code */
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/base/mf_mkdir.sas";
%inc mc;
/* execute */
%mf_mkdir(/tmp/this %nrstr(&) that)
result:
I need to export a data set as text file for an ancient batch process probably running on Unix. The file has one column and all fields are numeric.
I want to create a text file which emulates the way Excel creates Text (MS-DOS) files:
Saves a workbook as a tab-delimited text file for use on the MS-DOS
operating system, and ensures that tab characters, line breaks, and
other characters are interpreted correctly. Saves only the active
sheet.
What is the best way to achieve this?
DOS uses encoding page 437, which is a very limited set of characters. If you don't have any special characters, you're good. If you do have special characters, you'll need to change the encoding page to 437 in order to guarantee character compatibility. This can be done as a dataset option.
SAS internally names this pcoem437. You can see the difference in output by changing the encoding= option.
data have;
input var$;
datalines;
ElNiƱo
ElNino
;
run;
proc export data=have(encoding=pcoem437)
file='C:\Directory\want.txt'
dbms=tab
replace;
run;
If you just have one column then the delimiter doesn't matter. You can write the file using a DATA step very easily.
data _null_;
set have ;
file 'myfile.txt' ;
put VAR1 ;
run;
If you want to add an extra line with the column name then add this before the PUT statement.
if _n_=1 then put 'VAR1';
If you are worried about whether you need to generate LF or CRLF for the end of line you can control that with the TERMSTR= option on the FILE statement.
I'd like to use the following syntax
data new;
set old (where=(mystring in ('string1','string2',...,'string500')));
run;
in order to filter a very large input data set. The 500 strings at first are contained as numeric values in the variable "bbb" in the dataset "aux". So far I have created a macro variable which contains the required list of the 500 strings the following way:
proc sql noprint;
select bbb into :StringList1 separated by "',' "
from work.aux;
quit;
data _null_; call symputx('StringList2',compress("'&StringList1'")); run;
data new;
set old (where=(mystring in (&StringList2)));
run;
... which seems to work. But there is a warning telling me that
The quoted string currently being processed has become more than 262
characters long. You might have unbalanced quotation marks.
Results still seem to be plausible. Should I be worried that one day results might become wrong?
More importantly: I try to find a way to avoid using the compress function by setting up the
separated by "',' "
option in a way that does not contain blanks in the first place. Unfortunately the following seems not to work:
separated by "','"
It doesn't give me a eror message but when looking at the macro variable there is a multipage-mess of red line numbers (the color which usually denotes error messages), empty rows, minus signs, ... . The following screenshot shows part of the log after running this code:
proc sql noprint;
select vnr into :StringVar1 separated by "','"
from work.var_nr_import;
quit;
%put &StringVar1.;
Have already tried to make use of the STR()-function but no success so far.
I cannot replicate your error messages in SAS 9.3
If your variable is numeric you don't need quotes in the macro variable.
If it is character try using the QUOTE() function.
proc sql noprint;
select quote(bbb) into :StringList1 separated by " "
from work.aux;
quit;
A macro variable can only contain 65,534 characters. So if there are too many values of BBB then your macro variable value will be truncated. This could lead to unbalanced quotes. That is most likely the source of your errors.
Note that you can turn off the warning about the length of the quoted strings by using the NOQUOTELENMAX system option, but in this application you wouldn't want to because the individual quoted strings are not that long.
You will be better served to use another method to subset your data if lists this long are required.
This will work,
for double quotations
proc sql noprint;
select quote(bbb) into :StringList1 separated by ","
from work.aux;
quit;
for single quotations
proc sql noprint;
select "'"||bb||"'" into :StringList1 separated by ","
from work.aux;
quit;