Skip to content

Commit

Permalink
Add function 'find_and_replace' (#13)
Browse files Browse the repository at this point in the history
* Add function 'find_and_replace'

Signed-off-by: Jacob Perron <[email protected]>

* Fix spelling

Signed-off-by: Jacob Perron <[email protected]>

* Add shortcut for find == replace

Signed-off-by: Jacob Perron <[email protected]>

* Template on std::basic_string

Signed-off-by: Jacob Perron <[email protected]>

* changed find_and_replace to accept literals using magic 🌈✨

Signed-off-by: William Woodall <[email protected]>

* Fix lint

Signed-off-by: Jacob Perron <[email protected]>
  • Loading branch information
jacobperron committed Jul 26, 2019
1 parent e44526c commit 5089766
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ if(BUILD_TESTING)
ament_add_gtest(test_split test/test_split.cpp)

ament_add_gtest(test_filesystem_helper test/test_filesystem_helper.cpp)

ament_add_gtest(test_find_and_replace test/test_find_and_replace.cpp)
endif()

ament_package()
Expand Down
93 changes: 93 additions & 0 deletions include/rcpputils/find_and_replace.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef RCPPUTILS__FIND_AND_REPLACE_HPP_
#define RCPPUTILS__FIND_AND_REPLACE_HPP_

#include <memory>
#include <string>

namespace rcpputils
{

/// Find and replace all instances of a string with another string.
/**
* \param[in] input The input string.
* \param[in] find The substring to replace.
* \param[in] replace The string to substitute for each occurrence of `find`.
* \return A copy of the input string with all instances of the string `find` replaced with the
* string `replace`.
*/
template<
class CharT,
class Traits = std::char_traits<CharT>,
class Allocator = std::allocator<CharT>
>
std::basic_string<CharT, Traits, Allocator>
find_and_replace(
const std::basic_string<CharT, Traits, Allocator> & input,
const std::basic_string<CharT, Traits, Allocator> & find,
const std::basic_string<CharT, Traits, Allocator> & replace)
{
std::basic_string<CharT, Traits, Allocator> output = input;
const std::size_t find_len = find.length();
const std::size_t replace_len = replace.length();
if (find == replace) {
return output;
}
if (0u == find_len) {
return output;
}
std::size_t pos = output.find(find);
while (pos != std::basic_string<CharT, Traits, Allocator>::npos) {
output.replace(pos, find_len, replace);
pos = output.find(find, pos + replace_len);
}
return output;
}

namespace detail
{
template<typename CharT, std::size_t Length>
std::basic_string<CharT>
normalize_to_basic_string(const CharT (& char_string)[Length])
{
return std::basic_string<CharT>(char_string);
}

template<typename StringLikeT>
StringLikeT &&
normalize_to_basic_string(StringLikeT && string_like)
{
return string_like;
}
} // namespace detail

template<typename InputT, typename FindT, typename ReplaceT>
auto
find_and_replace(
InputT && input,
FindT && find,
ReplaceT && replace)
{
auto input_str = detail::normalize_to_basic_string(input);
return find_and_replace<typename decltype(input_str)::value_type>(
input_str,
detail::normalize_to_basic_string(find),
detail::normalize_to_basic_string(replace));
}

} // namespace rcpputils

#endif // RCPPUTILS__FIND_AND_REPLACE_HPP_
90 changes: 90 additions & 0 deletions test/test_find_and_replace.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <gtest/gtest.h>
#include <string>

#include "rcpputils/find_and_replace.hpp"

TEST(test_find_and_replace, find_and_replace) {
// Empty input
{
auto ret = rcpputils::find_and_replace("", "foo", "bar");
EXPECT_EQ("", ret);
}
// Empty find
{
auto ret = rcpputils::find_and_replace("foo", "", "bar");
EXPECT_EQ("foo", ret);
}
// Empty replace
{
auto ret = rcpputils::find_and_replace("foo", "foo", "");
EXPECT_EQ("", ret);
}
// Single occurrence
{
auto ret = rcpputils::find_and_replace("foo", "foo", "bar");
EXPECT_EQ("bar", ret);
}
// No occurrences
{
auto ret = rcpputils::find_and_replace("foo", "bar", "baz");
EXPECT_EQ("foo", ret);
}
// Multiple occurrences
{
auto ret = rcpputils::find_and_replace("foobarfoobar", "foo", "baz");
EXPECT_EQ("bazbarbazbar", ret);
}
{
auto ret = rcpputils::find_and_replace("foofoobar", "foo", "baz");
EXPECT_EQ("bazbazbar", ret);
}
// find == replace
{
auto ret = rcpputils::find_and_replace("foobar", "foo", "foo");
EXPECT_EQ("foobar", ret);
}
// find is a substring of replace
{
auto ret = rcpputils::find_and_replace("foobar", "foo", "barfoo");
EXPECT_EQ("barfoobar", ret);
}
// replace is a substring of find
{
auto ret = rcpputils::find_and_replace("foobar", "foobar", "bar");
EXPECT_EQ("bar", ret);
}
}

TEST(test_find_and_replace, find_and_replace_wstring) {
auto ret = rcpputils::find_and_replace(
std::wstring(L"foobar"),
std::wstring(L"foo"),
std::wstring(L"bar"));
EXPECT_EQ(std::wstring(L"barbar"), ret);
}

TEST(test_find_and_replace, find_and_replace_various_input_types) {
rcpputils::find_and_replace(std::string("foo"), "foo", "bar");
rcpputils::find_and_replace(std::string("foo"), std::string("foo"), "bar");
rcpputils::find_and_replace(std::string("foo"), "foo", std::string("bar"));
rcpputils::find_and_replace(std::string("foo"), std::string("foo"), std::string("bar"));

rcpputils::find_and_replace("foo", std::string("foo"), "bar");
rcpputils::find_and_replace("foo", std::string("foo"), std::string("bar"));

rcpputils::find_and_replace("foo", "foo", std::string("bar"));
}

0 comments on commit 5089766

Please sign in to comment.