Уже задано много вопросов о том, как токенизировать поток в C ++.
Пример: Как прочитать файл и получить слова на C ++
Но что сложнее найти, так это получить ту же функциональность, что и strtok ():
В основном strtok () позволяет разделить строку на целую группу пользовательских символов, в то время как поток C ++ позволяет использовать только white space
в качестве разделителя. К счастью, определение white space
определяется локалью, поэтому мы можем изменить локаль, чтобы другие символы воспринимались как пробел, и это позволит нам токенизировать поток более естественным образом.
#include <locale>
#include <string>
#include <sstream>
#include <iostream>
// This is my facet that will treat the ,.- as space characters and thus ignore them.
class WordSplitterFacet: public std::ctype<char>
{
public:
typedef std::ctype<char> base;
typedef base::char_type char_type;
WordSplitterFacet(std::locale const& l)
: base(table)
{
std::ctype<char> const& defaultCType = std::use_facet<std::ctype<char> >(l);
// Copy the default value from the provided locale
static char data[256];
for(int loop = 0;loop < 256;++loop) { data[loop] = loop;}
defaultCType.is(data, data+256, table);
// Modifications to default to include extra space types.
table[','] |= base::space;
table['.'] |= base::space;
table['-'] |= base::space;
}
private:
base::mask table[256];
};
Затем мы можем использовать этот аспект в локальном, как это:
std::ctype<char>* wordSplitter(new WordSplitterFacet(std::locale()));
<stream>.imbue(std::locale(std::locale(), wordSplitter));
Следующая часть вашего вопроса - как мне хранить эти слова в массиве? Ну, в C ++ вы бы не стали. Вы бы делегировали эту функциональность std :: vector / std :: string. Прочитав ваш код, вы увидите, что ваш код выполняет две основные функции в одной и той же части кода.
- Управляет памятью.
- Это токенизация данных.
Существует базовый принцип Separation of Concerns
, когда ваш код должен только попытаться сделать одну из двух вещей. Он должен либо осуществлять управление ресурсами (в данном случае управление памятью), либо бизнес-логику (токенизацию данных). Разделяя их на разные части кода, вы делаете код более простым в использовании и написании. К счастью, в этом примере все управление ресурсами уже осуществляется std :: vector / std :: string, что позволяет нам сосредоточиться на бизнес-логике.
Как уже много раз было показано, простой способ токенизации потока - использование оператора >> и строки. Это разобьет поток на слова. Затем вы можете использовать итераторы для автоматического циклического прохождения потока по токенизации потока.
std::vector<std::string> data;
for(std::istream_iterator<std::string> loop(<stream>); loop != std::istream_iterator<std::string>(); ++loop)
{
// In here loop is an iterator that has tokenized the stream using the
// operator >> (which for std::string reads one space separated word.
data.push_back(*loop);
}
Если мы объединяем это с некоторыми стандартными алгоритмами для упрощения кода.
std::copy(std::istream_iterator<std::string>(<stream>), std::istream_iterator<std::string>(), std::back_inserter(data));
Теперь объединяем все вышеперечисленное в одно приложение
int main()
{
// Create the facet.
std::ctype<char>* wordSplitter(new WordSplitterFacet(std::locale()));
// Here I am using a string stream.
// But any stream can be used. Note you must imbue a stream before it is used.
// Otherwise the imbue() will silently fail.
std::stringstream teststr;
teststr.imbue(std::locale(std::locale(), wordSplitter));
// Now that it is imbued we can use it.
// If this was a file stream then you could open it here.
teststr << "This, stri,plop";
cout << "die monster !";
std::vector<std::string> data;
std::copy(std::istream_iterator<std::string>(teststr), std::istream_iterator<std::string>(), std::back_inserter(data));
// Copy the array to cout one word per line
std::copy(data.begin(), data.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
}