1 // Copyright 2017 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "absl/strings/str_replace.h"
16
17 #include <list>
18 #include <map>
19 #include <string>
20 #include <tuple>
21 #include <utility>
22 #include <vector>
23
24 #include "gtest/gtest.h"
25 #include "absl/strings/str_cat.h"
26 #include "absl/strings/str_split.h"
27 #include "absl/strings/string_view.h"
28
TEST(StrReplaceAll,OneReplacement)29 TEST(StrReplaceAll, OneReplacement) {
30 std::string s;
31
32 // Empty string.
33 s = absl::StrReplaceAll(s, {{"", ""}});
34 EXPECT_EQ(s, "");
35 s = absl::StrReplaceAll(s, {{"x", ""}});
36 EXPECT_EQ(s, "");
37 s = absl::StrReplaceAll(s, {{"", "y"}});
38 EXPECT_EQ(s, "");
39 s = absl::StrReplaceAll(s, {{"x", "y"}});
40 EXPECT_EQ(s, "");
41
42 // Empty substring.
43 s = absl::StrReplaceAll("abc", {{"", ""}});
44 EXPECT_EQ(s, "abc");
45 s = absl::StrReplaceAll("abc", {{"", "y"}});
46 EXPECT_EQ(s, "abc");
47 s = absl::StrReplaceAll("abc", {{"x", ""}});
48 EXPECT_EQ(s, "abc");
49
50 // Substring not found.
51 s = absl::StrReplaceAll("abc", {{"xyz", "123"}});
52 EXPECT_EQ(s, "abc");
53
54 // Replace entire string.
55 s = absl::StrReplaceAll("abc", {{"abc", "xyz"}});
56 EXPECT_EQ(s, "xyz");
57
58 // Replace once at the start.
59 s = absl::StrReplaceAll("abc", {{"a", "x"}});
60 EXPECT_EQ(s, "xbc");
61
62 // Replace once in the middle.
63 s = absl::StrReplaceAll("abc", {{"b", "x"}});
64 EXPECT_EQ(s, "axc");
65
66 // Replace once at the end.
67 s = absl::StrReplaceAll("abc", {{"c", "x"}});
68 EXPECT_EQ(s, "abx");
69
70 // Replace multiple times with varying lengths of original/replacement.
71 s = absl::StrReplaceAll("ababa", {{"a", "xxx"}});
72 EXPECT_EQ(s, "xxxbxxxbxxx");
73
74 s = absl::StrReplaceAll("ababa", {{"b", "xxx"}});
75 EXPECT_EQ(s, "axxxaxxxa");
76
77 s = absl::StrReplaceAll("aaabaaabaaa", {{"aaa", "x"}});
78 EXPECT_EQ(s, "xbxbx");
79
80 s = absl::StrReplaceAll("abbbabbba", {{"bbb", "x"}});
81 EXPECT_EQ(s, "axaxa");
82
83 // Overlapping matches are replaced greedily.
84 s = absl::StrReplaceAll("aaa", {{"aa", "x"}});
85 EXPECT_EQ(s, "xa");
86
87 // The replacements are not recursive.
88 s = absl::StrReplaceAll("aaa", {{"aa", "a"}});
89 EXPECT_EQ(s, "aa");
90 }
91
TEST(StrReplaceAll,ManyReplacements)92 TEST(StrReplaceAll, ManyReplacements) {
93 std::string s;
94
95 // Empty string.
96 s = absl::StrReplaceAll("", {{"", ""}, {"x", ""}, {"", "y"}, {"x", "y"}});
97 EXPECT_EQ(s, "");
98
99 // Empty substring.
100 s = absl::StrReplaceAll("abc", {{"", ""}, {"", "y"}, {"x", ""}});
101 EXPECT_EQ(s, "abc");
102
103 // Replace entire string, one char at a time
104 s = absl::StrReplaceAll("abc", {{"a", "x"}, {"b", "y"}, {"c", "z"}});
105 EXPECT_EQ(s, "xyz");
106 s = absl::StrReplaceAll("zxy", {{"z", "x"}, {"x", "y"}, {"y", "z"}});
107 EXPECT_EQ(s, "xyz");
108
109 // Replace once at the start (longer matches take precedence)
110 s = absl::StrReplaceAll("abc", {{"a", "x"}, {"ab", "xy"}, {"abc", "xyz"}});
111 EXPECT_EQ(s, "xyz");
112
113 // Replace once in the middle.
114 s = absl::StrReplaceAll(
115 "Abc!", {{"a", "x"}, {"ab", "xy"}, {"b", "y"}, {"bc", "yz"}, {"c", "z"}});
116 EXPECT_EQ(s, "Ayz!");
117
118 // Replace once at the end.
119 s = absl::StrReplaceAll(
120 "Abc!",
121 {{"a", "x"}, {"ab", "xy"}, {"b", "y"}, {"bc!", "yz?"}, {"c!", "z;"}});
122 EXPECT_EQ(s, "Ayz?");
123
124 // Replace multiple times with varying lengths of original/replacement.
125 s = absl::StrReplaceAll("ababa", {{"a", "xxx"}, {"b", "XXXX"}});
126 EXPECT_EQ(s, "xxxXXXXxxxXXXXxxx");
127
128 // Overlapping matches are replaced greedily.
129 s = absl::StrReplaceAll("aaa", {{"aa", "x"}, {"a", "X"}});
130 EXPECT_EQ(s, "xX");
131 s = absl::StrReplaceAll("aaa", {{"a", "X"}, {"aa", "x"}});
132 EXPECT_EQ(s, "xX");
133
134 // Two well-known sentences
135 s = absl::StrReplaceAll("the quick brown fox jumped over the lazy dogs",
136 {
137 {"brown", "box"},
138 {"dogs", "jugs"},
139 {"fox", "with"},
140 {"jumped", "five"},
141 {"over", "dozen"},
142 {"quick", "my"},
143 {"the", "pack"},
144 {"the lazy", "liquor"},
145 });
146 EXPECT_EQ(s, "pack my box with five dozen liquor jugs");
147 }
148
TEST(StrReplaceAll,ManyReplacementsInMap)149 TEST(StrReplaceAll, ManyReplacementsInMap) {
150 std::map<const char *, const char *> replacements;
151 replacements["$who"] = "Bob";
152 replacements["$count"] = "5";
153 replacements["#Noun"] = "Apples";
154 std::string s = absl::StrReplaceAll("$who bought $count #Noun. Thanks $who!",
155 replacements);
156 EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s);
157 }
158
TEST(StrReplaceAll,ReplacementsInPlace)159 TEST(StrReplaceAll, ReplacementsInPlace) {
160 std::string s = std::string("$who bought $count #Noun. Thanks $who!");
161 int count;
162 count = absl::StrReplaceAll({{"$count", absl::StrCat(5)},
163 {"$who", "Bob"},
164 {"#Noun", "Apples"}}, &s);
165 EXPECT_EQ(count, 4);
166 EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s);
167 }
168
TEST(StrReplaceAll,ReplacementsInPlaceInMap)169 TEST(StrReplaceAll, ReplacementsInPlaceInMap) {
170 std::string s = std::string("$who bought $count #Noun. Thanks $who!");
171 std::map<absl::string_view, absl::string_view> replacements;
172 replacements["$who"] = "Bob";
173 replacements["$count"] = "5";
174 replacements["#Noun"] = "Apples";
175 int count;
176 count = absl::StrReplaceAll(replacements, &s);
177 EXPECT_EQ(count, 4);
178 EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s);
179 }
180
181 struct Cont {
182 Cont() = default;
ContCont183 explicit Cont(absl::string_view src) : data(src) {}
184
185 absl::string_view data;
186 };
187
188 template <int index>
get(const Cont & c)189 absl::string_view get(const Cont& c) {
190 auto splitter = absl::StrSplit(c.data, ':');
191 auto it = splitter.begin();
192 for (int i = 0; i < index; ++i) ++it;
193
194 return *it;
195 }
196
TEST(StrReplaceAll,VariableNumber)197 TEST(StrReplaceAll, VariableNumber) {
198 std::string s;
199 {
200 std::vector<std::pair<std::string, std::string>> replacements;
201
202 s = "abc";
203 EXPECT_EQ(0, absl::StrReplaceAll(replacements, &s));
204 EXPECT_EQ("abc", s);
205
206 s = "abc";
207 replacements.push_back({"a", "A"});
208 EXPECT_EQ(1, absl::StrReplaceAll(replacements, &s));
209 EXPECT_EQ("Abc", s);
210
211 s = "abc";
212 replacements.push_back({"b", "B"});
213 EXPECT_EQ(2, absl::StrReplaceAll(replacements, &s));
214 EXPECT_EQ("ABc", s);
215
216 s = "abc";
217 replacements.push_back({"d", "D"});
218 EXPECT_EQ(2, absl::StrReplaceAll(replacements, &s));
219 EXPECT_EQ("ABc", s);
220
221 EXPECT_EQ("ABcABc", absl::StrReplaceAll("abcabc", replacements));
222 }
223
224 {
225 std::map<const char*, const char*> replacements;
226 replacements["aa"] = "x";
227 replacements["a"] = "X";
228 s = "aaa";
229 EXPECT_EQ(2, absl::StrReplaceAll(replacements, &s));
230 EXPECT_EQ("xX", s);
231
232 EXPECT_EQ("xxX", absl::StrReplaceAll("aaaaa", replacements));
233 }
234
235 {
236 std::list<std::pair<absl::string_view, absl::string_view>> replacements = {
237 {"a", "x"}, {"b", "y"}, {"c", "z"}};
238
239 std::string s = absl::StrReplaceAll("abc", replacements);
240 EXPECT_EQ(s, "xyz");
241 }
242
243 {
244 using X = std::tuple<absl::string_view, std::string, int>;
245 std::vector<X> replacements(3);
246 replacements[0] = X{"a", "x", 1};
247 replacements[1] = X{"b", "y", 0};
248 replacements[2] = X{"c", "z", -1};
249
250 std::string s = absl::StrReplaceAll("abc", replacements);
251 EXPECT_EQ(s, "xyz");
252 }
253
254 {
255 std::vector<Cont> replacements(3);
256 replacements[0] = Cont{"a:x"};
257 replacements[1] = Cont{"b:y"};
258 replacements[2] = Cont{"c:z"};
259
260 std::string s = absl::StrReplaceAll("abc", replacements);
261 EXPECT_EQ(s, "xyz");
262 }
263 }
264
265 // Same as above, but using the in-place variant of absl::StrReplaceAll,
266 // that returns the # of replacements performed.
TEST(StrReplaceAll,Inplace)267 TEST(StrReplaceAll, Inplace) {
268 std::string s;
269 int reps;
270
271 // Empty string.
272 s = "";
273 reps = absl::StrReplaceAll({{"", ""}, {"x", ""}, {"", "y"}, {"x", "y"}}, &s);
274 EXPECT_EQ(reps, 0);
275 EXPECT_EQ(s, "");
276
277 // Empty substring.
278 s = "abc";
279 reps = absl::StrReplaceAll({{"", ""}, {"", "y"}, {"x", ""}}, &s);
280 EXPECT_EQ(reps, 0);
281 EXPECT_EQ(s, "abc");
282
283 // Replace entire string, one char at a time
284 s = "abc";
285 reps = absl::StrReplaceAll({{"a", "x"}, {"b", "y"}, {"c", "z"}}, &s);
286 EXPECT_EQ(reps, 3);
287 EXPECT_EQ(s, "xyz");
288 s = "zxy";
289 reps = absl::StrReplaceAll({{"z", "x"}, {"x", "y"}, {"y", "z"}}, &s);
290 EXPECT_EQ(reps, 3);
291 EXPECT_EQ(s, "xyz");
292
293 // Replace once at the start (longer matches take precedence)
294 s = "abc";
295 reps = absl::StrReplaceAll({{"a", "x"}, {"ab", "xy"}, {"abc", "xyz"}}, &s);
296 EXPECT_EQ(reps, 1);
297 EXPECT_EQ(s, "xyz");
298
299 // Replace once in the middle.
300 s = "Abc!";
301 reps = absl::StrReplaceAll(
302 {{"a", "x"}, {"ab", "xy"}, {"b", "y"}, {"bc", "yz"}, {"c", "z"}}, &s);
303 EXPECT_EQ(reps, 1);
304 EXPECT_EQ(s, "Ayz!");
305
306 // Replace once at the end.
307 s = "Abc!";
308 reps = absl::StrReplaceAll(
309 {{"a", "x"}, {"ab", "xy"}, {"b", "y"}, {"bc!", "yz?"}, {"c!", "z;"}}, &s);
310 EXPECT_EQ(reps, 1);
311 EXPECT_EQ(s, "Ayz?");
312
313 // Replace multiple times with varying lengths of original/replacement.
314 s = "ababa";
315 reps = absl::StrReplaceAll({{"a", "xxx"}, {"b", "XXXX"}}, &s);
316 EXPECT_EQ(reps, 5);
317 EXPECT_EQ(s, "xxxXXXXxxxXXXXxxx");
318
319 // Overlapping matches are replaced greedily.
320 s = "aaa";
321 reps = absl::StrReplaceAll({{"aa", "x"}, {"a", "X"}}, &s);
322 EXPECT_EQ(reps, 2);
323 EXPECT_EQ(s, "xX");
324 s = "aaa";
325 reps = absl::StrReplaceAll({{"a", "X"}, {"aa", "x"}}, &s);
326 EXPECT_EQ(reps, 2);
327 EXPECT_EQ(s, "xX");
328
329 // Two well-known sentences
330 s = "the quick brown fox jumped over the lazy dogs";
331 reps = absl::StrReplaceAll(
332 {
333 {"brown", "box"},
334 {"dogs", "jugs"},
335 {"fox", "with"},
336 {"jumped", "five"},
337 {"over", "dozen"},
338 {"quick", "my"},
339 {"the", "pack"},
340 {"the lazy", "liquor"},
341 },
342 &s);
343 EXPECT_EQ(reps, 8);
344 EXPECT_EQ(s, "pack my box with five dozen liquor jugs");
345 }
346