Macros for SAS Application Developers
https://github.com/sasjs/core
mp_replace.sas
Go to the documentation of this file.
1/**
2 @file
3 @brief Performs a text substitution on a file
4 @details Performs a find and replace on a file, either in place or to a new
5 file. Can be used on files where lines are longer than 32767.
6
7 Works by reading in the file byte by byte, then marking the beginning and end
8 of each matched string, before finally doing the replace.
9
10 Full credit for this highly efficient and syntactically satisfying SAS logic
11 goes to [Bartosz Jabłoński](https://www.linkedin.com/in/yabwon), founder of
12 the [SAS Packages](https://github.com/yabwon/SAS_PACKAGES) framework.
13
14 Usage:
15
16 %let file="%sysfunc(pathname(work))/file.txt";
17 %let str=replace/me;
18 %let rep=with/this;
19 data _null_;
20 file &file;
21 put 'blahblah';
22 put "blahblah&str.blah";
23 put 'blahblahblah';
24 run;
25 %mp_replace(&file, findvar=str, replacevar=rep)
26 data _null_;
27 infile &file;
28 input;
29 list;
30 run;
31
32 Note - if you are running a version of SAS that will allow the io package in
33 LUA, you can also use this macro: mp_gsubfile.sas
34
35 @param infile The QUOTED path to the file on which to perform the substitution
36 @param findvar= Macro variable NAME containing the string to search for
37 @param replacevar= Macro variable NAME containing the replacement string
38 @param outfile= (0) Optional QUOTED path to the adjusted output file (to
39 avoid overwriting the first file).
40
41 <h4> SAS Macros </h4>
42 @li mf_getuniquefileref.sas
43 @li mf_getuniquename.sas
44
45 <h4> Related Macros </h4>
46 @li mp_chop.sas
47 @li mp_gsubfile.sas
48 @li mp_replace.test.sas
49
50 @version 9.4
51 @author Bartosz Jabłoński
52 @author Allan Bowe
53**/
54
55%macro mp_replace(infile,
56 findvar=,
57 replacevar=,
58 outfile=0
59)/*/STORE SOURCE*/;
60
61%local inref dttm ds1;
62%let inref=%mf_getuniquefileref();
63%let outref=%mf_getuniquefileref();
64%if &outfile=0 %then %let outfile=&infile;
65%let ds1=%mf_getuniquename(prefix=allchars);
66%let ds2=%mf_getuniquename(prefix=startmark);
67
68/* START */
69%let dttm=%sysfunc(datetime());
70
71filename &inref &infile lrecl=1 recfm=n;
72
73data &ds1;
74 infile &inref;
75 input sourcechar $char1. @@;
76 format sourcechar hex2.;
77run;
78
79data &ds2;
80 /* set find string to length in bytes to cover trailing spaces */
81 length string $ %length(%superq(&findvar));
82 string =symget("&findvar");
83 drop string;
84
85 firstchar=char(string,1);
86 findlen=lengthm(string); /* <- for trailing bytes */
87
88 do _N_=1 to nobs;
89 set &ds1 nobs=nobs point=_N_;
90 if sourcechar=firstchar then do;
91 pos=1;
92 s=0;
93 do point=_N_ to min(_N_ + findlen -1,nobs);
94 set &ds1 point=point;
95 if sourcechar=char(string, pos) then s + 1;
96 else goto _leave_;
97 pos+1;
98 end;
99 _leave_:
100 if s=findlen then do;
101 START =_N_;
102 _N_ =_N_+ s - 1;
103 STOP =_N_;
104 output;
105 end;
106 end;
107 end;
108 stop;
109 keep START STOP;
110run;
111
112data &ds1;
113 declare hash HS(dataset:"&ds2(keep=start)");
114 HS.defineKey("start");
115 HS.defineDone();
116 declare hash HE(dataset:"&ds2(keep=stop)");
117 HE.defineKey("stop");
118 HE.defineDone();
119 do until(eof);
120 set &ds1 end=eof curobs =n;
121 start = ^HS.check(key:n);
122 stop = ^HE.check(key:n);
123 length strt $ 1;
124 strt =put(start,best. -L);
125 retain out 1;
126 if out then output;
127 if start then out=0;
128 if stop then out=1;
129 end;
130 stop;
131 keep sourcechar strt;
132run;
133
134filename &outref &outfile recfm=n;
135
136data _null_;
137 length replace $ %length(%superq(&replacevar));
138 replace=symget("&replacevar");
139 file &outref;
140 do until(eof);
141 set &ds1 end=eof;
142 if strt ="1" then put replace char.;
143 else put sourcechar char1.;
144 end;
145 stop;
146run;
147
148/* END */
149%put &sysmacroname took %sysevalf(%sysfunc(datetime())-&dttm) seconds to run;
150
151%mend mp_replace;