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 
73 filename &inref &infile lrecl=1 recfm=n;
74 
75 data &ds1;
76  infile &inref;
77  input sourcechar $char1. @@;
78  format sourcechar hex2.;
79 run;
80 
81 data &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;
112 run;
113 
114 data &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;
134 run;
135 
136 filename &outref &outfile recfm=n;
137 
138 data _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;
148 run;
149 
150 /* END */
151 /* %put &sysmacroname took %sysevalf(%sysfunc(datetime())-&dttm) secs to run; */
152 
153 %mend mp_replace;