1

I want to selectively replace the (") doublequotes in a C++ std::string. i.e. i want to replace all occurences of (") doublequotes in a string except the 1st and last occurence of (") doublequotes in the string.

Example- following code replaces ALL occurence of (") doublequotes

std::string str = "\"Hello people \" how are you doing \" what are \" you upto \"";
str = std::regex_replace(str, std::regex("\\\""), """);

However, i don't want to replace the 1st and last occurenece of the string.

i.e. in the string i don't want to replace (") just before "Hello" & the one at the end.

std::string str = "\"Hello people \" how are you doing \" what are \" you upto \"";
user0042
  • 7,917
  • 3
  • 24
  • 39
codeLover
  • 3,720
  • 10
  • 65
  • 121
  • @WiktorStribiżew you should post this as an answer, actually – ivanmoskalev Oct 24 '17 at 11:42
  • If you cannot assume there are >= 2 double quotes, you should probably check first e.g. with [`std::string::find_first_of`](http://en.cppreference.com/w/cpp/string/basic_string/find_first_of) and [`std::string::find_last_of`](http://en.cppreference.com/w/cpp/string/basic_string/find_last_of). Or just state preconditions on the input and don't violate them – svenevs Oct 24 '17 at 11:42
  • 1
    Is string can be `R"(some text before" text inside with possible " in it " some text after)"` ? or first/last quote are fist/last character ? – Jarod42 Oct 24 '17 at 11:45
  • So you have a problem. And you think "I know, I can solve this with regex!". Now you have two problems: your initial problem, and regex. – Yakk - Adam Nevraumont Oct 24 '17 at 17:32
  • Your use of `>` to quote part of your question is confusing. Are you quoting someone there? – Yakk - Adam Nevraumont Oct 24 '17 at 17:34

2 Answers2

3

Scenario 1: Quotes after leading whitespace/before trailing whitespace

You may use a regex that will capture quotes at the start/end of string together with leading/trailing whitespace into Group 1, and will match quotes in all other contexts. Then you need to implement custom replacement for each of the possibilities: when Group 1 matches, you need to paste the whole match back, if not, replacr with ":

#include <iostream>
#include <cstdlib>
#include <string>
#include <regex>
using namespace std;
 
template<class BidirIt, class Traits, class CharT, class UnaryFunction>
std::basic_string<CharT> regex_replace(BidirIt first, BidirIt last,
    const std::basic_regex<CharT,Traits>& re, UnaryFunction f)
{
    std::basic_string<CharT> s;
 
    typename std::match_results<BidirIt>::difference_type
        positionOfLastMatch = 0;
    auto endOfLastMatch = first;
 
    auto callback = [&](const std::match_results<BidirIt>& match)
    {
        auto positionOfThisMatch = match.position(0);
        auto diff = positionOfThisMatch - positionOfLastMatch;
 
        auto startOfThisMatch = endOfLastMatch;
        std::advance(startOfThisMatch, diff);
 
        s.append(endOfLastMatch, startOfThisMatch);
        s.append(f(match));
 
        auto lengthOfMatch = match.length(0);
 
        positionOfLastMatch = positionOfThisMatch + lengthOfMatch;
 
        endOfLastMatch = startOfThisMatch;
        std::advance(endOfLastMatch, lengthOfMatch);
    };
 
    std::sregex_iterator begin(first, last, re), end;
    std::for_each(begin, end, callback);
 
    s.append(endOfLastMatch, last);
 
    return s;
}
 
template<class Traits, class CharT, class UnaryFunction>
std::string regex_replace(const std::string& s,
    const std::basic_regex<CharT,Traits>& re, UnaryFunction f)
{
    return regex_replace(s.cbegin(), s.cend(), re, f);
}
 
std::string my_callback(const std::smatch& m) {
  if (m.str(1).length() % 2 == 0) {
    return "&quot;";
  } else {
    return m.str(0);
  }
}
 
int main() {
    std::string str = "\"Hello people \" how are you doing \" what are \" you upto \"";
    cout << regex_replace(str, regex("(^\\s*\"|\"\\s*$)|\""), my_callback) << endl;
 
    return 0;
}

See the C++ demo. The callback implemenation by John Martin.

Scenario 2: Quotes at the start/end of string

You may use

std::regex("(?!^)\"(?!$)")

The (?!^) negative lookahead fails the match if the " is found at the start (^) and (?!$) fails if it is found at the end ($) of string.

See the regex demo

C++ demo:

#include <iostream>
#include <regex>
using namespace std;
 
int main() {
    std::string str = "\"Hello people \" how are you doing \" what are \" you upto \"";
    str = std::regex_replace(str, std::regex("(?!^)\"(?!$)"), "&quot;");
    std::cout << str << std::endl;
    return 0;
}

Output: "Hello people &quot; how are you doing &quot; what are &quot; you upto "

Community
  • 1
  • 1
Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
  • thank you. i upvoted this. However, it still hasn't answered my exact question because, it has to skip the very 1st occurence of (") and the very last occurence of ("). But this regex doesn't work if i''ve a space for example in the begining. like :: std::string str = " \"Hello \"people \" how are you doing \" what are \" you upto \""; - -- has a space here - " \"H versus "\"H with no space. – codeLover Oct 24 '17 at 13:00
  • Do you really want to do it with regex? `std::regex`? With Boost, it would be much easier. – Wiktor Stribiżew Oct 24 '17 at 13:10
  • Requirement is not to use boost instead do with standard c++ libraries only. – codeLover Oct 24 '17 at 13:24
1

A solution without regex and boost

  auto aPos1 = aString.find_first_of("\"");
  auto aPos2 = aString.find_last_of("\"");
  auto aPos = aString.length() - 1;
  for( ; aPos > aPos1 ; aPos--)
  {
    auto aVal = aString.at(aPos);
    if(aPos != aPos2 && aPos != aPos1)
    {
       if(aVal == '\"')
       {
        aString.erase(aPos,1);
       }
    }
  }
  • The first and last `"` should only be preserved if they are separated with 0+ whitespaces from the start/end of string. The solution is not appropriate for the current OP scenario. – Wiktor Stribiżew Oct 24 '17 at 23:39