std::string result(&data[0], len);
// The below is necessary on Windows to make newlines stay as '\n' on a
- // roundtrip. Perhaps would be better to only replace '\r\n' with '\n',
- // but in practice this probably makes little difference.
- std::string::iterator end = std::remove(result.begin(), result.end(), '\r');
- result.erase(end, result.end());
+ // roundtrip.
+ result = replaceAll(result, "\r\n", "\n");
return result;
}
return result;
}
+namespace
+{
+
+std::string
+replaceInternal(const std::string &input, const char *from, const char *to,
+ bool bWholeWords)
+{
+ GMX_RELEASE_ASSERT(from != NULL && to != NULL,
+ "Replacement strings must not be NULL");
+ size_t matchLength = std::strlen(from);
+ std::string result;
+ size_t inputPos = 0;
+ size_t matchPos = input.find(from);
+ while (matchPos < input.length())
+ {
+ size_t matchEnd = matchPos + matchLength;
+ if (bWholeWords)
+ {
+ if (!((matchPos == 0 || !std::isalnum(input[matchPos-1]))
+ && (matchEnd == input.length() || !std::isalnum(input[matchEnd]))))
+ {
+ matchPos = input.find(from, matchPos + 1);
+ continue;
+ }
+
+ }
+ result.append(input, inputPos, matchPos - inputPos);
+ result.append(to);
+ inputPos = matchEnd;
+ matchPos = input.find(from, inputPos);
+ }
+ result.append(input, inputPos, matchPos - inputPos);
+ return result;
+}
+
+} // namespace
+
+std::string
+replaceAll(const std::string &input, const char *from, const char *to)
+{
+ return replaceInternal(input, from, to, false);
+}
+
+std::string
+replaceAllWords(const std::string &input, const char *from, const char *to)
+{
+ return replaceInternal(input, from, to, true);
+}
+
/********************************************************************
* TextLineWrapper::Impl
*/
return concatenateStrings(sarray, count);
}
+/*! \brief
+ * Replace all occurrences of a string with another string.
+ *
+ * \param[in] input Input string.
+ * \param[in] from String to find.
+ * \param[in] to String to use to replace \p from.
+ * \returns \p input with all occurrences of \p from replaced with \p to.
+ * \throws std::bad_alloc if out of memory.
+ *
+ * The replacement is greedy and not recursive: starting from the beginning of
+ * \p input, each match of \p from is replaced with \p to, and the search for
+ * the next match begins after the end of the previous match.
+ *
+ * Compexity is O(N), where N is length of output.
+ *
+ * \see replaceAllWords()
+ *
+ * \inpublicapi
+ */
+std::string replaceAll(const std::string &input,
+ const char *from, const char *to);
+/*! \brief
+ * Replace whole words with others.
+ *
+ * \param[in] input Input string.
+ * \param[in] from String to find.
+ * \param[in] to String to use to replace \p from.
+ * \returns \p input with all \p from words replaced with \p to.
+ * \throws std::bad_alloc if out of memory.
+ *
+ * Works as replaceAll(), but a match is only considered if it is delimited by
+ * non-alphanumeric characters.
+ *
+ * \see replaceAll()
+ *
+ * \inpublicapi
+ */
+std::string replaceAllWords(const std::string &input,
+ const char *from, const char *to);
+
/*! \brief
* Wraps lines to a predefined length.
*
checkText(gmx::concatenateStrings(strings), "CombinedStrings");
}
+/********************************************************************
+ * Tests for replaceAll() and replaceAllWords()
+ */
+
+TEST(ReplaceAllTest, HandlesEmptyStrings)
+{
+ EXPECT_EQ("", gmx::replaceAll("", "aaa", "bbbb"));
+ EXPECT_EQ("", gmx::replaceAllWords("", "aaa", "bbbb"));
+}
+
+TEST(ReplaceAllTest, HandlesNoMatches)
+{
+ const std::string text("Text with no matches");
+ EXPECT_EQ(text, gmx::replaceAll(text, "aaa", "bbbb"));
+ EXPECT_EQ(text, gmx::replaceAllWords(text, "aaa", "bbbb"));
+}
+
+TEST(ReplaceAllTest, HandlesMatchesAtEnds)
+{
+ EXPECT_EQ("bbbbtext", gmx::replaceAll("aaatext", "aaa", "bbbb"));
+ EXPECT_EQ("textbbbb", gmx::replaceAll("textaaa", "aaa", "bbbb"));
+ EXPECT_EQ("bbbb text", gmx::replaceAllWords("aaa text", "aaa", "bbbb"));
+ EXPECT_EQ("text bbbb", gmx::replaceAllWords("text aaa", "aaa", "bbbb"));
+}
+
+TEST(ReplaceAllTest, HandlesMultipleMatches)
+{
+ const std::string text("Text aaa with multiple aaa matches");
+ EXPECT_EQ("Text bbbb with multiple bbbb matches",
+ gmx::replaceAll(text, "aaa", "bbbb"));
+ EXPECT_EQ("Text bbbb with multiple bbbb matches",
+ gmx::replaceAllWords(text, "aaa", "bbbb"));
+}
+
+TEST(ReplaceAllTest, HandlesWordBoundaries)
+{
+ const std::string text("Text aaax with one word aaa match");
+ EXPECT_EQ("Text aaax with one word bbbb match",
+ gmx::replaceAllWords(text, "aaa", "bbbb"));
+}
+
+TEST(ReplaceAllTest, HandlesPossibleRecursiveMatches)
+{
+ const std::string text("Text with recursive aaabbbbbb matches");
+ EXPECT_EQ("Text with recursive aaaaaabbb matches",
+ gmx::replaceAll(text, "aaabbb", "aaaaaa"));
+}
+
/********************************************************************
* Tests for TextLineWrapper
*/