Заменить подстрок в строке после определенной подстроки
Суть: есть обычная строка std::string, в которой находится произвольной длины список чисел через запятую (например: "1,2,32454,0,67,123"). Нужно, чтобы после нуля, все последующие числа так же заменялись на 0 (т.е. "1,2,32454,0,0,0").
Как это можно сделать? Может, в boost-е есть что-то необходимое?
Ответы (1 шт):
Так устроит?
int main(int argc, char * argv[])
{
string ins{"1,2,32454,0,67,123"}, s;
bool z = false;
ranges::copy(views::join(split_view(ins,","sv) |
views::transform([&z](auto q) {
string s(q.begin(),q.end());
if (z || stoi(s)==0) { z = true; return "0,"s; }
else return s+","; })),
back_inserter(s));
s.pop_back();
cout << s;
}
Чисто теоретически (не могу у себя проверить) должно при поддержке C++23 работать
int main(int argc, char * argv[])
{
string ins{"1,2,32454,0,67,123"}, s;
bool z = false;
ranges::copy(views::join_with(split_view(ins,","sv) |
views::transform([&z](auto q) {
string s(q.begin(),q.end());
if (z || stoi(s)==0) { z = true; return "0"s; }
else return s; }),","),
back_inserter(s));
cout << s;
}
но тут я никакой гарантии не даю...
@D4rki, это, конечно, из пушки по воробьям. Проще каким-нибудь strtok'ом поделить строку на части и сшить наново с учетом нолей...
Update 1
Как я к этому пришел... Глобальное решение — делим строку по запятым, встретив 0, запоминаем, дальше все заменяем на 0, и собираем строку вновь.
Итак, деление. Это делается с помощью
split_view(ins,","sv)
sv — потому что функция требует string_view, без этого не понимает, что надо, и не работает.
Итак, мы получили набор string_view, представляющих числа. Дальше мы преобразуем их в строки, которые либо совпадают с исходными, либо нули (после обнаружения первого нуля). Сначала я думал сделать на выходе числа, но потом решил, что особого смысла в двойном преобразовании нет. Хотя строку все равно пришлось строить из string_view — чтобы получить целочисленное значение. Почему не сравнение со строкой? Да чтоб не упустить случай 000, например.
Это преобразование выполняется с помощью transform и лямбда-функции, которая захватывает переменную z — флаг, был ли уже нуль или нет:
[&z](auto q) {
string s(q.begin(),q.end());
if (z || stoi(s)==0) {
z = true; // Выставляем флаг.
return "0,"s; // Теперь нули
}
else return s+",";
} // Строка, что и была
Можно сэкономить и переписать, чтоб конвертирования в строку при z == true не было вовсе. Хотите — займитесь... Запятые добавил, потому что надо же разделитель, а готовой функции копирования с разделителем не нашел.
Далее собираем все это в одну последовательность с помощью views::join(), которую легко собрать в одну строку с помощью ranges::copy(), который копирует последовательность с использованием в качестве целевого итератора back_inserter(s), где s — целевая строка.
Последний штрих — удаление последней запятой s.pop_back();. Всё.
Если использовать join_with(), то вроде бы можно просто указать запятую как разделитель, но это я проверить у себя не смог, не реализовано это в VC++2019.
Update 2
Но если бы это было реально нужно мне, я бы написал что-то вроде
string trans(const string& in)
{
size_t pos = in.npos;
if (in.starts_with("0,")) pos = 0;
if (pos == in.npos) pos = in.find(",0,");
if (pos == in.npos) return in;
if (pos) ++pos;
size_t commas = count(in.begin()+pos,in.end(),',');
string res = in.substr(0,pos);
for(int i = 0; i <= commas; res += i++ == commas ? "0" : "0,");
return res;
}
и не мучился :)