Решение
Пример 4.32 демонстрирует, как это делается. Если читать текст в stringнепрерывными кусками с помощью getline(шаблон функции определен в ), то для анализа текста и создания структуры данных можно использовать функцию split, которая была представлена в рецепте 4.6.
Пример 4.32. Чтение файла с разделителями
#include
#include
#include
#include
using namespace std;
void split(const string& s, char c, vector& v) {
int i = 0;
int j = s.find(c);
while (j >= 0) {
v.push_back(s.substr(i, j-i));
i = ++j;
j = s.find(c, j);
if (j < 0) {
v.push_back(s.substr(i, s.length()));
}
}
}
void loadCSV(istream& in, vector*>& data) {
vector* p = NULL;
string tmp;
while (!in.eof()) {
getline(in, tmp, '\n'); // Получить следующую строку
p = new vector();
split(tmp, '.', *p); // Использовать split из
// Рецепта 4.7
data.push_back(p);
cout << tmp << '\n';
tmp.clear();
}
}
int main(int argc, char** argv) {
if (argc < 2)
return(EXIT_FAILURE);
ifstream in(argv[1]);
if (!in)
return(EXIT_FAILURE);
vector*> data;
loadCSV(in, data);
// Выполнить с данными какие-либо действия...
for (vector*>::iterator p = data.begin();
p != data end(); ++p) {
delete *p; // Убедитесь, что p
} // разыменован!
}
Обсуждение
В примере 4.32 почти нет ничего, что еще не было бы описано, getlineобсуждается в рецепте 4.19, a vector— в рецепте 4.3. Единственный фрагмент, заслуживающий упоминания, — это выделение памяти.
loadCSVсоздает новый vectorдля каждой прочитанной строки данных и сохраняет его в другом vector, состоящем из указателей на vector. Так как память для каждого из этих векторов выделяется из кучи, кто-то должен удалить ее, и этот кто-то — это вы (а не реализация vector).
vectorничего не знает о том, содержит ли он значение или указатель на значение или что-либо еще. Все, что он знает, — это то, что при его удалении он должен вызвать деструктор для каждого содержащегося в нем элемента. Если vectorхранит объекты, то все нормально, объект будет удален правильно. Но если vectorсодержит указатели, то удалены будут указатели, а не объекты, на которые они указывают.
Есть два способа гарантировать освобождение памяти. Первый заключается в том, что сделано в примере 4.32 вручную, как здесь.
for (vector*>::iterator p = data.begin();
p != data.end(); ++p) {
delete *p;
}
Либо можно использовать указатель со счетчиком ссылок, такой как smart_ptrиз проекта Boost, который станет частью будущего стандарта C++0x. Но реализация этого нетривиальна, так что я рекомендую почитать, что такое smart_ptrи как он работает. Для получения дополнительной информации по Boost посетите его домашнюю страницу по адресу www.boost.org .
4.24. Использование регулярных выражений для разделения строки
Проблема
Требуется разделить строку на лексемы, но необходимо выполнить более сложный поиск, чем показано в рецепте 4.7. Например, могут потребоваться лексемы, разделенные более чем одним символом или имеющие несколько различных форм. Это часто приводит к большому коду и путанице среди пользователей вашего класса или функции.
Решение
Используйте шаблон класса regexBoost. regexпозволяет использовать для строк и текстовых данных регулярные выражения. Пример 4.33 показывает, как использовать regexдля разделения строк.
Пример 4.33. Использование регулярных выражений Boost
#include
#include
#include
int main() {
std::string s = "who,lives-in-a,pineapple under the sea?";
boost::regex re(',|:|-|\\s+"); // Создаем регулярное выражение
boost::sregex_token_iterator // Создаем итератор, используя
p(s.begin(), s.end(), re, -1), // последовательность и это выражение
boost::sregex_token_iterator end; // Создаем маркер
// «конец-рег-выражения»
while (p != end)
std::cout << *p++ << '\n';
}
Обсуждение
Пример 4.33 показывает, как использовать regexдля перебора соответствий регулярному выражению. Следующая строка создает регулярное выражение.
boost::regex re(' ,|:| -|\\s+");
Она гласит, что каждое соответствие регулярному выражению — это либо запятая, либо двоеточие, либо тире, либо один или несколько пробелов. Символ канала — это логический оператор OR, используемый для объединения разделителей. Следующие две строки создают итератор.
Читать дальше