简体   繁体   中英

Converting a while loop to a do while loop

Here is a function to replace all the instances of a specific word(string) with another word(strng) in a parent string.

void clean(std::string &s,const std::string oldVal,const std::string newVal){
    std::string::size_type pos = 0;

    while((pos = s.find(oldVal,pos)) != std::string::npos){
        s.replace(pos,oldVal.size(),newVal);
        pos += newVal.size();
    }
}

I am pretty new to C++ and I found the while condition a little difficult to understand. So I thought of making this code more readable. I tried to make it into a do while loop. However, the program crashed. Throwing out_of_range exception.
What's wrong with my code? I used the same string to check both functions.

void clean2(std::string &s,const std::string oldVal,const std::string newVal){
    std::string::size_type pos = 0;
    do{
        pos = s.find(oldVal,pos);
        s.replace(pos,oldVal.size(),newVal);
        pos += newVal.size();
    }while(pos != std::string::npos);
}

This condition

pos != std::string::npos

you have to check after statement

pos = s.find(oldVal,pos);

Otherwise you can use invalid value of pos.

So the while loop in this case looks better then do while loop.:)

Instead of substituting the while loop for a do-while loop you could rewrite the function using a for loop. For example

void clean(std::string &s,const std::string oldVal,const std::string newVal)
{
    for ( std::string::size_type pos = 0; 
          ( pos = s.find( oldVal, pos ) ) != std::string::npos;
          pos += newVal.size() )
    {

        s.replace( pos, oldVal.size(), newVal );
    }
}

There is a reason that both while and do-while loops exist and it's not just for readability.
The main difference is the time that the condition is checked.
The previous versions works in a sequence of find -> test -> replace
Your version works in a sequence of find -> replace -> test.

What you can do is add an if before the replace that checks for the same loop condition before attempting the replace. However, that's both less efficient and less readable than the original IMO.

you need to both:

"not call replace when the string is not found" and "not add newVal.size() to pos" when string is not found. So you need another if inside the do-while-loop

In other words:

void clean2(std::string &s,const std::string oldVal,const std::string newVal){
    std::string::size_type pos = 0;
    do{
        pos = s.find(oldVal,pos);
        if (pos != std::string::npos)
        {
            s.replace(pos,oldVal.size(),newVal);
            pos += newVal.size();
        }
    }while(pos != std::string::npos);
}

Alternatively, you could do something like this:

 while(true)
 {
     pos = s.find(oldVal,pos);
     if (pos != std::string::npos)
     {
         s.replace(pos,oldVal.size(),newVal);
         pos += newVal.size();
     }
     else 
     {
         break;
     }
 }

Or many other variants on the same theme.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM