00001 // 00002 // Copyright 2017 The Abseil Authors. 00003 // 00004 // Licensed under the Apache License, Version 2.0 (the "License"); 00005 // you may not use this file except in compliance with the License. 00006 // You may obtain a copy of the License at 00007 // 00008 // https://www.apache.org/licenses/LICENSE-2.0 00009 // 00010 // Unless required by applicable law or agreed to in writing, software 00011 // distributed under the License is distributed on an "AS IS" BASIS, 00012 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00013 // See the License for the specific language governing permissions and 00014 // limitations under the License. 00015 // 00016 // ----------------------------------------------------------------------------- 00017 // File: str_replace.h 00018 // ----------------------------------------------------------------------------- 00019 // 00020 // This file defines `absl::StrReplaceAll()`, a general-purpose string 00021 // replacement function designed for large, arbitrary text substitutions, 00022 // especially on strings which you are receiving from some other system for 00023 // further processing (e.g. processing regular expressions, escaping HTML 00024 // entities, etc.). `StrReplaceAll` is designed to be efficient even when only 00025 // one substitution is being performed, or when substitution is rare. 00026 // 00027 // If the string being modified is known at compile-time, and the substitutions 00028 // vary, `absl::Substitute()` may be a better choice. 00029 // 00030 // Example: 00031 // 00032 // std::string html_escaped = absl::StrReplaceAll(user_input, { 00033 // {"&", "&"}, 00034 // {"<", "<"}, 00035 // {">", ">"}, 00036 // {"\"", """}, 00037 // {"'", "'"}}); 00038 #ifndef ABSL_STRINGS_STR_REPLACE_H_ 00039 #define ABSL_STRINGS_STR_REPLACE_H_ 00040 00041 #include <string> 00042 #include <utility> 00043 #include <vector> 00044 00045 #include "absl/base/attributes.h" 00046 #include "absl/strings/string_view.h" 00047 00048 namespace absl { 00049 00050 // StrReplaceAll() 00051 // 00052 // Replaces character sequences within a given string with replacements provided 00053 // within an initializer list of key/value pairs. Candidate replacements are 00054 // considered in order as they occur within the string, with earlier matches 00055 // taking precedence, and longer matches taking precedence for candidates 00056 // starting at the same position in the string. Once a substitution is made, the 00057 // replaced text is not considered for any further substitutions. 00058 // 00059 // Example: 00060 // 00061 // std::string s = absl::StrReplaceAll( 00062 // "$who bought $count #Noun. Thanks $who!", 00063 // {{"$count", absl::StrCat(5)}, 00064 // {"$who", "Bob"}, 00065 // {"#Noun", "Apples"}}); 00066 // EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s); 00067 ABSL_MUST_USE_RESULT std::string StrReplaceAll( 00068 absl::string_view s, 00069 std::initializer_list<std::pair<absl::string_view, absl::string_view>> 00070 replacements); 00071 00072 // Overload of `StrReplaceAll()` to accept a container of key/value replacement 00073 // pairs (typically either an associative map or a `std::vector` of `std::pair` 00074 // elements). A vector of pairs is generally more efficient. 00075 // 00076 // Examples: 00077 // 00078 // std::map<const absl::string_view, const absl::string_view> replacements; 00079 // replacements["$who"] = "Bob"; 00080 // replacements["$count"] = "5"; 00081 // replacements["#Noun"] = "Apples"; 00082 // std::string s = absl::StrReplaceAll( 00083 // "$who bought $count #Noun. Thanks $who!", 00084 // replacements); 00085 // EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s); 00086 // 00087 // // A std::vector of std::pair elements can be more efficient. 00088 // std::vector<std::pair<const absl::string_view, std::string>> replacements; 00089 // replacements.push_back({"&", "&"}); 00090 // replacements.push_back({"<", "<"}); 00091 // replacements.push_back({">", ">"}); 00092 // std::string s = absl::StrReplaceAll("if (ptr < &foo)", 00093 // replacements); 00094 // EXPECT_EQ("if (ptr < &foo)", s); 00095 template <typename StrToStrMapping> 00096 std::string StrReplaceAll(absl::string_view s, 00097 const StrToStrMapping& replacements); 00098 00099 // Overload of `StrReplaceAll()` to replace character sequences within a given 00100 // output string *in place* with replacements provided within an initializer 00101 // list of key/value pairs, returning the number of substitutions that occurred. 00102 // 00103 // Example: 00104 // 00105 // std::string s = std::string("$who bought $count #Noun. Thanks $who!"); 00106 // int count; 00107 // count = absl::StrReplaceAll({{"$count", absl::StrCat(5)}, 00108 // {"$who", "Bob"}, 00109 // {"#Noun", "Apples"}}, &s); 00110 // EXPECT_EQ(count, 4); 00111 // EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s); 00112 int StrReplaceAll( 00113 std::initializer_list<std::pair<absl::string_view, absl::string_view>> 00114 replacements, 00115 std::string* target); 00116 00117 // Overload of `StrReplaceAll()` to replace patterns within a given output 00118 // string *in place* with replacements provided within a container of key/value 00119 // pairs. 00120 // 00121 // Example: 00122 // 00123 // std::string s = std::string("if (ptr < &foo)"); 00124 // int count = absl::StrReplaceAll({{"&", "&"}, 00125 // {"<", "<"}, 00126 // {">", ">"}}, &s); 00127 // EXPECT_EQ(count, 2); 00128 // EXPECT_EQ("if (ptr < &foo)", s); 00129 template <typename StrToStrMapping> 00130 int StrReplaceAll(const StrToStrMapping& replacements, std::string* target); 00131 00132 // Implementation details only, past this point. 00133 namespace strings_internal { 00134 00135 struct ViableSubstitution { 00136 absl::string_view old; 00137 absl::string_view replacement; 00138 size_t offset; 00139 00140 ViableSubstitution(absl::string_view old_str, 00141 absl::string_view replacement_str, size_t offset_val) 00142 : old(old_str), replacement(replacement_str), offset(offset_val) {} 00143 00144 // One substitution occurs "before" another (takes priority) if either 00145 // it has the lowest offset, or it has the same offset but a larger size. 00146 bool OccursBefore(const ViableSubstitution& y) const { 00147 if (offset != y.offset) return offset < y.offset; 00148 return old.size() > y.old.size(); 00149 } 00150 }; 00151 00152 // Build a vector of ViableSubstitutions based on the given list of 00153 // replacements. subs can be implemented as a priority_queue. However, it turns 00154 // out that most callers have small enough a list of substitutions that the 00155 // overhead of such a queue isn't worth it. 00156 template <typename StrToStrMapping> 00157 std::vector<ViableSubstitution> FindSubstitutions( 00158 absl::string_view s, const StrToStrMapping& replacements) { 00159 std::vector<ViableSubstitution> subs; 00160 subs.reserve(replacements.size()); 00161 00162 for (const auto& rep : replacements) { 00163 using std::get; 00164 absl::string_view old(get<0>(rep)); 00165 00166 size_t pos = s.find(old); 00167 if (pos == s.npos) continue; 00168 00169 // Ignore attempts to replace "". This condition is almost never true, 00170 // but above condition is frequently true. That's why we test for this 00171 // now and not before. 00172 if (old.empty()) continue; 00173 00174 subs.emplace_back(old, get<1>(rep), pos); 00175 00176 // Insertion sort to ensure the last ViableSubstitution comes before 00177 // all the others. 00178 size_t index = subs.size(); 00179 while (--index && subs[index - 1].OccursBefore(subs[index])) { 00180 std::swap(subs[index], subs[index - 1]); 00181 } 00182 } 00183 return subs; 00184 } 00185 00186 int ApplySubstitutions(absl::string_view s, 00187 std::vector<ViableSubstitution>* subs_ptr, 00188 std::string* result_ptr); 00189 00190 } // namespace strings_internal 00191 00192 template <typename StrToStrMapping> 00193 std::string StrReplaceAll(absl::string_view s, 00194 const StrToStrMapping& replacements) { 00195 auto subs = strings_internal::FindSubstitutions(s, replacements); 00196 std::string result; 00197 result.reserve(s.size()); 00198 strings_internal::ApplySubstitutions(s, &subs, &result); 00199 return result; 00200 } 00201 00202 template <typename StrToStrMapping> 00203 int StrReplaceAll(const StrToStrMapping& replacements, std::string* target) { 00204 auto subs = strings_internal::FindSubstitutions(*target, replacements); 00205 if (subs.empty()) return 0; 00206 00207 std::string result; 00208 result.reserve(target->size()); 00209 int substitutions = 00210 strings_internal::ApplySubstitutions(*target, &subs, &result); 00211 target->swap(result); 00212 return substitutions; 00213 } 00214 00215 } // namespace absl 00216 00217 #endif // ABSL_STRINGS_STR_REPLACE_H_