Есть ли простой простой способ заменить все вхождения "/" в строке с помощью "\/", чтобы избежать всех косых черт в std::string?
Как заменить все вхождения одного символа на два символа с помощью std::string?
Ответ 1
Вероятно, самый простой способ сделать это - повысить библиотеку строковых алгоритмов.
boost::replace_all(myString, "/", "\\/");
std::string result = boost::replace_all_copy(myString, "/", "\\/");
Ответ 2
Ответ - нет... нет "простого" способа, если вы имеете в виду однострочный набор, уже предоставленный стандартной библиотекой. Однако выполнять эту функцию не сложно.
Прежде всего, я думаю, что, вероятно, вам также потребуется заменить \
на \\
и другие специальные символы. В этом случае использование реализации replaceAll
, заданное ildjarn, будет раздражать (вам нужно будет заменить одну и ту же строку несколько раз).
По-моему, существует много случаев обработки строк, когда ничто не сравнивается с использованием явного подхода char *
. В этом конкретном случае, однако, вероятно, просто использование индекса в порядке:
std::string escape(const std::string& s)
{
int n = s.size(), wp = 0;
std::vector<char> result(n*2);
for (int i=0; i<n; i++)
{
if (s[i] == '/' || s[i] == '\\')
result[wp++] = '\\';
result[wp++] = s[i];
}
return std::string(&result[0], &result[wp]);
}
В основном идея состоит в том, чтобы перемещаться по строке и добавлять дополнительный символ \
перед любым специальным символом (в приведенном выше я просто обрабатывал /
и \
, но вы поняли эту идею).
Известно, что результат имеет максимальную длину 2*n
в длину, поэтому я предопределяю его, делая всю обработку O (n) (подход replaceAll
вместо этого перемещает остальную часть строки вправо, делая ее O (n ^ 2)).
Даже для коротких строк, таких как "this is a test with /slashes/ that should be /escaped/"
, вышеуказанная функция на моем ПК более эффективна (1,3x в скорости) даже при вызове replaceAll
только один раз и обработке вместо двух специальных символов в escape
.
Обратите также внимание на то, что эта функция, естественно, возвращает отдельную строку вместо изменения строки на месте (IMO - лучший интерфейс), и в сравнении с синхронизацией мне приходилось создавать строку для каждого вызова, чтобы результаты были даже сдвинуты в сторону равенства из-за который добавил постоянное время.
Вышеприведенный подход к чтению и записи также может быть легко расширен до более сложных подстановок (например, замена >
на >
или символов, не находящихся в диапазоне печати с кодировкой %xx
), сохраняющих хорошую эффективность для больших строк (просто один проход).
Ответ 3
Пример того, как это сделать, приведен на странице cppreference.com std::string::replace
:
std::string& replaceAll(std::string& context, std::string const& from, std::string const& to)
{
std::size_t lookHere = 0;
std::size_t foundHere;
while((foundHere = context.find(from, lookHere)) != std::string::npos)
{
context.replace(foundHere, from.size(), to);
lookHere = foundHere + to.size();
}
return context;
}
Ответ 4
Ответ 5
Чтобы заменить все вхождения подстроки в строке другой подстрокой:
#include <iostream>
void replace_all(std::string& input, const std::string& from, const std::string& to) {
size_t pos = 0;
while ((pos = input.find(from, pos)) != std::string::npos) {
input.replace(pos, from.size(), to);
pos += to.size();
}
}
int main() {
std::string str("i am a geek/nerd/crazy person.");
replace_all(str, "/", "\\/");
std::cout << str << '\n';
}
Вывод:
$ g++-6.1.0 -std=c++17 -g -Og -Werror -Wall -Wextra -pedantic -Wold-style-cast -Wnon-virtual-dtor -Wshadow -Wcast-align -Wunused -Woverloaded-virtual -Wconversion -Wsign-conversion -Wmisleading-indentation -fsanitize=address,leak,undefined; ./a.out
i am a geek\/nerd\/crazy person.
Ответ 6
Я экстраполировал вопрос, чтобы сделать поточную реализацию, чтобы вы могли избежать множества символов.
Потоковая передача действительно берет бисквит для больших томов [1], потому что в противном случае вы получите фрагментацию кучи/производительность. Кроме того, это позволяет избежать строк, хранящихся практически в любом источнике, поскольку образцы показывают
#include <iostream>
#include <iterator>
#include <set>
#include <sstream>
#include <string>
template <class _II, class _OI>
static _OI escapeSomeChars(const _II inIt, const _II endIt, _OI outIt)
{
for (_II it=inIt; it!=endIt; ++it)
switch (*it)
{
case '\0': outIt++ = '\\'; outIt++ = '0'; break;
case '\n': outIt++ = '\\'; outIt++ = 'n'; break;
case '\\':
case '"' :
case '$' :
case '/' : outIt++ = '\\';
default : outIt++ = *it;
}
return outIt;
}
static std::string escapeSomeChars(const std::string& input)
{
std::ostringstream os;
escapeSomeChars(input.begin(), input.end(), std::ostream_iterator<char>(os));
return os.str();
}
namespace /*anon*/ {
struct rawchar { // helper - see e.g. http://bytes.com/topic/c/answers/436124-copy-istream_iterator-question
char _c; rawchar(char c=0) : _c(c) {}
operator const char&() const { return _c; }
friend std::istream& operator>>(std::istream& is, rawchar& out) { return is.get(out._c); }
};
}
int main()
{
static const char data[] = "\"I will \\$one day \\have \\all \\\\my slash\\es escaped, much \\like\\ in the source!\n\"";
// use the overload for std::string
std::cout << escapeSomeChars(data);
std::cout << std::endl;
// streaming in & out:
std::istringstream is(data);
escapeSomeChars(std::istream_iterator<rawchar>(is), std::istream_iterator<rawchar>(), std::ostream_iterator<char>(std::cout));
std::cout << std::endl;
// but you don't need an istream, you can use any STL iterator range
escapeSomeChars(data, data+sizeof(data)/sizeof(data[0]), std::ostream_iterator<char>(std::cout));
std::cout << std::endl;
// but any source and target will do:
std::string asstring(data);
std::set<char> chars(asstring.begin(), asstring.end());
asstring.clear();
escapeSomeChars(chars.begin(), chars.end(), std::back_inserter(asstring));
std::cout << "Unique characters in data: '" << asstring << "', but properly escaped!" << std::endl;
return 0;
}
Я выбрал переключатель, потому что он будет оптимизирован компилятором. Для динамических наборов экранируемых символов я бы предпочел какой-то поиск (вектор с std:: find будет делать, хотя для больших наборов лучшим выбором будет std:: set с set:: find).
Надеюсь, что это поможет
[1] см., например, эта красивая ошибка, с которой я недавно столкнулся: GParted: упрощенная реализация cleanup_cursor()