Выбрать главу

      test_(isPrime(i++));

      i = sieveChars.find('P', i);

    }

    i = sieveChars.find_first_not_of('P');

    while (i != string::npos) {

      test_(!isPrime(i++));

      i = sieveChars.find_first_not_of('P', i);

    }

  }

};

int main() {

  SieveTest t;

  t.run();

  return t.report();

} ///:~

The find( ) function allows you to walk forward through a string, detecting multiple occurrences of a character or a group of characters, and find_first_not_of( ) allows you to find other characters or substrings.

There are no functions in the string class to change the case of a string, but you can easily create these functions using the Standard C library functions toupper( ) and tolower( ), which change the case of one character at a time. The following example illustrates a case-insensitive search:.

//: C03:Find.cpp

//{L} ../TestSuite/Test

#include <cctype>

#include <cstddef>

#include <string>

#include "../TestSuite/Test.h"

using namespace std;

// Make an uppercase copy of s

string upperCase(const string& s) {

  string upper(s);

  for(size_t i = 0; i < s.length(); ++i)

    upper[i] = toupper(upper[i]);

  return upper;

}

// Make a lowercase copy of s

string lowerCase(const string& s) {

  string lower(s);

  for(size_t i = 0; i < s.length(); ++i)

    lower[i] = tolower(lower[i]);

  return lower;

}

class FindTest : public TestSuite::Test {

  string chooseOne;

public:

  FindTest() : chooseOne("Eenie, Meenie, Miney, Mo") {}

  void testUpper() {

    string upper = upperCase(chooseOne);

    const string LOWER = "abcdefghijklmnopqrstuvwxyz";

    test_(upper.find_first_of(LOWER) == string::npos);

  }

  void testLower() {

    string lower = lowerCase(chooseOne);

    const string UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    test_(lower.find_first_of(UPPER) == string::npos);

  }

  void testSearch() {

    // Case sensitive search

    size_t i = chooseOne.find("een");

    test_(i == 8);

    // Search lowercase:

    string test = lowerCase(chooseOne);

    i = test.find("een");

    test_(i == 0);

    i = test.find("een", ++i);

    test_(i == 8);

    i = test.find("een", ++i);

    test_(i == string::npos);

    // Search uppercase:

    test = upperCase(chooseOne);

    i = test.find("EEN");

    test_(i == 0);

    i = test.find("EEN", ++i);

    test_(i == 8);

    i = test.find("EEN", ++i);

    test_(i == string::npos);

  }

  void run() {

    testUpper();

    testLower();

    testSearch();

  }

};

int main() {

  FindTest t;

  t.run();

  return t.report();

} ///:~

Both the upperCase( ) and lowerCase( ) functions follow the same form: they make a copy of the argument string and change the case. The NewFind.cpp program isn’t the best solution to the case-sensitivity problem, so we’ll revisit it when we examine string comparisons.

Finding in reverse

Sometimes it’s necessary to search through a string from end to beginning, if you need to find the data in "last in / first out" order. The string member function rfind( ) handles this job.

//: C03:Rparse.cpp

//{L} ../TestSuite/Test

#include <string>

#include <vector>

#include "../TestSuite/Test.h"

using namespace std;

class RparseTest : public TestSuite::Test {

  // To store the words:

  vector<string> strings;

public:

  void parseForData() {

    // The ';' characters will be delimiters

    string s("now.;sense;make;to;going;is;This");

    // The last element of the string:

    int last = s.size();

    // The beginning of the current word:

    int current = s.rfind(';');

    // Walk backward through the string:

    while(current != string::npos){

      // Push each word into the vector.

      // Current is incremented before copying to

      // avoid copying the delimiter:

      ++current;

      strings.push_back(

        s.substr(current, last - current));

      // Back over the delimiter we just found,

      // and set last to the end of the next word:

      current -= 2;

      last = current + 1;

      // Find the next delimiter

      current = s.rfind(';', current);

    }

    // Pick up the first word - it's not

    // preceded by a delimiter

    strings.push_back(s.substr(0, last));

  }

  void testData() {

    // Test order them in the new order:

    test_(strings[0] == "This");

    test_(strings[1] == "is");

    test_(strings[2] == "going");

    test_(strings[3] == "to");

    test_(strings[4] == "make");

    test_(strings[5] == "sense");

    test_(strings[6] == "now.");

    string sentence;

    for(int i = 0; i < strings.size() - 1; i++)

      sentence += strings[i] += " ";

    // Manually put last word in to avoid an extra space

    sentence += strings[strings.size() - 1];

    test_(sentence == "This is going to make sense now.");

  }

  void run() {

    parseForData();

    testData();

  }

};

int main() {

  RparseTest t;

  t.run();

  return t.report();

} ///:~

The string member function rfind( ) backs through the string looking for tokens and reporting the array index of matching characters or string::npos if it is unsuccessful.

Finding first/last of a set of characters

The find_first_of( ) and find_last_of( ) member functions can be conveniently put to work to create a little utility that will strip whitespace characters from both ends of a string. Notice that it doesn’t touch the original string, but instead returns a new string:.