1*9356374aSAndroid Build Coastguard Worker //
2*9356374aSAndroid Build Coastguard Worker // Copyright 2017 The Abseil Authors.
3*9356374aSAndroid Build Coastguard Worker //
4*9356374aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
5*9356374aSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
6*9356374aSAndroid Build Coastguard Worker // You may obtain a copy of the License at
7*9356374aSAndroid Build Coastguard Worker //
8*9356374aSAndroid Build Coastguard Worker // https://www.apache.org/licenses/LICENSE-2.0
9*9356374aSAndroid Build Coastguard Worker //
10*9356374aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
11*9356374aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
12*9356374aSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*9356374aSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
14*9356374aSAndroid Build Coastguard Worker // limitations under the License.
15*9356374aSAndroid Build Coastguard Worker //
16*9356374aSAndroid Build Coastguard Worker // -----------------------------------------------------------------------------
17*9356374aSAndroid Build Coastguard Worker // File: str_replace.h
18*9356374aSAndroid Build Coastguard Worker // -----------------------------------------------------------------------------
19*9356374aSAndroid Build Coastguard Worker //
20*9356374aSAndroid Build Coastguard Worker // This file defines `absl::StrReplaceAll()`, a general-purpose string
21*9356374aSAndroid Build Coastguard Worker // replacement function designed for large, arbitrary text substitutions,
22*9356374aSAndroid Build Coastguard Worker // especially on strings which you are receiving from some other system for
23*9356374aSAndroid Build Coastguard Worker // further processing (e.g. processing regular expressions, escaping HTML
24*9356374aSAndroid Build Coastguard Worker // entities, etc.). `StrReplaceAll` is designed to be efficient even when only
25*9356374aSAndroid Build Coastguard Worker // one substitution is being performed, or when substitution is rare.
26*9356374aSAndroid Build Coastguard Worker //
27*9356374aSAndroid Build Coastguard Worker // If the string being modified is known at compile-time, and the substitutions
28*9356374aSAndroid Build Coastguard Worker // vary, `absl::Substitute()` may be a better choice.
29*9356374aSAndroid Build Coastguard Worker //
30*9356374aSAndroid Build Coastguard Worker // Example:
31*9356374aSAndroid Build Coastguard Worker //
32*9356374aSAndroid Build Coastguard Worker // std::string html_escaped = absl::StrReplaceAll(user_input, {
33*9356374aSAndroid Build Coastguard Worker // {"&", "&"},
34*9356374aSAndroid Build Coastguard Worker // {"<", "<"},
35*9356374aSAndroid Build Coastguard Worker // {">", ">"},
36*9356374aSAndroid Build Coastguard Worker // {"\"", """},
37*9356374aSAndroid Build Coastguard Worker // {"'", "'"}});
38*9356374aSAndroid Build Coastguard Worker #ifndef ABSL_STRINGS_STR_REPLACE_H_
39*9356374aSAndroid Build Coastguard Worker #define ABSL_STRINGS_STR_REPLACE_H_
40*9356374aSAndroid Build Coastguard Worker
41*9356374aSAndroid Build Coastguard Worker #include <string>
42*9356374aSAndroid Build Coastguard Worker #include <utility>
43*9356374aSAndroid Build Coastguard Worker #include <vector>
44*9356374aSAndroid Build Coastguard Worker
45*9356374aSAndroid Build Coastguard Worker #include "absl/base/attributes.h"
46*9356374aSAndroid Build Coastguard Worker #include "absl/base/nullability.h"
47*9356374aSAndroid Build Coastguard Worker #include "absl/strings/string_view.h"
48*9356374aSAndroid Build Coastguard Worker
49*9356374aSAndroid Build Coastguard Worker namespace absl {
50*9356374aSAndroid Build Coastguard Worker ABSL_NAMESPACE_BEGIN
51*9356374aSAndroid Build Coastguard Worker
52*9356374aSAndroid Build Coastguard Worker // StrReplaceAll()
53*9356374aSAndroid Build Coastguard Worker //
54*9356374aSAndroid Build Coastguard Worker // Replaces character sequences within a given string with replacements provided
55*9356374aSAndroid Build Coastguard Worker // within an initializer list of key/value pairs. Candidate replacements are
56*9356374aSAndroid Build Coastguard Worker // considered in order as they occur within the string, with earlier matches
57*9356374aSAndroid Build Coastguard Worker // taking precedence, and longer matches taking precedence for candidates
58*9356374aSAndroid Build Coastguard Worker // starting at the same position in the string. Once a substitution is made, the
59*9356374aSAndroid Build Coastguard Worker // replaced text is not considered for any further substitutions.
60*9356374aSAndroid Build Coastguard Worker //
61*9356374aSAndroid Build Coastguard Worker // Example:
62*9356374aSAndroid Build Coastguard Worker //
63*9356374aSAndroid Build Coastguard Worker // std::string s = absl::StrReplaceAll(
64*9356374aSAndroid Build Coastguard Worker // "$who bought $count #Noun. Thanks $who!",
65*9356374aSAndroid Build Coastguard Worker // {{"$count", absl::StrCat(5)},
66*9356374aSAndroid Build Coastguard Worker // {"$who", "Bob"},
67*9356374aSAndroid Build Coastguard Worker // {"#Noun", "Apples"}});
68*9356374aSAndroid Build Coastguard Worker // EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s);
69*9356374aSAndroid Build Coastguard Worker ABSL_MUST_USE_RESULT std::string StrReplaceAll(
70*9356374aSAndroid Build Coastguard Worker absl::string_view s,
71*9356374aSAndroid Build Coastguard Worker std::initializer_list<std::pair<absl::string_view, absl::string_view>>
72*9356374aSAndroid Build Coastguard Worker replacements);
73*9356374aSAndroid Build Coastguard Worker
74*9356374aSAndroid Build Coastguard Worker // Overload of `StrReplaceAll()` to accept a container of key/value replacement
75*9356374aSAndroid Build Coastguard Worker // pairs (typically either an associative map or a `std::vector` of `std::pair`
76*9356374aSAndroid Build Coastguard Worker // elements). A vector of pairs is generally more efficient.
77*9356374aSAndroid Build Coastguard Worker //
78*9356374aSAndroid Build Coastguard Worker // Examples:
79*9356374aSAndroid Build Coastguard Worker //
80*9356374aSAndroid Build Coastguard Worker // std::map<const absl::string_view, const absl::string_view> replacements;
81*9356374aSAndroid Build Coastguard Worker // replacements["$who"] = "Bob";
82*9356374aSAndroid Build Coastguard Worker // replacements["$count"] = "5";
83*9356374aSAndroid Build Coastguard Worker // replacements["#Noun"] = "Apples";
84*9356374aSAndroid Build Coastguard Worker // std::string s = absl::StrReplaceAll(
85*9356374aSAndroid Build Coastguard Worker // "$who bought $count #Noun. Thanks $who!",
86*9356374aSAndroid Build Coastguard Worker // replacements);
87*9356374aSAndroid Build Coastguard Worker // EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s);
88*9356374aSAndroid Build Coastguard Worker //
89*9356374aSAndroid Build Coastguard Worker // // A std::vector of std::pair elements can be more efficient.
90*9356374aSAndroid Build Coastguard Worker // std::vector<std::pair<const absl::string_view, std::string>> replacements;
91*9356374aSAndroid Build Coastguard Worker // replacements.push_back({"&", "&"});
92*9356374aSAndroid Build Coastguard Worker // replacements.push_back({"<", "<"});
93*9356374aSAndroid Build Coastguard Worker // replacements.push_back({">", ">"});
94*9356374aSAndroid Build Coastguard Worker // std::string s = absl::StrReplaceAll("if (ptr < &foo)",
95*9356374aSAndroid Build Coastguard Worker // replacements);
96*9356374aSAndroid Build Coastguard Worker // EXPECT_EQ("if (ptr < &foo)", s);
97*9356374aSAndroid Build Coastguard Worker template <typename StrToStrMapping>
98*9356374aSAndroid Build Coastguard Worker std::string StrReplaceAll(absl::string_view s,
99*9356374aSAndroid Build Coastguard Worker const StrToStrMapping& replacements);
100*9356374aSAndroid Build Coastguard Worker
101*9356374aSAndroid Build Coastguard Worker // Overload of `StrReplaceAll()` to replace character sequences within a given
102*9356374aSAndroid Build Coastguard Worker // output string *in place* with replacements provided within an initializer
103*9356374aSAndroid Build Coastguard Worker // list of key/value pairs, returning the number of substitutions that occurred.
104*9356374aSAndroid Build Coastguard Worker //
105*9356374aSAndroid Build Coastguard Worker // Example:
106*9356374aSAndroid Build Coastguard Worker //
107*9356374aSAndroid Build Coastguard Worker // std::string s = std::string("$who bought $count #Noun. Thanks $who!");
108*9356374aSAndroid Build Coastguard Worker // int count;
109*9356374aSAndroid Build Coastguard Worker // count = absl::StrReplaceAll({{"$count", absl::StrCat(5)},
110*9356374aSAndroid Build Coastguard Worker // {"$who", "Bob"},
111*9356374aSAndroid Build Coastguard Worker // {"#Noun", "Apples"}}, &s);
112*9356374aSAndroid Build Coastguard Worker // EXPECT_EQ(count, 4);
113*9356374aSAndroid Build Coastguard Worker // EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s);
114*9356374aSAndroid Build Coastguard Worker int StrReplaceAll(
115*9356374aSAndroid Build Coastguard Worker std::initializer_list<std::pair<absl::string_view, absl::string_view>>
116*9356374aSAndroid Build Coastguard Worker replacements,
117*9356374aSAndroid Build Coastguard Worker absl::Nonnull<std::string*> target);
118*9356374aSAndroid Build Coastguard Worker
119*9356374aSAndroid Build Coastguard Worker // Overload of `StrReplaceAll()` to replace patterns within a given output
120*9356374aSAndroid Build Coastguard Worker // string *in place* with replacements provided within a container of key/value
121*9356374aSAndroid Build Coastguard Worker // pairs.
122*9356374aSAndroid Build Coastguard Worker //
123*9356374aSAndroid Build Coastguard Worker // Example:
124*9356374aSAndroid Build Coastguard Worker //
125*9356374aSAndroid Build Coastguard Worker // std::string s = std::string("if (ptr < &foo)");
126*9356374aSAndroid Build Coastguard Worker // int count = absl::StrReplaceAll({{"&", "&"},
127*9356374aSAndroid Build Coastguard Worker // {"<", "<"},
128*9356374aSAndroid Build Coastguard Worker // {">", ">"}}, &s);
129*9356374aSAndroid Build Coastguard Worker // EXPECT_EQ(count, 2);
130*9356374aSAndroid Build Coastguard Worker // EXPECT_EQ("if (ptr < &foo)", s);
131*9356374aSAndroid Build Coastguard Worker template <typename StrToStrMapping>
132*9356374aSAndroid Build Coastguard Worker int StrReplaceAll(const StrToStrMapping& replacements,
133*9356374aSAndroid Build Coastguard Worker absl::Nonnull<std::string*> target);
134*9356374aSAndroid Build Coastguard Worker
135*9356374aSAndroid Build Coastguard Worker // Implementation details only, past this point.
136*9356374aSAndroid Build Coastguard Worker namespace strings_internal {
137*9356374aSAndroid Build Coastguard Worker
138*9356374aSAndroid Build Coastguard Worker struct ViableSubstitution {
139*9356374aSAndroid Build Coastguard Worker absl::string_view old;
140*9356374aSAndroid Build Coastguard Worker absl::string_view replacement;
141*9356374aSAndroid Build Coastguard Worker size_t offset;
142*9356374aSAndroid Build Coastguard Worker
ViableSubstitutionViableSubstitution143*9356374aSAndroid Build Coastguard Worker ViableSubstitution(absl::string_view old_str,
144*9356374aSAndroid Build Coastguard Worker absl::string_view replacement_str, size_t offset_val)
145*9356374aSAndroid Build Coastguard Worker : old(old_str), replacement(replacement_str), offset(offset_val) {}
146*9356374aSAndroid Build Coastguard Worker
147*9356374aSAndroid Build Coastguard Worker // One substitution occurs "before" another (takes priority) if either
148*9356374aSAndroid Build Coastguard Worker // it has the lowest offset, or it has the same offset but a larger size.
OccursBeforeViableSubstitution149*9356374aSAndroid Build Coastguard Worker bool OccursBefore(const ViableSubstitution& y) const {
150*9356374aSAndroid Build Coastguard Worker if (offset != y.offset) return offset < y.offset;
151*9356374aSAndroid Build Coastguard Worker return old.size() > y.old.size();
152*9356374aSAndroid Build Coastguard Worker }
153*9356374aSAndroid Build Coastguard Worker };
154*9356374aSAndroid Build Coastguard Worker
155*9356374aSAndroid Build Coastguard Worker // Build a vector of ViableSubstitutions based on the given list of
156*9356374aSAndroid Build Coastguard Worker // replacements. subs can be implemented as a priority_queue. However, it turns
157*9356374aSAndroid Build Coastguard Worker // out that most callers have small enough a list of substitutions that the
158*9356374aSAndroid Build Coastguard Worker // overhead of such a queue isn't worth it.
159*9356374aSAndroid Build Coastguard Worker template <typename StrToStrMapping>
FindSubstitutions(absl::string_view s,const StrToStrMapping & replacements)160*9356374aSAndroid Build Coastguard Worker std::vector<ViableSubstitution> FindSubstitutions(
161*9356374aSAndroid Build Coastguard Worker absl::string_view s, const StrToStrMapping& replacements) {
162*9356374aSAndroid Build Coastguard Worker std::vector<ViableSubstitution> subs;
163*9356374aSAndroid Build Coastguard Worker subs.reserve(replacements.size());
164*9356374aSAndroid Build Coastguard Worker
165*9356374aSAndroid Build Coastguard Worker for (const auto& rep : replacements) {
166*9356374aSAndroid Build Coastguard Worker using std::get;
167*9356374aSAndroid Build Coastguard Worker absl::string_view old(get<0>(rep));
168*9356374aSAndroid Build Coastguard Worker
169*9356374aSAndroid Build Coastguard Worker size_t pos = s.find(old);
170*9356374aSAndroid Build Coastguard Worker if (pos == s.npos) continue;
171*9356374aSAndroid Build Coastguard Worker
172*9356374aSAndroid Build Coastguard Worker // Ignore attempts to replace "". This condition is almost never true,
173*9356374aSAndroid Build Coastguard Worker // but above condition is frequently true. That's why we test for this
174*9356374aSAndroid Build Coastguard Worker // now and not before.
175*9356374aSAndroid Build Coastguard Worker if (old.empty()) continue;
176*9356374aSAndroid Build Coastguard Worker
177*9356374aSAndroid Build Coastguard Worker subs.emplace_back(old, get<1>(rep), pos);
178*9356374aSAndroid Build Coastguard Worker
179*9356374aSAndroid Build Coastguard Worker // Insertion sort to ensure the last ViableSubstitution comes before
180*9356374aSAndroid Build Coastguard Worker // all the others.
181*9356374aSAndroid Build Coastguard Worker size_t index = subs.size();
182*9356374aSAndroid Build Coastguard Worker while (--index && subs[index - 1].OccursBefore(subs[index])) {
183*9356374aSAndroid Build Coastguard Worker std::swap(subs[index], subs[index - 1]);
184*9356374aSAndroid Build Coastguard Worker }
185*9356374aSAndroid Build Coastguard Worker }
186*9356374aSAndroid Build Coastguard Worker return subs;
187*9356374aSAndroid Build Coastguard Worker }
188*9356374aSAndroid Build Coastguard Worker
189*9356374aSAndroid Build Coastguard Worker int ApplySubstitutions(absl::string_view s,
190*9356374aSAndroid Build Coastguard Worker absl::Nonnull<std::vector<ViableSubstitution>*> subs_ptr,
191*9356374aSAndroid Build Coastguard Worker absl::Nonnull<std::string*> result_ptr);
192*9356374aSAndroid Build Coastguard Worker
193*9356374aSAndroid Build Coastguard Worker } // namespace strings_internal
194*9356374aSAndroid Build Coastguard Worker
195*9356374aSAndroid Build Coastguard Worker template <typename StrToStrMapping>
StrReplaceAll(absl::string_view s,const StrToStrMapping & replacements)196*9356374aSAndroid Build Coastguard Worker std::string StrReplaceAll(absl::string_view s,
197*9356374aSAndroid Build Coastguard Worker const StrToStrMapping& replacements) {
198*9356374aSAndroid Build Coastguard Worker auto subs = strings_internal::FindSubstitutions(s, replacements);
199*9356374aSAndroid Build Coastguard Worker std::string result;
200*9356374aSAndroid Build Coastguard Worker result.reserve(s.size());
201*9356374aSAndroid Build Coastguard Worker strings_internal::ApplySubstitutions(s, &subs, &result);
202*9356374aSAndroid Build Coastguard Worker return result;
203*9356374aSAndroid Build Coastguard Worker }
204*9356374aSAndroid Build Coastguard Worker
205*9356374aSAndroid Build Coastguard Worker template <typename StrToStrMapping>
StrReplaceAll(const StrToStrMapping & replacements,absl::Nonnull<std::string * > target)206*9356374aSAndroid Build Coastguard Worker int StrReplaceAll(const StrToStrMapping& replacements,
207*9356374aSAndroid Build Coastguard Worker absl::Nonnull<std::string*> target) {
208*9356374aSAndroid Build Coastguard Worker auto subs = strings_internal::FindSubstitutions(*target, replacements);
209*9356374aSAndroid Build Coastguard Worker if (subs.empty()) return 0;
210*9356374aSAndroid Build Coastguard Worker
211*9356374aSAndroid Build Coastguard Worker std::string result;
212*9356374aSAndroid Build Coastguard Worker result.reserve(target->size());
213*9356374aSAndroid Build Coastguard Worker int substitutions =
214*9356374aSAndroid Build Coastguard Worker strings_internal::ApplySubstitutions(*target, &subs, &result);
215*9356374aSAndroid Build Coastguard Worker target->swap(result);
216*9356374aSAndroid Build Coastguard Worker return substitutions;
217*9356374aSAndroid Build Coastguard Worker }
218*9356374aSAndroid Build Coastguard Worker
219*9356374aSAndroid Build Coastguard Worker ABSL_NAMESPACE_END
220*9356374aSAndroid Build Coastguard Worker } // namespace absl
221*9356374aSAndroid Build Coastguard Worker
222*9356374aSAndroid Build Coastguard Worker #endif // ABSL_STRINGS_STR_REPLACE_H_
223