Назад | Начало урока | Вперед
Содержание

Глава 3

Вверх

Поиск в обратном направлении

Если поиск в строке требуется выполнять от конца к началу (чтобы искать данные "от последнего вхождения к первому"), воспользуйтесь функцией rfind():


//: C03:Rparse.h
#ifndef RPARSE_H
#define RPARSE_H
#include <cstddef>
#include <string>
#include <vector>
#include "../TestSuite/Test.h"
using std::size_t;
using std::string;
using std::vector;

class RparseTest : public TestSuite::Test {

// Вектор для хранения слов:
vector<string> strings;
public:

void parseForData() {

// Символы ';' являются ограничителями
string s("now.;sense;make;to;going;is;This");
// Последний элемент строки:
int last = s.size();
// Начало текущего слова:
size_t current = s.rfind(';');

// Перебор строки в обратном направлении:
while(current != string::npos){

// Занесние слов в вектор.
// Переменная current инкрементируется перед копированием,
// чтобы предотвратить копирование ограничителя:
++current;
strings.push_back(s.substr(current, last - current));

// Пропустить найденный ограничитель
// и установить last в конец следующего слова:
current -= 2;
last = current + 1;
// Поиск следующего ограничителя
current = s.rfind(';', current);

}

// Получение первого слова, не имеющего
// префикса-ограничителя.
strings.push_back(s.substr(0, last));

}

void testData() {

// Тестирование в новом порядке:
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] += " ";
// Вручную занести последнее слово, чтобы избежать
// сохранения лишнего пробела.
sentence += strings[strings.size() - 1];
test_(sentence == "This is going to make sense now.");
}

void run() {

parseForData();
testData();
}
};
#endif // RPARSE_H ///:~


//: C03:Rparse.cpp
//{L} ../TestSuite/Test
#include "Rparse.h"

int main() {

RparseTest t;
t.run();
return t.report();
} ///:~


Результат:

This is going to make sense now. !!!

Анализ:

Строковая функция rfind() перебирает строку в обратном направлении, ищет заданные лексемы и возвращает массив(!) индексов совпадающих символов (При отсутствии совпадений возвращает string::npos.)

Итак все по порядку:

Объявляем вектор strings.

vector strings;

Затем пишем две функции: parseForData() и testData(), которые будут вызваны в третьей функции - run().

В функции parseForData(), строковая функция rfind() перебирает строку в обратном направлении, ищет заданные лексемы. В данном случае она ищет символ ";". Найдя этот символ в первый раз, функция возвращает его позицию в строке, которую мы сохраняем в переменной current. Далее нас у есть две переменных - текущая позиция (current) и конечная позиция (last). Теперь используем эти две переменных в вызове функции substr():

s.substr(current, last - current)

Эта функция возвратит подстроку, которую мы положим в вектор при помощи его функции push_back():

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

Далее присвоим переменной last значение переменной current, и вновь вызовем функцию rfind передав ей два параметра - символ для поиска и начальную позицию поиска:

// Пропустить найденный ограничитель
// и установить last в конец следующего слова:
current -= 2;
last = current + 1;
// Поиск следующего ограничителя
current = s.rfind(';', current);
}

Так в цикле мы разобьем всю строку на подстроки и сложим все подстроки в вектор. Каждую подстроку в свою ячейку вектора. Так работает функция parseForData().

Далее есть функция testData(), которая тестирует содержимое вектора strings. И далее в цикле при помощи оператора конкатенации и перебирая все элементы вектора, складывает их в новую строку sentence. Затем тестирует содержание этой новой строки, действительно ли она содержит "This is going to make sense now.". Все верно! Программа работает!

Эккель использует свой стиль программирования. Он не выводит строки на консоль для проверки. А проверяет строку при помощи функции test_:

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

То есть сравнивает строку с эталонной. Удобный подход. Для того, чтобы он работал необходимо подключить заголовочный файл Test.h, как это сделано в программе. Поскольку у меня нет этого заголовочного файла, то я просто для проверки выводже строки на консоль, а строки типа test_(sentence == "This is going to make sense now."); ремирую.

Впрочем ничто не мешает мне написать самому подобный заголовочный файл и использовать эту технику в своих программах. Для разбиения строки на отдельные слова, в качестве разделителя мы можем использовать пробел. Кстати сделай такую программу разбиения строки на слова использовав в качестве прототипа данную программу!

Вверх

Следующая программа разбивает строку на слова и создает новую строку, в которой слова расположены в обратном порядке. В качестве разделителя используется пробел.


//: C03:Rparse.h
#ifndef RPARSE_H
#define RPARSE_H
#include <cstddef>
#include <string>
#include <vector>
#include <iostream>
using namespace std;

class RparseTest : public TestSuite::Test {

// Вектор для хранения слов:
vector <string> strings;
public:

void parseForData() {

// Символ ' ' пробел является ограничителем
string s("In fact, his sleep was so short that he never had time to sober up ");
// Последний элемент строки:
int last = s.size();
// Начало текущего слова:
size_t current = s.rfind(' ');

// Перебор строки в обратном направлении:
while(current != string::npos){

// Занесние слов в вектор.
// Переменная current инкрементируется перед копированием,
// чтобы предотвратить копирование ограничителя:
++current;
strings.push_back(s.substr(current, last - current));

// Пропустить найденный ограничитель
// и установить last в конец следующего слова:
current -= 2;
last = current + 1;
// Поиск следующего ограничителя
current = s.rfind(' ', current);

}

// Получение первого слова, не имеющего
// префикса-ограничителя.
strings.push_back(s.substr(0, last));

}

void testData() {


for(int i = 0; i < strings.size() - 1; i++)
sentence += strings[i] += " ";
// Вручную занести последнее слово, чтобы избежать
// сохранения лишнего пробела.
sentence += strings[strings.size() - 1];
}

void run() {

parseForData();
testData();
}
};
#endif // RPARSE_H ///:~


//: C03:Rparse.cpp
#include "Rparse.h"
using namespace std;

int main() {

RparseTest t;
t.run();
return 0;
} ///:~


Программа не только разбила строку на слова, но и в новой строке расположила их в обратном порядке.


Назад | Начало урока | Вверх | Вперед
Содержание

Hosted by uCoz