1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/strings/string_split.h"
7 #include "base/logging.h"
8 #include "base/strings/string_util.h"
9 #include "base/third_party/icu/icu_utf.h"
15 // PieceToOutputType converts a StringPiece as needed to a given output type,
16 // which is either the same type of StringPiece (a NOP) or the corresponding
17 // non-piece string type.
19 // The default converter is a NOP, it works when the OutputType is the
20 // correct StringPiece.
21 template<typename Str
, typename OutputType
>
22 OutputType
PieceToOutputType(BasicStringPiece
<Str
> piece
) {
25 template<> // Convert StringPiece to std::string
26 std::string PieceToOutputType
<std::string
, std::string
>(StringPiece piece
) {
27 return piece
.as_string();
29 template<> // Convert StringPiece16 to string16.
30 string16 PieceToOutputType
<string16
, string16
>(StringPiece16 piece
) {
31 return piece
.as_string();
34 // Returns either the ASCII or UTF-16 whitespace.
35 template<typename Str
> BasicStringPiece
<Str
> WhitespaceForType();
36 template<> StringPiece16 WhitespaceForType
<string16
>() {
37 return kWhitespaceUTF16
;
39 template<> StringPiece WhitespaceForType
<std::string
>() {
40 return kWhitespaceASCII
;
43 // Optimize the single-character case to call find() on the string instead,
44 // since this is the common case and can be made faster. This could have been
45 // done with template specialization too, but would have been less clear.
47 // There is no corresponding FindFirstNotOf because StringPiece already
48 // implements these different versions that do the optimized searching.
49 size_t FindFirstOf(StringPiece piece
, char c
, size_t pos
) {
50 return piece
.find(c
, pos
);
52 size_t FindFirstOf(StringPiece16 piece
, char16 c
, size_t pos
) {
53 return piece
.find(c
, pos
);
55 size_t FindFirstOf(StringPiece piece
, StringPiece one_of
, size_t pos
) {
56 return piece
.find_first_of(one_of
, pos
);
58 size_t FindFirstOf(StringPiece16 piece
, StringPiece16 one_of
, size_t pos
) {
59 return piece
.find_first_of(one_of
, pos
);
62 // General string splitter template. Can take 8- or 16-bit input, can produce
63 // the corresponding string or StringPiece output, and can take single- or
64 // multiple-character delimiters.
66 // DelimiterType is either a character (Str::value_type) or a string piece of
67 // multiple characters (BasicStringPiece<Str>). StringPiece has a version of
68 // find for both of these cases, and the single-character version is the most
69 // common and can be implemented faster, which is why this is a template.
70 template<typename Str
, typename OutputStringType
, typename DelimiterType
>
71 static std::vector
<OutputStringType
> SplitStringT(
72 BasicStringPiece
<Str
> str
,
73 DelimiterType delimiter
,
74 WhitespaceHandling whitespace
,
75 SplitResult result_type
) {
76 std::vector
<OutputStringType
> result
;
81 while (start
!= Str::npos
) {
82 size_t end
= FindFirstOf(str
, delimiter
, start
);
84 BasicStringPiece
<Str
> piece
;
85 if (end
== Str::npos
) {
86 piece
= str
.substr(start
);
89 piece
= str
.substr(start
, end
- start
);
93 if (whitespace
== TRIM_WHITESPACE
)
94 piece
= TrimString(piece
, WhitespaceForType
<Str
>(), TRIM_ALL
);
96 if (result_type
== SPLIT_WANT_ALL
|| !piece
.empty())
97 result
.push_back(PieceToOutputType
<Str
, OutputStringType
>(piece
));
102 bool SplitStringIntoKeyValue(const std::string
& line
,
103 char key_value_delimiter
,
105 std::string
* value
) {
109 // Find the delimiter.
110 size_t end_key_pos
= line
.find_first_of(key_value_delimiter
);
111 if (end_key_pos
== std::string::npos
) {
112 DVLOG(1) << "cannot find delimiter in: " << line
;
113 return false; // no delimiter
115 key
->assign(line
, 0, end_key_pos
);
117 // Find the value string.
118 std::string
remains(line
, end_key_pos
, line
.size() - end_key_pos
);
119 size_t begin_value_pos
= remains
.find_first_not_of(key_value_delimiter
);
120 if (begin_value_pos
== std::string::npos
) {
121 DVLOG(1) << "cannot parse value from line: " << line
;
122 return false; // no value
124 value
->assign(remains
, begin_value_pos
, remains
.size() - begin_value_pos
);
128 template <typename STR
>
129 void SplitStringUsingSubstrT(const STR
& str
,
131 std::vector
<STR
>* r
) {
133 typename
STR::size_type begin_index
= 0;
135 const typename
STR::size_type end_index
= str
.find(s
, begin_index
);
136 if (end_index
== STR::npos
) {
137 const STR term
= str
.substr(begin_index
);
139 TrimWhitespace(term
, TRIM_ALL
, &tmp
);
143 const STR term
= str
.substr(begin_index
, end_index
- begin_index
);
145 TrimWhitespace(term
, TRIM_ALL
, &tmp
);
147 begin_index
= end_index
+ s
.size();
153 std::vector
<std::string
> SplitString(StringPiece input
,
154 StringPiece separators
,
155 WhitespaceHandling whitespace
,
156 SplitResult result_type
) {
157 if (separators
.size() == 1) {
158 return SplitStringT
<std::string
, std::string
, char>(
159 input
, separators
[0], whitespace
, result_type
);
161 return SplitStringT
<std::string
, std::string
, StringPiece
>(
162 input
, separators
, whitespace
, result_type
);
165 std::vector
<string16
> SplitString(StringPiece16 input
,
166 StringPiece16 separators
,
167 WhitespaceHandling whitespace
,
168 SplitResult result_type
) {
169 if (separators
.size() == 1) {
170 return SplitStringT
<string16
, string16
, char16
>(
171 input
, separators
[0], whitespace
, result_type
);
173 return SplitStringT
<string16
, string16
, StringPiece16
>(
174 input
, separators
, whitespace
, result_type
);
177 std::vector
<StringPiece
> SplitStringPiece(StringPiece input
,
178 StringPiece separators
,
179 WhitespaceHandling whitespace
,
180 SplitResult result_type
) {
181 if (separators
.size() == 1) {
182 return SplitStringT
<std::string
, StringPiece
, char>(
183 input
, separators
[0], whitespace
, result_type
);
185 return SplitStringT
<std::string
, StringPiece
, StringPiece
>(
186 input
, separators
, whitespace
, result_type
);
189 std::vector
<StringPiece16
> SplitStringPiece(StringPiece16 input
,
190 StringPiece16 separators
,
191 WhitespaceHandling whitespace
,
192 SplitResult result_type
) {
193 if (separators
.size() == 1) {
194 return SplitStringT
<string16
, StringPiece16
, char16
>(
195 input
, separators
[0], whitespace
, result_type
);
197 return SplitStringT
<string16
, StringPiece16
, StringPiece16
>(
198 input
, separators
, whitespace
, result_type
);
201 void SplitString(const string16
& str
,
203 std::vector
<string16
>* result
) {
204 DCHECK(CBU16_IS_SINGLE(c
));
205 *result
= SplitStringT
<string16
, string16
, char16
>(
206 str
, c
, TRIM_WHITESPACE
, SPLIT_WANT_ALL
);
208 // Backward-compat hack: The old SplitString implementation would keep
209 // empty substrings, for example:
210 // "a,,b" -> ["a", "", "b"]
211 // "a, ,b" -> ["a", "", "b"]
212 // which the current code also does. But the old one would discard them when
213 // the only result was that empty string:
215 // In the latter case, our new code will give [""]
216 if (result
->size() == 1 && (*result
)[0].empty())
220 void SplitString(const std::string
& str
,
222 std::vector
<std::string
>* result
) {
227 *result
= SplitStringT
<std::string
, std::string
, char>(
228 str
, c
, TRIM_WHITESPACE
, SPLIT_WANT_ALL
);
230 // Backward-compat hack, see above.
231 if (result
->size() == 1 && (*result
)[0].empty())
236 bool SplitStringIntoKeyValuePairs(const std::string
& line
,
237 char key_value_delimiter
,
238 char key_value_pair_delimiter
,
239 StringPairs
* key_value_pairs
) {
240 key_value_pairs
->clear();
242 std::vector
<std::string
> pairs
;
243 SplitString(line
, key_value_pair_delimiter
, &pairs
);
246 for (size_t i
= 0; i
< pairs
.size(); ++i
) {
247 // Don't add empty pairs into the result.
248 if (pairs
[i
].empty())
253 if (!SplitStringIntoKeyValue(pairs
[i
], key_value_delimiter
, &key
, &value
)) {
254 // Don't return here, to allow for pairs without associated
255 // value or key; just record that the split failed.
258 key_value_pairs
->push_back(make_pair(key
, value
));
263 void SplitStringUsingSubstr(const string16
& str
,
265 std::vector
<string16
>* r
) {
266 SplitStringUsingSubstrT(str
, s
, r
);
269 void SplitStringUsingSubstr(const std::string
& str
,
270 const std::string
& s
,
271 std::vector
<std::string
>* r
) {
272 SplitStringUsingSubstrT(str
, s
, r
);
275 void SplitStringDontTrim(StringPiece16 str
,
277 std::vector
<string16
>* result
) {
278 DCHECK(CBU16_IS_SINGLE(c
));
279 *result
= SplitStringT
<string16
, string16
, char16
>(
280 str
, c
, KEEP_WHITESPACE
, SPLIT_WANT_ALL
);
283 void SplitStringDontTrim(StringPiece str
,
285 std::vector
<std::string
>* result
) {
290 *result
= SplitStringT
<std::string
, std::string
, char>(
291 str
, c
, KEEP_WHITESPACE
, SPLIT_WANT_ALL
);
294 void SplitStringAlongWhitespace(const string16
& str
,
295 std::vector
<string16
>* result
) {
296 *result
= SplitStringT
<string16
, string16
, StringPiece16
>(
297 str
, StringPiece16(kWhitespaceASCIIAs16
),
298 TRIM_WHITESPACE
, SPLIT_WANT_NONEMPTY
);
301 void SplitStringAlongWhitespace(const std::string
& str
,
302 std::vector
<std::string
>* result
) {
303 *result
= SplitStringT
<std::string
, std::string
, StringPiece
>(
304 str
, StringPiece(kWhitespaceASCII
),
305 TRIM_WHITESPACE
, SPLIT_WANT_NONEMPTY
);