tags:

views:

1155

answers:

7

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
A: 

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.

Idan K
It is what I intended, I have updated the code to show how.
David Sykes
A: 

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

Paolo Tedesco
I want to use the return value of the previous find, without having to check it each time
David Sykes
+3  A: 

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.

James Hopkin
Where does the standard state this? I could only find that `npos` is of type `basic_string::size_type` and I could find no restrictions on the range of allowed values of the second parameter to this overload of find.
Charles Bailey
@Charles: Here http://www.cplusplus.com/reference/string/string/find/ it says "Position of the first character in the string to be taken into consideration for possible matches. A value of 0 means that the entire string is considered." With that documentation, I wouldn't even _try_ to pass it npos, since the behaviour may be implementation dependent.
Daniel Daranas
But the standard is far clearer on what find should do and it has no restriction on allowed behaviour for any value of the second parameter.
Charles Bailey
Agree with Charles. cplusplus.com does not define the C++ library.
MSalters
@Charles: Ok so the standard allows any value for that parameter. Then you are right and the implementation David is using has a mistake here. I leave my above comment for completeness of the discussion, although I no longer hold it :)
Daniel Daranas
A: 

Behavior is undefined if you pass npos:

[update]
STL documentation (the two reproductions I can find, anyway) mention string::npos only as possible 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
 }
peterchen
Why is the behaviour undefined when you pass npos?
Charles Bailey
@Charles Bailey: updated my response.
peterchen
But the standard doesn't have to mention explicitly what happens when you pass string::npos in. string::npos is a valid value for string::size_type and there are no explicit restrictions on the input range. As far as I can see, the description of the behaviour for string::find is still well specified for all values of the second parameter.
Charles Bailey
And note that _the_ STL documentation (the only one, by ISO) actually does state which argument values are valid, if that is a subset. Cf. basic_string::copy() on the preceding page. // Furthermore, no STL implementation may or can use a negative value for npos, as its type is unsigned (and some sort of integer).
MSalters
Good point, MSalters. I've removed the respective sentence above. Still, I wouldn't expect a return value of npos if the start index is not withoin the string limits - unless that would be stated explicitly in the find documentation, or implicitly in the documentation conventions.
peterchen
Returning npos follows naturally from its "Effects:" section. It's what the function returns if there is no equality between the elements. And those elements are specified via .at(), not the more natural operator[] so it's clear that an out-of-range index does not trigger undefined behavior.
MSalters
+6  A: 

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.

Charles Bailey
+2  A: 

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() );
Charles Bailey
+2  A: 

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().

MSalters