str_replace_test.cc (10255B)
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 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 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 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 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 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; 183 explicit Cont(absl::string_view src) : data(src) {} 184 185 absl::string_view data; 186 }; 187 188 template <int index> 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 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. 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 }