Разделить строку на слова несколькими разделителями
У меня есть текст (значимый текст или арифметическое выражение), и я хочу разбить его на слова.
Если бы у меня был единственный разделитель, я бы использовал:
std::stringstream stringStream(inputString);
std::string word;
while(std::getline(stringStream, word, delimiter))
{
wordVector.push_back(word);
}
Как я могу разбить строку на токены с несколькими разделителями?
Ответы
Ответ 1
Предполагая, что один из разделителей является символом новой строки, следующее считывает строку и далее разделяет ее разделителями. В этом примере я выбрал пространство разделителей, апостроф и полуточку.
std::stringstream stringStream(inputString);
std::string line;
while(std::getline(stringStream, line))
{
std::size_t prev = 0, pos;
while ((pos = line.find_first_of(" ';", prev)) != std::string::npos)
{
if (pos > prev)
wordVector.push_back(line.substr(prev, pos-prev));
prev = pos+1;
}
if (prev < line.length())
wordVector.push_back(line.substr(prev, std::string::npos));
}
Ответ 2
Если у вас есть импульс, вы можете использовать:
#include <boost/algorithm/string.hpp>
std::string inputString("One!Two,Three:Four");
std::string delimiters("|,:");
std::vector<std::string> parts;
boost::split(parts, inputString, boost::is_any_of(delimiters));
Ответ 3
Я не знаю, почему никто не указал ручной способ, но вот он:
const std::string delims(";,:. \n\t");
inline bool isDelim(char c) {
for (int i = 0; i < delims.size(); ++i)
if (delims[i] == c)
return true;
return false;
}
и в функции:
std::stringstream stringStream(inputString);
std::string word; char c;
while (stringStream) {
word.clear();
// Read word
while (!isDelim((c = stringStream.get())))
word.push_back(c);
if (c != EOF)
stringStream.unget();
wordVector.push_back(word);
// Read delims
while (isDelim((c = stringStream.get())));
if (c != EOF)
stringStream.unget();
}
Таким образом, вы можете сделать что-то полезное с delims, если хотите.
Ответ 4
Если вам интересно, как это сделать самому и не использовать boost.
Предполагая, что строка разделителя может быть очень длинной, предположим, что M, проверяя для каждого char в вашей строке, если она является разделителем, будет стоить O (M), поэтому делать это в цикле для всех символов в вашем исходная строка, скажем, длиной N, равна O (M * N).
Я бы использовал словарь (например, map - "delimiter" для "booleans" ), но здесь я бы использовал простой логический массив, который имеет true в значении index = ascii для каждого разделителя).
Теперь итерация по строке и проверка, является ли char разделителем O (1), что в конечном итоге дает нам O (N) в целом.
Вот мой пример кода:
const int dictSize = 256;
vector<string> tokenizeMyString(const string &s, const string &del)
{
static bool dict[dictSize] = { false};
vector<string> res;
for (int i = 0; i < del.size(); ++i) {
dict[del[i]] = true;
}
string token("");
for (auto &i : s) {
if (dict[i]) {
if (!token.empty()) {
res.push_back(token);
token.clear();
}
}
else {
token += i;
}
}
if (!token.empty()) {
res.push_back(token);
}
return res;
}
int main()
{
string delString = "MyDog:Odie, MyCat:Garfield MyNumber:1001001";
//the delimiters are " " (space) and "," (comma)
vector<string> res = tokenizeMyString(delString, " ,");
for (auto &i : res) {
cout << "token: " << i << endl;
}
return 0;
}
Примечание: tokenizeMyString возвращает вектор по значению и сначала создает его в стеке, поэтому мы используем здесь мощность компилятора → > RVO - оптимизация возвращаемого значения:)
Ответ 5
Использование библиотеки Эрика Ниблера range-v3:
https://godbolt.org/z/ZnxfSa
#include <string>
#include <iostream>
#include "range/v3/all.hpp"
int main()
{
std::string s = "user1:192.168.0.1|user2:192.168.0.2|user3:192.168.0.3";
auto words = s
| ranges::view::split('|')
| ranges::view::transform([](auto w){
return w | ranges::view::split(':');
});
ranges::for_each(words, [](auto i){ std::cout << i << "\n"; });
}
Ответ 6
Использование std::regex
std::regex
может разбивать строки на несколько строк:
std::regex re("[\\|,:]");
std::sregex_token_iterator first{input.begin(), input.end(), re, -1}, last;//the '-1' is what makes the regex split (-1 := what was not matched)
std::vector<std::string> tokens{first, last};
Попробуйте сами