Macros for SAS Application Developers
https://github.com/sasjs/core
Loading...
Searching...
No Matches
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 [in] infile The QUOTED path to the file on which to perform the
36 substitution. Note that you can extract the pathname from a fileref using
37 the pathname function, eg: `"%sysfunc(pathname(fref))"`;
38 @param [in] findvar= Macro variable NAME containing the string to search for
39 @param [in] replacevar= Macro variable NAME containing the replacement string
40 @param [out] outfile= (0) Optional QUOTED path to the adjusted output file (to
41 avoid overwriting the first file).
42
43 <h4> SAS Macros </h4>
44 @li mf_getuniquefileref.sas
45 @li mf_getuniquename.sas
46
47 <h4> Related Macros </h4>
48 @li mp_chop.sas
49 @li mp_gsubfile.sas
50 @li mp_replace.test.sas
51
52 @version 9.4
53 @author Bartosz Jabłoński
54 @author Allan Bowe
55**/
56
57%macro mp_replace(infile,
58 findvar=,
59 replacevar=,
60 outfile=0
61)/*/STORE SOURCE*/;
62
63%local inref dttm ds1;
64%let inref=%mf_getuniquefileref();
65%let outref=%mf_getuniquefileref();
66%if &outfile=0 %then %let outfile=&infile;
67%let ds1=%mf_getuniquename(prefix=allchars);
68%let ds2=%mf_getuniquename(prefix=startmark);
69
70/* START */
71%let dttm=%sysfunc(datetime());
72
73filename &inref &infile lrecl=1 recfm=n;
74
75data &ds1;
76 infile &inref;
77 input sourcechar $char1. @@;
78 format sourcechar hex2.;
79run;
80
81data &ds2;
82 /* set find string to length in bytes to cover trailing spaces */
83 length string $ %length(%superq(&findvar));
84 string =symget("&findvar");
85 drop string;
86
87 firstchar=char(string,1);
88 findlen=lengthm(string); /* <- for trailing bytes */
89
90 do _N_=1 to nobs;
91 set &ds1 nobs=nobs point=_N_;
92 if sourcechar=firstchar then do;
93 pos=1;
94 s=0;
95 do point=_N_ to min(_N_ + findlen -1,nobs);
96 set &ds1 point=point;
97 if sourcechar=char(string, pos) then s + 1;
98 else goto _leave_;
99 pos+1;
100 end;
101 _leave_:
102 if s=findlen then do;
103 START =_N_;
104 _N_ =_N_+ s - 1;
105 STOP =_N_;
106 output;
107 end;
108 end;
109 end;
110 stop;
111 keep START STOP;
112run;
113
114data &ds1;
115 declare hash HS(dataset:"&ds2(keep=start)");
116 HS.defineKey("start");
117 HS.defineDone();
118 declare hash HE(dataset:"&ds2(keep=stop)");
119 HE.defineKey("stop");
120 HE.defineDone();
121 do until(eof);
122 set &ds1 end=eof curobs =n;
123 start = ^HS.check(key:n);
124 stop = ^HE.check(key:n);
125 length strt $ 1;
126 strt =put(start,best. -L);
127 retain out 1;
128 if out then output;
129 if start then out=0;
130 if stop then out=1;
131 end;
132 stop;
133 keep sourcechar strt;
134run;
135
136filename &outref &outfile recfm=n;
137
138data _null_;
139 length replace $ %length(%superq(&replacevar));
140 replace=symget("&replacevar");
141 file &outref;
142 do until(eof);
143 set &ds1 end=eof;
144 if strt ="1" then put replace char.;
145 else put sourcechar char1.;
146 end;
147 stop;
148run;
149
150/* END */
151%put &sysmacroname took %sysevalf(%sysfunc(datetime())-&dttm) seconds to run;
152
153%mend mp_replace;