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