Question

I am doing a series of searches in a string, and somewhere along the line one of the strings will be missed, and my set of searches should fail.

I had expected that once the position reached std::string::npos it would stay there, but it does not. Passing std::string::npos to std::string.find seems to start the search at the beginning again

std::string str("frederick");
std::string::size_type pos = str.find("der",std::string::npos);
TS_ASSERT_EQUALS(pos, std::string::npos); // FAIL, 3 is returned

Why is it not being taken to indicate the end of the string?

Update: The intention is search for a series of strings in order, and check the result at the end

pos = str.find(string1, pos)
pos = str.find(string2, pos)
pos = str.find(string3, pos)
if (pos != std:string::npos)
{ // All strings found
Was it helpful?

Solution

Looking at the spec, I think that there may be a bug in your implementation.

basic_string::find should return the lowest position xpos such that pos <= xpos and xpos + str.size() <= size() and at(xpos + I) == str.at(I) for all elements I controlled by str.

basic_string::npos is -1 converted to an unsigned type so must be the largest number representable by that unsigned type. Given that no other position xpos can satisfy even the first part of npos <= xpos and find must return npos on failure, as far as I can see npos is the only valid return value for basic_string::find when passed npos as the second parameter.

OTHER TIPS

Compare string::find() and string::copy(). (In N2798, that's 21.3.7.2 and 21.3.6.7, pages 686/687) Both take a position argument. Yet only string::copy has a "Requires: pos <= size()" clause. Hence, string::find does not require pos <= size().

From that point on, Charles Bailey has the correct logic. Look at the range of valid return values, and it becomes clear that only the only return value which matches the rqeuirements is string::npos. Any other value returned is smaller than string::npos, failing 21.3.7.2/1.


From N2798=08-0308, copyright ISO/IEC:

21.3.7.2 basic_string::find [string::find]

size_type find(const basic_string<charT,traits,Allocator>& str, size_type pos = 0) const;

1 Effects: Determines the lowest position xpos, if possible, such that both of the following conditions obtain: — pos <= xpos and xpos + str.size() <= size();traits::eq(at(xpos+I), str.at(I)) for all elements I of the string controlled by str. 2 Returns: xpos if the function can determine such a value for xpos. Otherwise, returns npos. 3 Remarks: Uses traits::eq().

std::string::npos is not a valid argument for std::string::find.

The definition of find in the Standard only mentions npos as a possible return value, not a start position.

You may find that the free function std::search is easier to use in this situation. E.g.

std::string::const_iterator iter = str.begin();

iter = std::search( iter, str.end(), string1.begin(), string1.end() );
iter = std::search( iter, str.end(), string2.begin(), string2.end() );
iter = std::search( iter, str.end(), string3.begin(), string3.end() );

Behavior is undefined if you pass npos:

[update]
STL documentation (the two reproductions I can find, anyway) mention string::npos only as possible return value, not as a valid value for pos. The latter is the index where search starts.

But see also comments below (I'm not an expert on the ISO standard, I am limiting my expectations based on the docs I have).

The STL implementation would typically use an clearly-out-of-range value (such as ((size_type)-1). How this is handled as parameter is not clearly stated, so I wouldn't rely on that behavior. [/update]

So you need to start at 0, and check for pos != npos after every call to find:

 pos = str.find(string1, 0)
 if (pos != std:string::npos)
   pos = str.find(string2, pos)
 if (pos != std:string::npos)
   pos = str.find(string3, pos)

 if (pos != std:string::npos)
 { 
   // All strings found
 }

You should rather use as starting position the length of the string.

Passing std::string::npos as the second argument to find means "start finding on or after std::string::npos position in the string".

Clearly this is not what you intended.

EDIT:

This might do what you originally intended:

string s;
string::size_type pos;

if ((pos = s.find(s1)) != string::npos && (pos = s.find(s2, pos)) != npos && 
    (pos = s.find(s3,pos)) != string::npos)
{
    // okay
}

I haven't tested it but it should work, you might prefer peterchen style as it's more readable.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top