Я пытаюсь перебрать слова строки.
Можно предположить, что строка состоит из слов, разделенных пробелами.
Обратите внимание, что меня не интересуют строковые функции Си или подобные манипуляции/доступ к символам. Кроме того, пожалуйста, дайте приоритет элегантности над эффективностью в вашем ответе.
Лучшее решение, которое у меня есть сейчас:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main()
{
string s = "Somewhere down the road";
istringstream iss(s);
do
{
string subs;
iss >> subs;
cout << "Substring: " << subs << endl;
} while (iss);
}
Есть ли более элегантный способ сделать это?
Для чего это стоит, здесь другой способ извлечь токены из входной строки, полагаясь только на стандартные библиотеки. Это пример силы и элегантности дизайна STL.
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <iterator>
int main() {
using namespace std;
string sentence = "And I feel fine...";
istringstream iss(sentence);
copy(istream_iterator<string>(iss),
istream_iterator<string>(),
ostream_iterator<string>(cout, "\n"));
}
Вместо копирования извлеченных токенов в выходной поток можно вставить их в контейнер, используя тот же общий алгоритм copy
.
vector<string> tokens;
copy(istream_iterator<string>(iss),
istream_iterator<string>(),
back_inserter(tokens));
... или создайте vector
напрямую:
vector<string> tokens{istream_iterator<string>{iss},
istream_iterator<string>{}};
Я использую это для разделения строки на разделитель. Первый помещает результаты в предварительно сконструированный вектор, второй возвращает новый вектор.
#include <string>
#include <sstream>
#include <vector>
#include <iterator>
template<typename Out>
void split(const std::string &s, char delim, Out result) {
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim)) {
*(result++) = item;
}
}
std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
split(s, delim, std::back_inserter(elems));
return elems;
}
Обратите внимание, что это решение не пропускает пустые токены, поэтому следующее будет найдено 4 элемента, один из которых пуст:
std::vector<std::string> x = split("one:two::three", ':');
split("one two three", ' ');
возвращает вектор с 4 элементами? Я не уверен, что это так, но я проверю это.
Возможное решение с использованием Boost может быть:
#include <boost/algorithm/string.hpp>
std::vector<std::string> strs;
boost::split(strs, "string to split", boost::is_any_of("\t "));
Этот подход может быть даже быстрее, чем подход stringstream
. И поскольку это универсальная функция шаблона, она может использоваться для разделения других типов строк (wchar и т.д. Или UTF-8) с использованием всех видов разделителей.
Подробнее см. .
#include <vector>
#include <string>
#include <sstream>
int main()
{
std::string str("Split me by whitespaces");
std::string buf; // Have a buffer string
std::stringstream ss(str); // Insert the string into a stream
std::vector<std::string> tokens; // Create vector to hold our words
while (ss >> buf)
tokens.push_back(buf);
return 0;
}
' '
...
getline
в условии while
например, чтобы разделить запятыми, используйте while(getline(ss, buff, ','))
.
Для тех, с кем он не подходит, чтобы пожертвовать всей эффективностью для размера кода и увидеть "эффективный" как тип элегантности, следующее должно поразить сладкое место (и я думаю, что класс контейнера-шаблона - это удивительно элегантное дополнение.):
template < class ContainerT >
void tokenize(const std::string& str, ContainerT& tokens,
const std::string& delimiters = " ", bool trimEmpty = false)
{
std::string::size_type pos, lastPos = 0, length = str.length();
using value_type = typename ContainerT::value_type;
using size_type = typename ContainerT::size_type;
while(lastPos < length + 1)
{
pos = str.find_first_of(delimiters, lastPos);
if(pos == std::string::npos)
{
pos = length;
}
if(pos != lastPos || !trimEmpty)
tokens.push_back(value_type(str.data()+lastPos,
(size_type)pos-lastPos ));
lastPos = pos + 1;
}
}
Обычно я предпочитаю использовать std::vector<std::string>
типы как мой второй параметр (ContainerT
)... но list<>
быстрее, чем vector<>
, когда прямой доступ не нужен, и вы даже можете создать свой собственный string class и используйте что-то вроде std::list<subString>
, где subString
не делает копии для невероятной скорости.
Это более чем вдвое быстрее, чем самый быстрый токенизировать на этой странице и почти в 5 раз быстрее, чем некоторые другие. Также с идеальными типами параметров вы можете исключить все копии строк и списков для дополнительного увеличения скорости.
Кроме того, он не выполняет (крайне неэффективный) возврат результата, а скорее передает токены в качестве ссылки, что также позволяет вам создавать токены, используя несколько вызовов, если вы этого пожелаете.
Наконец, он позволяет указать, нужно ли обрезать пустые токены из результатов с помощью последнего необязательного параметра.
Все, что ему нужно, это std::string
... остальные необязательны. Он не использует потоки или библиотеку boost, но достаточно гибкий, чтобы естественным образом принимать некоторые из этих иностранных типов.
typedef ContainerT Base; typedef typename Base::value_type ValueType; typedef typename ValueType::size_type SizeType;
Затем подставить значения value_type и size_types соответственно.
Здесь другое решение. Он компактен и достаточно эффективен:
std::vector<std::string> split(const std::string &text, char sep) {
std::vector<std::string> tokens;
std::size_t start = 0, end = 0;
while ((end = text.find(sep, start)) != std::string::npos) {
tokens.push_back(text.substr(start, end - start));
start = end + 1;
}
tokens.push_back(text.substr(start));
return tokens;
}
Его можно легко запланировать, чтобы обрабатывать разделители строк, широкие строки и т.д.
Обратите внимание, что разбиение ""
приводит к одной пустой строке, а разбиение ","
(т.е. sep) приводит к двум пустым строкам.
Его также можно легко развернуть, чтобы пропустить пустые токены:
std::vector<std::string> split(const std::string &text, char sep) {
std::vector<std::string> tokens;
std::size_t start = 0, end = 0;
while ((end = text.find(sep, start)) != std::string::npos) {
if (end != start) {
tokens.push_back(text.substr(start, end - start));
}
start = end + 1;
}
if (end != start) {
tokens.push_back(text.substr(start));
}
return tokens;
}
Если требуется разделение строки на нескольких разделителях при пропуске пустых токенов, эта версия может быть использована:
std::vector<std::string> split(const std::string& text, const std::string& delims)
{
std::vector<std::string> tokens;
std::size_t start = text.find_first_not_of(delims), end = 0;
while((end = text.find_first_of(delims, start)) != std::string::npos)
{
tokens.push_back(text.substr(start, end - start));
start = text.find_first_not_of(delims, end);
}
if(start != std::string::npos)
tokens.push_back(text.substr(start));
return tokens;
}
Это мой любимый способ перебора строки. Вы можете делать все, что хотите, за каждое слово.
string line = "a line of text to iterate through";
string word;
istringstream iss(line, istringstream::in);
while( iss >> word )
{
// Do something on `word` here...
}
word
как char
?
Это похоже на Stack вопрос с переполнением Как я хочу tokenize строку в С++?.
#include <iostream>
#include <string>
#include <boost/tokenizer.hpp>
using namespace std;
using namespace boost;
int main(int argc, char** argv)
{
string text = "token test\tstring";
char_separator<char> sep(" \t");
tokenizer<char_separator<char>> tokens(text, sep);
for (const string& t : tokens)
{
cout << t << "." << endl;
}
}
Мне нравится следующее, потому что он помещает результаты в вектор, поддерживает строку как разделитель и дает контроль над сохранением пустых значений. Но это не выглядит так хорошо.
#include <ostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
vector<string> split(const string& s, const string& delim, const bool keep_empty = true) {
vector<string> result;
if (delim.empty()) {
result.push_back(s);
return result;
}
string::const_iterator substart = s.begin(), subend;
while (true) {
subend = search(substart, s.end(), delim.begin(), delim.end());
string temp(substart, subend);
if (keep_empty || !temp.empty()) {
result.push_back(temp);
}
if (subend == s.end()) {
break;
}
substart = subend + delim.size();
}
return result;
}
int main() {
const vector<string> words = split("So close no matter how far", " ");
copy(words.begin(), words.end(), ostream_iterator<string>(cout, "\n"));
}
Конечно, Boost имеет split()
, который работает частично так. И, если по "белому пространству" вы действительно имеете в виду любой тип белого пространства, использование разделения Boost с is_any_of()
отлично работает.
STL уже не имеет такого метода.
Однако вы можете использовать функцию C strtok()
, используя std::string::c_str()
, или вы можете написать свой собственный. Вот пример кода, который я нашел после быстрого поиска Google ("Разделение строки STL"):
void Tokenize(const string& str,
vector<string>& tokens,
const string& delimiters = " ")
{
// Skip delimiters at beginning.
string::size_type lastPos = str.find_first_not_of(delimiters, 0);
// Find first "non-delimiter".
string::size_type pos = str.find_first_of(delimiters, lastPos);
while (string::npos != pos || string::npos != lastPos)
{
// Found a token, add it to the vector.
tokens.push_back(str.substr(lastPos, pos - lastPos));
// Skip delimiters. Note the "not_of"
lastPos = str.find_first_not_of(delimiters, pos);
// Find next "non-delimiter"
pos = str.find_first_of(delimiters, lastPos);
}
}
Взято из: http://oopweb.com/CPP/Documents/CPPHOWTO/Volume/C++Programming-HOWTO-7.html
Если у вас есть вопросы о образце кода, оставьте комментарий, и я объясню.
И только потому, что он не реализует typedef
называемый итератором или перегрузкой, оператор <<
не означает, что это плохой код. Я использую C-функции довольно часто. Например, printf
и scanf
работают быстрее, чем std::cin
и std::cout
(значительно), синтаксис fopen
намного более дружелюбен для двоичных типов, и они также имеют тенденцию создавать меньшие EXE.
Не продавайте эту сделку "Elegance over performance".
Вот функция разделения, которая:
игнорирует пустые токены (их можно легко изменить)
template<typename T>
vector<T>
split(const T & str, const T & delimiters) {
vector<T> v;
typename T::size_type start = 0;
auto pos = str.find_first_of(delimiters, start);
while(pos != T::npos) {
if(pos != start) // ignore empty tokens
v.emplace_back(str, start, pos - start);
start = pos + 1;
pos = str.find_first_of(delimiters, start);
}
if(start < str.length()) // ignore trailing delimiter
v.emplace_back(str, start, str.length() - start); // add what left of the string
return v;
}
Пример использования:
vector<string> v = split<string>("Hello, there; World", ";,");
vector<wstring> v = split<wstring>(L"Hello, there; World", L";,");
У меня есть 2-строчное решение этой проблемы:
char sep = ' ';
std::string s="1 This is an example";
for(size_t p=0, q=0; p!=s.npos; p=q)
std::cout << s.substr(p+(p!=0), (q=s.find(sep, p+1))-p-(p!=0)) << std::endl;
Затем вместо печати вы можете поместить его в вектор.
Еще один гибкий и быстрый способ
template<typename Operator>
void tokenize(Operator& op, const char* input, const char* delimiters) {
const char* s = input;
const char* e = s;
while (*e != 0) {
e = s;
while (*e != 0 && strchr(delimiters, *e) == 0) ++e;
if (e - s > 0) {
op(s, e - s);
}
s = e + 1;
}
}
Использовать его с вектором строк (Edit: Поскольку кто-то указал, что не наследует классы STL... hrmf;)):
template<class ContainerType>
class Appender {
public:
Appender(ContainerType& container) : container_(container) {;}
void operator() (const char* s, unsigned length) {
container_.push_back(std::string(s,length));
}
private:
ContainerType& container_;
};
std::vector<std::string> strVector;
Appender v(strVector);
tokenize(v, "A number of words to be tokenized", " \t");
Что это! И это всего лишь один способ использовать токенизатор, например, как просто считать слова:
class WordCounter {
public:
WordCounter() : noOfWords(0) {}
void operator() (const char*, unsigned) {
++noOfWords;
}
unsigned noOfWords;
};
WordCounter wc;
tokenize(wc, "A number of words to be counted", " \t");
ASSERT( wc.noOfWords == 7 );
Ограничено воображением;)
Appender
"Почему мы не должны наследовать класс от классов STL?"
Здесь простое решение, в котором используется только стандартная библиотека регулярных выражений
#include <regex>
#include <string>
#include <vector>
std::vector<string> Tokenize( const string str, const std::regex regex )
{
using namespace std;
std::vector<string> result;
sregex_token_iterator it( str.begin(), str.end(), regex, -1 );
sregex_token_iterator reg_end;
for ( ; it != reg_end; ++it ) {
if ( !it->str().empty() ) //token could be empty:check
result.emplace_back( it->str() );
}
return result;
}
Аргумент regex позволяет проверять несколько аргументов (пробелы, запятые и т.д.)
Обычно я проверяю только разбиение на пробелы и запятые, поэтому у меня также есть эта функция по умолчанию:
std::vector<string> TokenizeDefault( const string str )
{
using namespace std;
regex re( "[\\s,]+" );
return Tokenize( str, re );
}
"[\\s,]+"
проверяет пробелы (\\s
) и запятые (,
).
Обратите внимание, что если вы хотите разделить wstring
вместо string
,
std::regex
на std::wregex
sregex_token_iterator
на wsregex_token_iterator
Обратите внимание, что вы также можете взять строковый аргумент по ссылке, в зависимости от вашего компилятора.
Если вам нравится использовать boost, но вы хотите использовать целую строку в качестве разделителя (вместо одиночных символов, как в большинстве предложенных ранее решений), вы можете использовать boost_split_iterator
.
Пример кода, включающего удобный шаблон:
#include <iostream>
#include <vector>
#include <boost/algorithm/string.hpp>
template<typename _OutputIterator>
inline void split(
const std::string& str,
const std::string& delim,
_OutputIterator result)
{
using namespace boost::algorithm;
typedef split_iterator<std::string::const_iterator> It;
for(It iter=make_split_iterator(str, first_finder(delim, is_equal()));
iter!=It();
++iter)
{
*(result++) = boost::copy_range<std::string>(*iter);
}
}
int main(int argc, char* argv[])
{
using namespace std;
vector<string> splitted;
split("HelloFOOworldFOO!", "FOO", back_inserter(splitted));
// or directly to console, for example
split("HelloFOOworldFOO!", "FOO", ostream_iterator<string>(cout, "\n"));
return 0;
}
Использование std::stringstream
как вы отлично работаете, и делаете именно то, что хотите. Если вы просто ищете другой способ сделать что-то, вы можете использовать std::find()
/ std::find_first_of()
и std::string::substr()
.
Вот пример:
#include <iostream>
#include <string>
int main()
{
std::string s("Somewhere down the road");
std::string::size_type prev_pos = 0, pos = 0;
while( (pos = s.find(' ', pos)) != std::string::npos )
{
std::string substring( s.substr(prev_pos, pos-prev_pos) );
std::cout << substring << '\n';
prev_pos = ++pos;
}
std::string substring( s.substr(prev_pos, pos-prev_pos) ); // Last word
std::cout << substring << '\n';
return 0;
}
prev_pos = pos += delimiter.length();
: prev_pos = pos += delimiter.length();
Существует функция с именем strtok
.
#include<string>
using namespace std;
vector<string> split(char* str,const char* delim)
{
char* saveptr;
char* token = strtok_r(str,delim,&saveptr);
vector<string> result;
while(token != NULL)
{
result.push_back(token);
token = strtok_r(NULL,delim,&saveptr);
}
return result;
}
strtok
из стандартной библиотеки C, а не C ++. Не безопасно использовать в многопоточных программах. Он изменяет строку ввода.
strtok
когда другой поток все еще обрабатывает, этот указатель на символ будет перезаписан, и тогда оба потока будут иметь неверные результаты. mkssoftware.com/docs/man3/strtok.3.asp
Вот решение регулярного выражения, которое использует только стандартную библиотеку регулярных выражений. (Я немного ржавый, поэтому могут быть несколько синтаксических ошибок, но это, по крайней мере, общая идея)
#include <regex.h>
#include <string.h>
#include <vector.h>
using namespace std;
vector<string> split(string s){
regex r ("\\w+"); //regex matches whole words, (greedy, so no fragment words)
regex_iterator<string::iterator> rit ( s.begin(), s.end(), r );
regex_iterator<string::iterator> rend; //iterators to iterate thru words
vector<string> result<regex_iterator>(rit, rend);
return result; //iterates through the matches to fill the vector
}
Строковый поток может быть удобным, если вам нужно проанализировать строку с помощью непространственных символов:
string s = "Name:JAck; Spouse:Susan; ...";
string dummy, name, spouse;
istringstream iss(s);
getline(iss, dummy, ':');
getline(iss, name, ';');
getline(iss, dummy, ':');
getline(iss, spouse, ';')
Короткие и элегантные
#include <vector>
#include <string>
using namespace std;
vector<string> split(string data, string token)
{
vector<string> output;
size_t pos = string::npos; // size_t to avoid improbable overflow
do
{
pos = data.find(token);
output.push_back(data.substr(0, pos));
if (string::npos != pos)
data = data.substr(pos + token.size());
} while (string::npos != pos);
return output;
}
может использовать любую строку в качестве разделителя, также может использоваться с двоичными данными (std::string поддерживает двоичные данные, включая нули)
с помощью:
auto a = split("this!!is!!!example!string", "!!");
выход:
this
is
!example!string
До сих пор я использовал тот, что был в Boost, но мне было нужно что-то, что от него не зависит, поэтому я пришел к этому
static void Split(std::vector<std::string>& lst, const std::string& input, const std::string& separators, bool remove_empty = true)
{
std::ostringstream word;
for (size_t n = 0; n < input.size(); ++n)
{
if (std::string::npos == separators.find(input[n]))
word << input[n];
else
{
if (!word.str().empty() || !remove_empty)
lst.push_back(word.str());
word.str("");
}
}
if (!word.str().empty() || !remove_empty)
lst.push_back(word.str());
}
Хорошо, что в separators
вы можете передать более одного символа.
Я перекатил свой собственный, используя strtok, и использовал boost для разделения строки. Лучший метод, который я нашел, это С++ String Toolkit Library. Это невероятно гибко и быстро.
#include <iostream>
#include <vector>
#include <string>
#include <strtk.hpp>
const char *whitespace = " \t\r\n\f";
const char *whitespace_and_punctuation = " \t\r\n\f;,=";
int main()
{
{ // normal parsing of a string into a vector of strings
std::string s("Somewhere down the road");
std::vector<std::string> result;
if( strtk::parse( s, whitespace, result ) )
{
for(size_t i = 0; i < result.size(); ++i )
std::cout << result[i] << std::endl;
}
}
{ // parsing a string into a vector of floats with other separators
// besides spaces
std::string s("3.0, 3.14; 4.0");
std::vector<float> values;
if( strtk::parse( s, whitespace_and_punctuation, values ) )
{
for(size_t i = 0; i < values.size(); ++i )
std::cout << values[i] << std::endl;
}
}
{ // parsing a string into specific variables
std::string s("angle = 45; radius = 9.9");
std::string w1, w2;
float v1, v2;
if( strtk::parse( s, whitespace_and_punctuation, w1, v1, w2, v2) )
{
std::cout << "word " << w1 << ", value " << v1 << std::endl;
std::cout << "word " << w2 << ", value " << v2 << std::endl;
}
}
return 0;
}
Инструментарий обладает гораздо большей гибкостью, чем показывает этот простой пример, но его полезность при разборе строки в полезных элементах невероятна.
Я сделал это, потому что мне был нужен простой способ разделить строки и строки на основе c... Надеюсь, кто-то еще сможет найти его полезным. Также он не полагается на токены, и вы можете использовать поля в качестве разделителей, что еще один ключ, который мне нужен.
Я уверен, что есть улучшения, которые можно улучшить, чтобы еще больше улучшить его элегантность и, пожалуйста, всеми силами
StringSplitter.hpp:
#include <vector>
#include <iostream>
#include <string.h>
using namespace std;
class StringSplit
{
private:
void copy_fragment(char*, char*, char*);
void copy_fragment(char*, char*, char);
bool match_fragment(char*, char*, int);
int untilnextdelim(char*, char);
int untilnextdelim(char*, char*);
void assimilate(char*, char);
void assimilate(char*, char*);
bool string_contains(char*, char*);
long calc_string_size(char*);
void copy_string(char*, char*);
public:
vector<char*> split_cstr(char);
vector<char*> split_cstr(char*);
vector<string> split_string(char);
vector<string> split_string(char*);
char* String;
bool do_string;
bool keep_empty;
vector<char*> Container;
vector<string> ContainerS;
StringSplit(char * in)
{
String = in;
}
StringSplit(string in)
{
size_t len = calc_string_size((char*)in.c_str());
String = new char[len + 1];
memset(String, 0, len + 1);
copy_string(String, (char*)in.c_str());
do_string = true;
}
~StringSplit()
{
for (int i = 0; i < Container.size(); i++)
{
if (Container[i] != NULL)
{
delete[] Container[i];
}
}
if (do_string)
{
delete[] String;
}
}
};
StringSplitter.cpp:
#include <string.h>
#include <iostream>
#include <vector>
#include "StringSplit.hpp"
using namespace std;
void StringSplit::assimilate(char*src, char delim)
{
int until = untilnextdelim(src, delim);
if (until > 0)
{
char * temp = new char[until + 1];
memset(temp, 0, until + 1);
copy_fragment(temp, src, delim);
if (keep_empty || *temp != 0)
{
if (!do_string)
{
Container.push_back(temp);
}
else
{
string x = temp;
ContainerS.push_back(x);
}
}
else
{
delete[] temp;
}
}
}
void StringSplit::assimilate(char*src, char* delim)
{
int until = untilnextdelim(src, delim);
if (until > 0)
{
char * temp = new char[until + 1];
memset(temp, 0, until + 1);
copy_fragment(temp, src, delim);
if (keep_empty || *temp != 0)
{
if (!do_string)
{
Container.push_back(temp);
}
else
{
string x = temp;
ContainerS.push_back(x);
}
}
else
{
delete[] temp;
}
}
}
long StringSplit::calc_string_size(char* _in)
{
long i = 0;
while (*_in++)
{
i++;
}
return i;
}
bool StringSplit::string_contains(char* haystack, char* needle)
{
size_t len = calc_string_size(needle);
size_t lenh = calc_string_size(haystack);
while (lenh--)
{
if (match_fragment(haystack + lenh, needle, len))
{
return true;
}
}
return false;
}
bool StringSplit::match_fragment(char* _src, char* cmp, int len)
{
while (len--)
{
if (*(_src + len) != *(cmp + len))
{
return false;
}
}
return true;
}
int StringSplit::untilnextdelim(char* _in, char delim)
{
size_t len = calc_string_size(_in);
if (*_in == delim)
{
_in += 1;
return len - 1;
}
int c = 0;
while (*(_in + c) != delim && c < len)
{
c++;
}
return c;
}
int StringSplit::untilnextdelim(char* _in, char* delim)
{
int s = calc_string_size(delim);
int c = 1 + s;
if (!string_contains(_in, delim))
{
return calc_string_size(_in);
}
else if (match_fragment(_in, delim, s))
{
_in += s;
return calc_string_size(_in);
}
while (!match_fragment(_in + c, delim, s))
{
c++;
}
return c;
}
void StringSplit::copy_fragment(char* dest, char* src, char delim)
{
if (*src == delim)
{
src++;
}
int c = 0;
while (*(src + c) != delim && *(src + c))
{
*(dest + c) = *(src + c);
c++;
}
*(dest + c) = 0;
}
void StringSplit::copy_string(char* dest, char* src)
{
int i = 0;
while (*(src + i))
{
*(dest + i) = *(src + i);
i++;
}
}
void StringSplit::copy_fragment(char* dest, char* src, char* delim)
{
size_t len = calc_string_size(delim);
size_t lens = calc_string_size(src);
if (match_fragment(src, delim, len))
{
src += len;
lens -= len;
}
int c = 0;
while (!match_fragment(src + c, delim, len) && (c < lens))
{
*(dest + c) = *(src + c);
c++;
}
*(dest + c) = 0;
}
vector<char*> StringSplit::split_cstr(char Delimiter)
{
int i = 0;
while (*String)
{
if (*String != Delimiter && i == 0)
{
assimilate(String, Delimiter);
}
if (*String == Delimiter)
{
assimilate(String, Delimiter);
}
i++;
String++;
}
String -= i;
delete[] String;
return Container;
}
vector<string> StringSplit::split_string(char Delimiter)
{
do_string = true;
int i = 0;
while (*String)
{
if (*String != Delimiter && i == 0)
{
assimilate(String, Delimiter);
}
if (*String == Delimiter)
{
assimilate(String, Delimiter);
}
i++;
String++;
}
String -= i;
delete[] String;
return ContainerS;
}
vector<char*> StringSplit::split_cstr(char* Delimiter)
{
int i = 0;
size_t LenDelim = calc_string_size(Delimiter);
while(*String)
{
if (!match_fragment(String, Delimiter, LenDelim) && i == 0)
{
assimilate(String, Delimiter);
}
if (match_fragment(String, Delimiter, LenDelim))
{
assimilate(String,Delimiter);
}
i++;
String++;
}
String -= i;
delete[] String;
return Container;
}
vector<string> StringSplit::split_string(char* Delimiter)
{
do_string = true;
int i = 0;
size_t LenDelim = calc_string_size(Delimiter);
while (*String)
{
if (!match_fragment(String, Delimiter, LenDelim) && i == 0)
{
assimilate(String, Delimiter);
}
if (match_fragment(String, Delimiter, LenDelim))
{
assimilate(String, Delimiter);
}
i++;
String++;
}
String -= i;
delete[] String;
return ContainerS;
}
Примеры:
int main(int argc, char*argv[])
{
StringSplit ss = "This:CUT:is:CUT:an:CUT:example:CUT:cstring";
vector<char*> Split = ss.split_cstr(":CUT:");
for (int i = 0; i < Split.size(); i++)
{
cout << Split[i] << endl;
}
return 0;
}
Будет выводиться:
Это
это
пример
CString
int main(int argc, char*argv[])
{
StringSplit ss = "This:is:an:example:cstring";
vector<char*> Split = ss.split_cstr(':');
for (int i = 0; i < Split.size(); i++)
{
cout << Split[i] << endl;
}
return 0;
}
int main(int argc, char*argv[])
{
string mystring = "This[SPLIT]is[SPLIT]an[SPLIT]example[SPLIT]string";
StringSplit ss = mystring;
vector<string> Split = ss.split_string("[SPLIT]");
for (int i = 0; i < Split.size(); i++)
{
cout << Split[i] << endl;
}
return 0;
}
int main(int argc, char*argv[])
{
string mystring = "This|is|an|example|string";
StringSplit ss = mystring;
vector<string> Split = ss.split_string('|');
for (int i = 0; i < Split.size(); i++)
{
cout << Split[i] << endl;
}
return 0;
}
Сохранять пустые записи (по умолчанию исключаются пустые записи):
StringSplit ss = mystring;
ss.keep_empty = true;
vector<string> Split = ss.split_string(":DELIM:");
Цель заключалась в том, чтобы сделать его похожим на метод С# Split(), где разделение строки так же просто, как:
String[] Split =
"Hey:cut:what's:cut:your:cut:name?".Split(new[]{":cut:"}, StringSplitOptions.None);
foreach(String X in Split)
{
Console.Write(X);
}
Я надеюсь, что кто-то еще найдет это полезным, как я.
Как насчет этого:
#include <string>
#include <vector>
using namespace std;
vector<string> split(string str, const char delim) {
vector<string> v;
string tmp;
for(string::const_iterator i; i = str.begin(); i <= str.end(); ++i) {
if(*i != delim && i != str.end()) {
tmp += *i;
} else {
v.push_back(tmp);
tmp = "";
}
}
return v;
}
Этот ответ берет строку и помещает ее в вектор строк. Он использует библиотеку boost.
#include <boost/algorithm/string.hpp>
std::vector<std::string> strs;
boost::split(strs, "string to split", boost::is_any_of("\t "));
#include<iostream>
#include<string>
#include<sstream>
#include<vector>
using namespace std;
vector<string> split(const string &s, char delim) {
vector<string> elems;
stringstream ss(s);
string item;
while (getline(ss, item, delim)) {
elems.push_back(item);
}
return elems;
}
int main() {
vector<string> x = split("thi is an sample test",' ');
unsigned int i;
for(i=0;i<x.size();i++)
cout<<i<<":"<<x[i]<<endl;
return 0;
}
Недавно мне пришлось разбить слово с верблюжьим словом на подслов. Нет разделителей, только верхние символы.
#include <string>
#include <list>
#include <locale> // std::isupper
template<class String>
const std::list<String> split_camel_case_string(const String &s)
{
std::list<String> R;
String w;
for (String::const_iterator i = s.begin(); i < s.end(); ++i) { {
if (std::isupper(*i)) {
if (w.length()) {
R.push_back(w);
w.clear();
}
}
w += *i;
}
if (w.length())
R.push_back(w);
return R;
}
Например, это разделяет "AQueryTrades" на "A", "Query" и "Trades". Функция работает с узкими и широкими строками. Поскольку он уважает нынешний язык, он разбивает "RaumfahrtÜberwachungsVerordnung" на "Raumfahrt", "Überwachungs" и "Verordnung".
Примечание std::upper
должно быть действительно передано как аргумент шаблона функции. Тогда более обобщенная из этой функции может разбиваться на разделители типа ","
, ";"
или " "
тоже.
std::isupper
мог быть передан в качестве аргумента, а не std::upper
. Во-вторых, поместите typename
перед String::const_iterator
.
Мне нравится использовать методы boost/regex для этой задачи, поскольку они обеспечивают максимальную гибкость для задания критериев разделения.
#include <iostream>
#include <string>
#include <boost/regex.hpp>
int main() {
std::string line("A:::line::to:split");
const boost::regex re(":+"); // one or more colons
// -1 means find inverse matches aka split
boost::sregex_token_iterator tokens(line.begin(),line.end(),re,-1);
boost::sregex_token_iterator end;
for (; tokens != end; ++tokens)
std::cout << *tokens << std::endl;
}
Вот еще один способ сделать это.
void split_string(string text,vector<string>& words)
{
int i=0;
char ch;
string word;
while(ch=text[i++])
{
if (isspace(ch))
{
if (!word.empty())
{
words.push_back(word);
}
word = "";
}
else
{
word += ch;
}
}
if (!word.empty())
{
words.push_back(word);
}
}
Получить Boost!: -)
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string.hpp>
#include <iostream>
#include <vector>
using namespace std;
using namespace boost;
int main(int argc, char**argv) {
typedef vector < string > list_type;
list_type list;
string line;
line = "Somewhere down the road";
split(list, line, is_any_of(" "));
for(int i = 0; i < list.size(); i++)
{
cout << list[i] << endl;
}
return 0;
}
В этом примере выводится вывод -
Somewhere
down
the
road
В приведенном ниже коде используется strtok()
, чтобы разбить строку на токены и сохранить токены в векторе.
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
char one_line_string[] = "hello hi how are you nice weather we are having ok then bye";
char seps[] = " ,\t\n";
char *token;
int main()
{
vector<string> vec_String_Lines;
token = strtok( one_line_string, seps );
cout << "Extracting and storing data in a vector..\n\n\n";
while( token != NULL )
{
vec_String_Lines.push_back(token);
token = strtok( NULL, seps );
}
cout << "Displaying end result in vector line storage..\n\n";
for ( int i = 0; i < vec_String_Lines.size(); ++i)
cout << vec_String_Lines[i] << "\n";
cout << "\n\n\n";
return 0;
}
Я использую этот простак, потому что мы получили класс String "специальный" (т.е. не стандартный):
void splitString(const String &s, const String &delim, std::vector<String> &result) {
const int l = delim.length();
int f = 0;
int i = s.indexOf(delim,f);
while (i>=0) {
String token( i-f > 0 ? s.substring(f,i-f) : "");
result.push_back(token);
f=i+l;
i = s.indexOf(delim,f);
}
String token = s.substring(f);
result.push_back(token);
}
#include <iostream>
#include <regex>
using namespace std;
int main() {
string s = "foo bar baz";
regex e("\\s+");
regex_token_iterator<string::iterator> i(s.begin(), s.end(), e, -1);
regex_token_iterator<string::iterator> end;
while (i != end)
cout << " [" << *i++ << "]";
}
IMO, это самое близкое к python re.split(). См. cplusplus.com для получения дополнительной информации о regex_token_iterator. -1 (4-й аргумент в regex_token_iterator ctor) - это раздел последовательности, которая не сопоставлена, используя совпадение как разделитель.
Это намного лучший способ сделать это. Он может принимать любой символ и не разделять строки, если вы этого не хотите. Никаких специальных библиотек не требуется (ну, кроме std, но кто действительно считает, что дополнительная библиотека), никаких указателей, ссылок и статических. Просто простой простой С++.
#pragma once
#include <vector>
#include <sstream>
using namespace std;
class Helpers
{
public:
static vector<string> split(string s, char delim)
{
stringstream temp (stringstream::in | stringstream::out);
vector<string> elems(0);
if (s.size() == 0 || delim == 0)
return elems;
for(char c : s)
{
if(c == delim)
{
elems.push_back(temp.str());
temp = stringstream(stringstream::in | stringstream::out);
}
else
temp << c;
}
if (temp.str().size() > 0)
elems.push_back(temp.str());
return elems;
}
//Splits string s with a list of delimiters in delims (it just a list, like if we wanted to
//split at the following letters, a, b, c we would make delims="abc".
static vector<string> split(string s, string delims)
{
stringstream temp (stringstream::in | stringstream::out);
vector<string> elems(0);
bool found;
if(s.size() == 0 || delims.size() == 0)
return elems;
for(char c : s)
{
found = false;
for(char d : delims)
{
if (c == d)
{
elems.push_back(temp.str());
temp = stringstream(stringstream::in | stringstream::out);
found = true;
break;
}
}
if(!found)
temp << c;
}
if(temp.str().size() > 0)
elems.push_back(temp.str());
return elems;
}
};
Как любитель, это первое решение, которое мне пришло в голову. Мне любопытно, почему я еще не видел подобного решения, есть ли что-то принципиально неправильное в том, как я это сделал?
#include <iostream>
#include <string>
#include <vector>
std::vector<std::string> split(const std::string &s, const std::string &delims)
{
std::vector<std::string> result;
std::string::size_type pos = 0;
while (std::string::npos != (pos = s.find_first_not_of(delims, pos))) {
auto pos2 = s.find_first_of(delims, pos);
result.emplace_back(s.substr(pos, std::string::npos == pos2 ? pos2 : pos2 - pos));
pos = pos2;
}
return result;
}
int main()
{
std::string text{"And then I said: \"I don't get it, why would you even do that!?\""};
std::string delims{" :;\".,?!"};
auto words = split(text, delims);
std::cout << "\nSentence:\n " << text << "\n\nWords:";
for (const auto &w : words) {
std::cout << "\n " << w;
}
return 0;
}
При работе с пробелом в качестве разделителя очевидный ответ использования std::istream_iterator<T>
уже задан и проголосовал много. Разумеется, элементы не могут быть разделены пробелами, а вместо этого некоторыми разделителями. Я не заметил никакого ответа, который просто переопределяет значение пробела, которое будет указано разделителем, а затем использует обычный подход.
Как изменить, какие потоки учитывают пробелы, вы просто измените поток std::locale
с помощью (std::istream::imbue()
) с фасет std::ctype<char>
с собственным определением того, что означает пробел (это можно сделать для std::ctype<wchar_t>
, но это на самом деле немного отличается, потому что std::ctype<char>
управляется таблицей, а std::ctype<wchar_t>
управляется виртуальными функциями).
#include <iostream>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <locale>
struct whitespace_mask {
std::ctype_base::mask mask_table[std::ctype<char>::table_size];
whitespace_mask(std::string const& spaces) {
std::ctype_base::mask* table = this->mask_table;
std::ctype_base::mask const* tab
= std::use_facet<std::ctype<char>>(std::locale()).table();
for (std::size_t i(0); i != std::ctype<char>::table_size; ++i) {
table[i] = tab[i] & ~std::ctype_base::space;
}
std::for_each(spaces.begin(), spaces.end(), [=](unsigned char c) {
table[c] |= std::ctype_base::space;
});
}
};
class whitespace_facet
: private whitespace_mask
, public std::ctype<char> {
public:
whitespace_facet(std::string const& spaces)
: whitespace_mask(spaces)
, std::ctype<char>(this->mask_table) {
}
};
struct whitespace {
std::string spaces;
whitespace(std::string const& spaces): spaces(spaces) {}
};
std::istream& operator>>(std::istream& in, whitespace const& ws) {
std::locale loc(in.getloc(), new whitespace_facet(ws.spaces));
in.imbue(loc);
return in;
}
// everything above would probably go into a utility library...
int main() {
std::istringstream in("a, b, c, d, e");
std::copy(std::istream_iterator<std::string>(in >> whitespace(", ")),
std::istream_iterator<std::string>(),
std::ostream_iterator<std::string>(std::cout, "\n"));
std::istringstream pipes("a b c| d |e e");
std::copy(std::istream_iterator<std::string>(pipes >> whitespace("|")),
std::istream_iterator<std::string>(),
std::ostream_iterator<std::string>(std::cout, "\n"));
}
Большая часть кода предназначена для упаковки инструмента общего назначения, обеспечивающего мягкие разделители: объединяются несколько разделителей в строке. Невозможно создать пустую последовательность. Если в потоке требуются разные разделители, вы, вероятно, используете по-разному настроенные потоки, используя общий поток-буфер:
void f(std::istream& in) {
std::istream pipes(in.rdbuf());
pipes >> whitespace("|");
std::istream comma(in.rdbuf());
comma >> whitespace(",");
std::string s0, s1;
if (pipes >> s0 >> std::ws // read up to first pipe and ignore sequence of pipes
&& comma >> s1 >> std::ws) { // read up to first comma and ignore commas
// ...
}
}
Я написал следующий фрагмент кода. Вы можете указать разделитель, который может быть строкой. Результат аналогичен Java String.split с пустой строкой в результате.
Например, если мы называем split ( "ABCPICKABCANYABCTWO: ABC", "ABC" ), результат следующий:
0 <len:0>
1 PICK <len:4>
2 ANY <len:3>
3 TWO: <len:4>
4 <len:0>
код:
vector <string> split(const string& str, const string& delimiter = " ") {
vector <string> tokens;
string::size_type lastPos = 0;
string::size_type pos = str.find(delimiter, lastPos);
while (string::npos != pos) {
// Found a token, add it to the vector.
cout << str.substr(lastPos, pos - lastPos) << endl;
tokens.push_back(str.substr(lastPos, pos - lastPos));
lastPos = pos + delimiter.size();
pos = str.find(delimiter, lastPos);
}
tokens.push_back(str.substr(lastPos, str.size() - lastPos));
return tokens;
}
Вот мое решение, используя С++ 11 и STL. Он должен быть достаточно эффективным:
#include <vector>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
std::vector<std::string> split(const std::string& s)
{
std::vector<std::string> v;
const auto end = s.end();
auto to = s.begin();
decltype(to) from;
while((from = std::find_if(to, end,
[](char c){ return !std::isspace(c); })) != end)
{
to = std::find_if(from, end, [](char c){ return std::isspace(c); });
v.emplace_back(from, to);
}
return v;
}
int main()
{
std::string s = "this is the string to split";
auto v = split(s);
for(auto&& s: v)
std::cout << s << '\n';
}
Вывод:
this
is
the
string
to
split
end
не s.end()
.
pos
, end
и done
?
Я использую следующий код:
namespace Core
{
typedef std::wstring String;
void SplitString(const Core::String& input, const Core::String& splitter, std::list<Core::String>& output)
{
if (splitter.empty())
{
throw std::invalid_argument(); // for example
}
std::list<Core::String> lines;
Core::String::size_type offset = 0;
for (;;)
{
Core::String::size_type splitterPos = input.find(splitter, offset);
if (splitterPos != Core::String::npos)
{
lines.push_back(input.substr(offset, splitterPos - offset));
offset = splitterPos + splitter.size();
}
else
{
lines.push_back(input.substr(offset));
break;
}
}
lines.swap(output);
}
}
// gtest:
class SplitStringTest: public testing::Test
{
};
TEST_F(SplitStringTest, EmptyStringAndSplitter)
{
std::list<Core::String> result;
ASSERT_ANY_THROW(Core::SplitString(Core::String(), Core::String(), result));
}
TEST_F(SplitStringTest, NonEmptyStringAndEmptySplitter)
{
std::list<Core::String> result;
ASSERT_ANY_THROW(Core::SplitString(L"xy", Core::String(), result));
}
TEST_F(SplitStringTest, EmptyStringAndNonEmptySplitter)
{
std::list<Core::String> result;
Core::SplitString(Core::String(), Core::String(L","), result);
ASSERT_EQ(1, result.size());
ASSERT_EQ(Core::String(), *result.begin());
}
TEST_F(SplitStringTest, OneCharSplitter)
{
std::list<Core::String> result;
Core::SplitString(L"x,y", L",", result);
ASSERT_EQ(2, result.size());
ASSERT_EQ(L"x", *result.begin());
ASSERT_EQ(L"y", *result.rbegin());
Core::SplitString(L",xy", L",", result);
ASSERT_EQ(2, result.size());
ASSERT_EQ(Core::String(), *result.begin());
ASSERT_EQ(L"xy", *result.rbegin());
Core::SplitString(L"xy,", L",", result);
ASSERT_EQ(2, result.size());
ASSERT_EQ(L"xy", *result.begin());
ASSERT_EQ(Core::String(), *result.rbegin());
}
TEST_F(SplitStringTest, TwoCharsSplitter)
{
std::list<Core::String> result;
Core::SplitString(L"x,.y,z", L",.", result);
ASSERT_EQ(2, result.size());
ASSERT_EQ(L"x", *result.begin());
ASSERT_EQ(L"y,z", *result.rbegin());
Core::SplitString(L"x,,y,z", L",,", result);
ASSERT_EQ(2, result.size());
ASSERT_EQ(L"x", *result.begin());
ASSERT_EQ(L"y,z", *result.rbegin());
}
TEST_F(SplitStringTest, RecursiveSplitter)
{
std::list<Core::String> result;
Core::SplitString(L",,,", L",,", result);
ASSERT_EQ(2, result.size());
ASSERT_EQ(Core::String(), *result.begin());
ASSERT_EQ(L",", *result.rbegin());
Core::SplitString(L",.,.,", L",.,", result);
ASSERT_EQ(2, result.size());
ASSERT_EQ(Core::String(), *result.begin());
ASSERT_EQ(L".,", *result.rbegin());
Core::SplitString(L"x,.,.,y", L",.,", result);
ASSERT_EQ(2, result.size());
ASSERT_EQ(L"x", *result.begin());
ASSERT_EQ(L".,y", *result.rbegin());
Core::SplitString(L",.,,.,", L",.,", result);
ASSERT_EQ(3, result.size());
ASSERT_EQ(Core::String(), *result.begin());
ASSERT_EQ(Core::String(), *(++result.begin()));
ASSERT_EQ(Core::String(), *result.rbegin());
}
TEST_F(SplitStringTest, NullTerminators)
{
std::list<Core::String> result;
Core::SplitString(L"xy", Core::String(L"\0", 1), result);
ASSERT_EQ(1, result.size());
ASSERT_EQ(L"xy", *result.begin());
Core::SplitString(Core::String(L"x\0y", 3), Core::String(L"\0", 1), result);
ASSERT_EQ(2, result.size());
ASSERT_EQ(L"x", *result.begin());
ASSERT_EQ(L"y", *result.rbegin());
}
Это моя версия, взятая источником Кева:
#include <string>
#include <vector>
void split(vector<string> &result, string str, char delim ) {
string tmp;
string::iterator i;
result.clear();
for(i = str.begin(); i <= str.end(); ++i) {
if((const char)*i != delim && i != str.end()) {
tmp += *i;
} else {
result.push_back(tmp);
tmp = "";
}
}
}
После этого вызовите функцию и сделайте что-нибудь с ней:
vector<string> hosts;
split(hosts, "192.168.1.2,192.168.1.3", ',');
for( size_t i = 0; i < hosts.size(); i++){
cout << "Connecting host : " << hosts.at(i) << "..." << endl;
}
Быстрая версия, которая использует vector
как базовый класс, предоставляя полный доступ ко всем своим операторам:
// Split string into parts.
class Split : public std::vector<std::string>
{
public:
Split(const std::string& str, char* delimList)
{
size_t lastPos = 0;
size_t pos = str.find_first_of(delimList);
while (pos != std::string::npos)
{
if (pos != lastPos)
push_back(str.substr(lastPos, pos-lastPos));
lastPos = pos + 1;
pos = str.find_first_of(delimList, lastPos);
}
if (lastPos < str.length())
push_back(str.substr(lastPos, pos-lastPos));
}
};
Пример, используемый для заполнения набора STL:
std::set<std::string> words;
Split split("Hello,World", ",");
words.insert(split.begin(), split.end());
Да, я просмотрел все 30 примеров.
Я не смог найти версию split
, которая работает для разделителей char, поэтому здесь моя:
#include <string>
#include <vector>
using namespace std;
vector<string> split(const string &str, const string &delim)
{
const auto delim_pos = str.find(delim);
if (delim_pos == string::npos)
return {str};
vector<string> ret{str.substr(0, delim_pos)};
auto tail = split(str.substr(delim_pos + delim.size(), string::npos), delim);
ret.insert(ret.end(), tail.begin(), tail.end());
return ret;
}
Вероятно, это не самая эффективная реализация, но это очень простое рекурсивное решение, используя только <string>
и <vector>
.
А, он написан на С++ 11, но в этом коде нет ничего особенного, поэтому вы можете легко адаптировать его к С++ 98.
Это мое решение этой проблемы:
vector<string> get_tokens(string str) {
vector<string> dt;
stringstream ss;
string tmp;
ss << str;
for (size_t i; !ss.eof(); ++i) {
ss >> tmp;
dt.push_back(tmp);
}
return dt;
}
Эта функция возвращает вектор строк.
Для тех, кому нужна альтернатива в разделении строки с разделителем строк, возможно, вы можете попробовать следующее решение.
std::vector<size_t> str_pos(const std::string &search, const std::string &target)
{
std::vector<size_t> founds;
if(!search.empty())
{
size_t start_pos = 0;
while (true)
{
size_t found_pos = target.find(search, start_pos);
if(found_pos != std::string::npos)
{
size_t found = found_pos;
founds.push_back(found);
start_pos = (found_pos + 1);
}
else
{
break;
}
}
}
return founds;
}
std::string str_sub_index(size_t begin_index, size_t end_index, const std::string &target)
{
std::string sub;
size_t size = target.length();
const char* copy = target.c_str();
for(size_t i = begin_index; i <= end_index; i++)
{
if(i >= size)
{
break;
}
else
{
char c = copy[i];
sub += c;
}
}
return sub;
}
std::vector<std::string> str_split(const std::string &delimiter, const std::string &target)
{
std::vector<std::string> splits;
if(!delimiter.empty())
{
std::vector<size_t> founds = str_pos(delimiter, target);
size_t founds_size = founds.size();
if(founds_size > 0)
{
size_t search_len = delimiter.length();
size_t begin_index = 0;
for(int i = 0; i <= founds_size; i++)
{
std::string sub;
if(i != founds_size)
{
size_t pos = founds.at(i);
sub = str_sub_index(begin_index, pos - 1, target);
begin_index = (pos + search_len);
}
else
{
sub = str_sub_index(begin_index, (target.length() - 1), target);
}
splits.push_back(sub);
}
}
}
return splits;
}
Эти фрагменты состоят из 3-х функций. Плохая новость заключается в использовании функции str_split
, в которой вам понадобятся две другие функции. Да, это огромный кусок кода. Но хорошая новость заключается в том, что эти дополнительные две функции могут работать независимо, а иногда и могут быть полезны...:)
Протестирована функция в блоке main()
следующим образом:
int main()
{
std::string s = "Hello, world! We need to make the world a better place. Because your world is also my world, and our children world.";
std::vector<std::string> split = str_split("world", s);
for(int i = 0; i < split.size(); i++)
{
std::cout << split[i] << std::endl;
}
}
И это создаст:
Hello,
! We need to make the
a better place. Because your
is also my
, and our children
.
Я считаю, что это не самый эффективный код, но, по крайней мере, он работает. Надеюсь, что это поможет.
Вот моя версия
#include <vector>
inline std::vector<std::string> Split(const std::string &str, const std::string &delim = " ")
{
std::vector<std::string> tokens;
if (str.size() > 0)
{
if (delim.size() > 0)
{
std::string::size_type currPos = 0, prevPos = 0;
while ((currPos = str.find(delim, prevPos)) != std::string::npos)
{
std::string item = str.substr(prevPos, currPos - prevPos);
if (item.size() > 0)
{
tokens.push_back(item);
}
prevPos = currPos + 1;
}
tokens.push_back(str.substr(prevPos));
}
else
{
tokens.push_back(str);
}
}
return tokens;
}
Он работает с многосимвольными разделителями. Это предотвращает попадание пустых токенов в ваши результаты. Он использует один заголовок. Он возвращает строку как один единственный токен, когда вы не указываете разделитель. Он также возвращает пустой результат, если строка пуста. Это, к сожалению, неэффективно из-за огромной копии std::vector
, если вы не компилируете с использованием С++ 11, который должен использовать схему перемещения. В С++ 11 этот код должен быть быстрым.
Нет Boost, нет строковых потоков, только стандартная библиотека C, взаимодействующая с std::string
и std::list
: функции библиотеки C для легкого анализа, типы данных С++ для легкого управления памятью.
Пробелом считается любая комбинация новых строк, вкладок и пробелов. Набор символов пробелов устанавливается переменной wschars
.
#include <string>
#include <list>
#include <iostream>
#include <cstring>
using namespace std;
const char *wschars = "\t\n ";
list<string> split(const string &str)
{
const char *cstr = str.c_str();
list<string> out;
while (*cstr) { // while remaining string not empty
size_t toklen;
cstr += strspn(cstr, wschars); // skip leading whitespace
toklen = strcspn(cstr, wschars); // figure out token length
if (toklen) // if we have a token, add to list
out.push_back(string(cstr, toklen));
cstr += toklen; // skip over token
}
// ran out of string; return list
return out;
}
int main(int argc, char **argv)
{
list<string> li = split(argv[1]);
for (list<string>::iterator i = li.begin(); i != li.end(); i++)
cout << "{" << *i << "}" << endl;
return 0;
}
Run:
$ ./split ""
$ ./split "a"
{a}
$ ./split " a "
{a}
$ ./split " a b"
{a}
{b}
$ ./split " a b c"
{a}
{b}
{c}
$ ./split " a b c d "
{a}
{b}
{c}
{d}
Рекурсивная версия split
(сама разбита на две функции). Все деструктивные манипуляции с переменными исчезли, за исключением нажатия строк в список!
void split_rec(const char *cstr, list<string> &li)
{
if (*cstr) {
const size_t leadsp = strspn(cstr, wschars);
const size_t toklen = strcspn(cstr + leadsp, wschars);
if (toklen)
li.push_back(string(cstr + leadsp, toklen));
split_rec(cstr + leadsp + toklen, li);
}
}
list<string> split(const string &str)
{
list<string> out;
split_rec(str.c_str(), out);
return out;
}
Я искал способ разделить строку на разделитель любой длины, поэтому я начал писать ее с нуля, поскольку существующие решения мне не подошли.
Вот мой маленький алгоритм, используя только STL:
//use like this
//std::vector<std::wstring> vec = Split<std::wstring> (L"Hello##world##!", L"##");
template <typename valueType>
static std::vector <valueType> Split (valueType text, const valueType& delimiter)
{
std::vector <valueType> tokens;
size_t pos = 0;
valueType token;
while ((pos = text.find(delimiter)) != valueType::npos)
{
token = text.substr(0, pos);
tokens.push_back (token);
text.erase(0, pos + delimiter.length());
}
tokens.push_back (text);
return tokens;
}
Он может использоваться с разделителем любой длины и формы, насколько я проверял. Создайте экземпляр типа string или wstring.
Весь алгоритм выполняет поиск разделителя, получает часть строки, которая соответствует разделителю, удаляет разделитель и выполняет поиск до тех пор, пока не найдет его больше.
Конечно, вы можете использовать любое количество пробелов для разделителя.
Надеюсь, это поможет.
LazyStringSplitter:
#include <string>
#include <algorithm>
#include <unordered_set>
using namespace std;
class LazyStringSplitter
{
string::const_iterator start, finish;
unordered_set<char> chop;
public:
// Empty Constructor
explicit LazyStringSplitter()
{}
explicit LazyStringSplitter (const string cstr, const string delims)
: start(cstr.begin())
, finish(cstr.end())
, chop(delims.begin(), delims.end())
{}
void operator () (const string cstr, const string delims)
{
chop.insert(delims.begin(), delims.end());
start = cstr.begin();
finish = cstr.end();
}
bool empty() const { return (start >= finish); }
string next()
{
// return empty string
// if ran out of characters
if (empty())
return string("");
auto runner = find_if(start, finish, [&](char c) {
return chop.count(c) == 1;
});
// construct next string
string ret(start, runner);
start = runner + 1;
// Never return empty string
// + tail recursion makes this method efficient
return !ret.empty() ? ret : next();
}
};
LazyStringSplitter
по одной причине - он не разбивает строку за один раз.next
, который возвращает следующую строку, которая разделена на исходнуюПРОГРАММА ТЕСТИРОВАНИЯ
#include <iostream>
using namespace std;
int main()
{
LazyStringSplitter splitter;
// split at the characters ' ', '!', '.', ','
splitter("This, is a string. And here is another string! Let test and see how well this does.", " !.,");
while (!splitter.empty())
cout << splitter.next() << endl;
return 0;
}
OUTPUT
This
is
a
string
And
here
is
another
string
Let's
test
and
see
how
well
this
does
Следующим планом улучшения этого является реализация методов begin
и end
, чтобы можно было сделать что-то вроде:
vector<string> split_string(splitter.begin(), splitter.end());
Это функция, которую я написал, которая помогает мне делать многое. Это помогло мне при выполнении протокола для WebSockets
.
using namespace std;
#include <iostream>
#include <vector>
#include <sstream>
#include <string>
vector<string> split ( string input , string split_id ) {
vector<string> result;
int i = 0;
bool add;
string temp;
stringstream ss;
size_t found;
string real;
int r = 0;
while ( i != input.length() ) {
add = false;
ss << input.at(i);
temp = ss.str();
found = temp.find(split_id);
if ( found != string::npos ) {
add = true;
real.append ( temp , 0 , found );
} else if ( r > 0 && ( i+1 ) == input.length() ) {
add = true;
real.append ( temp , 0 , found );
}
if ( add ) {
result.push_back(real);
ss.str(string());
ss.clear();
temp.clear();
real.clear();
r = 0;
}
i++;
r++;
}
return result;
}
int main() {
string s = "S,o,m,e,w,h,e,r,e, down the road \n In a really big C++ house. \n Lives a little old lady. \n That no one ever knew. \n She comes outside. \n In the very hot sun. \n\n\n\n\n\n\n\n And throws C++ at us. \n The End. FIN.";
vector < string > Token;
Token = split ( s , "," );
for ( int i = 0 ; i < Token.size(); i++) cout << Token.at(i) << endl;
cout << endl << Token.size();
int a;
cin >> a;
return a;
}
Я использую следующие
void split(string in, vector<string>& parts, char separator) {
string::iterator ts, curr;
ts = curr = in.begin();
for(; curr <= in.end(); curr++ ) {
if( (curr == in.end() || *curr == separator) && curr > ts )
parts.push_back( string( ts, curr ));
if( curr == in.end() )
break;
if( *curr == separator ) ts = curr + 1;
}
}
PlasmaHH, я забыл включить дополнительную проверку (curr > ts) для удаления жетонов с пробелами.
На основе Galik answer Я сделал это. Это в основном здесь, поэтому мне не нужно постоянно писать это снова и снова. Это безумие, что С++ по-прежнему не имеет встроенной функции split. Особенности:
"\r\n"
)#include <string>
#include <vector>
#include <algorithm>
std::vector<std::string> split(const std::string& s, const std::string& delims)
{
using namespace std;
vector<string> v;
// Start of an element.
size_t elemStart = 0;
// We start searching from the end of the previous element, which
// initially is the start of the string.
size_t elemEnd = 0;
// Find the first non-delim, i.e. the start of an element, after the end of the previous element.
while((elemStart = s.find_first_not_of(delims, elemEnd)) != string::npos)
{
// Find the first delem, i.e. the end of the element (or if this fails it is the end of the string).
elemEnd = s.find_first_of(delims, elemStart);
// Add it.
v.emplace_back(s, elemStart, elemEnd == string::npos ? string::npos : elemEnd - elemStart);
}
// When there are no more non-spaces, we are done.
return v;
}
Здесь я беру на себя это. Мне пришлось обрабатывать входную строку слово за словом, что могло быть сделано с использованием пространства для подсчета слов, но я чувствовал, что это будет утомительно, и я должен разделить слова на векторы.
#include<iostream>
#include<vector>
#include<string>
#include<stdio.h>
using namespace std;
int main()
{
char x = '\0';
string s = "";
vector<string> q;
x = getchar();
while(x != '\n')
{
if(x == ' ')
{
q.push_back(s);
s = "";
x = getchar();
continue;
}
s = s + x;
x = getchar();
}
q.push_back(s);
for(int i = 0; i<q.size(); i++)
cout<<q[i]<<" ";
return 0;
}
Просто для удобства:
template<class V, typename T>
bool in(const V &v, const T &el) {
return std::find(v.begin(), v.end(), el) != v.end();
}
Фактическое разбиение на основе нескольких разделителей:
std::vector<std::string> split(const std::string &s,
const std::vector<char> &delims) {
std::vector<std::string> res;
auto stuff = [&delims](char c) { return !in(delims, c); };
auto space = [&delims](char c) { return in(delims, c); };
auto first = std::find_if(s.begin(), s.end(), stuff);
while (first != s.end()) {
auto last = std::find_if(first, s.end(), space);
res.push_back(std::string(first, last));
first = std::find_if(last + 1, s.end(), stuff);
}
return res;
}
Использование:
int main() {
std::string s = " aaa, bb cc ";
for (auto el: split(s, {' ', ','}))
std::cout << el << std::endl;
return 0;
}
#include <iostream>
#include <vector>
using namespace std;
int main() {
string str = "ABC AABCD CDDD RABC GHTTYU FR";
str += " "; //dirty hack: adding extra space to the end
vector<string> v;
for (int i=0; i<(int)str.size(); i++) {
int a, b;
a = i;
for (int j=i; j<(int)str.size(); j++) {
if (str[j] == ' ') {
b = j;
i = j;
break;
}
}
v.push_back(str.substr(a, b-a));
}
for (int i=0; i<v.size(); i++) {
cout<<v[i].size()<<" "<<v[i]<<endl;
}
return 0;
}
Здесь моя запись:
template <typename Container, typename InputIter, typename ForwardIter>
Container
split(InputIter first, InputIter last,
ForwardIter s_first, ForwardIter s_last)
{
Container output;
while (true) {
auto pos = std::find_first_of(first, last, s_first, s_last);
output.emplace_back(first, pos);
if (pos == last) {
break;
}
first = ++pos;
}
return output;
}
template <typename Output = std::vector<std::string>,
typename Input = std::string,
typename Delims = std::string>
Output
split(const Input& input, const Delims& delims = " ")
{
using std::cbegin;
using std::cend;
return split<Output>(cbegin(input), cend(input),
cbegin(delims), cend(delims));
}
auto vec = split("Mary had a little lamb");
Первое определение - это общая функция типа STL, в которой используются две пары итераторов. Вторая функция - это удобная функция, которая позволяет вам делать все begin()
и end()
самостоятельно. Вы также можете указать тип выходного контейнера в качестве параметра шаблона, если вы хотели бы использовать list
, например.
Что делает его элегантным (IMO) - это то, что в отличие от большинства других ответов, оно не ограничивается строками, но будет работать с любым STL-совместимым контейнером. Без изменения кода выше вы можете сказать:
using vec_of_vecs_t = std::vector<std::vector<int>>;
std::vector<int> v{1, 2, 0, 3, 4, 5, 0, 7, 8, 0, 9};
auto r = split<vec_of_vecs_t>(v, std::initializer_list<int>{0, 2});
который разбивает вектор v
на отдельные векторы каждый раз, когда встречается a 0
или 2
.
(Также добавленный бонус, который со строками, эта реализация быстрее, чем версии strtok()
- и getline()
, по крайней мере, в моей системе.)
Мы можем использовать strtok в С++,
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char str[]="Mickey M;12034;911416313;M;01a;9001;NULL;0;13;12;0;CPP,C;MSC,3D;FEND,BEND,SEC;";
char *pch = strtok (str,";,");
while (pch != NULL)
{
cout<<pch<<"\n";
pch = strtok (NULL, ";,");
}
return 0;
}
// adapted from a "regular" csv parse
std::string stringIn = "my csv is 10233478 NOTseparated by commas";
std::vector<std::string> commaSeparated(1);
int commaCounter = 0;
for (int i=0; i<stringIn.size(); i++) {
if (stringIn[i] == " ") {
commaSeparated.push_back("");
commaCounter++;
} else {
commaSeparated.at(commaCounter) += stringIn[i];
}
}
в конце вы будете иметь вектор строк с каждым элементом предложения, разделенным пробелами. только нестандартный ресурс std::vector (но поскольку задействован std::string, я решил, что это будет приемлемо).
пустые строки сохраняются как отдельные элементы.
Я только что написал прекрасный пример того, как разделить символ char на символ, который затем помещает каждый массив символов (слова, разделенные вашим символом) в вектор. Для простоты я сделал векторный тип строки std.
Я надеюсь, что это поможет и будет вам читаемым.
#include <vector>
#include <string>
#include <iostream>
void push(std::vector<std::string> &WORDS, std::string &TMP){
WORDS.push_back(TMP);
TMP = "";
}
std::vector<std::string> mySplit(char STRING[]){
std::vector<std::string> words;
std::string s;
for(unsigned short i = 0; i < strlen(STRING); i++){
if(STRING[i] != ' '){
s += STRING[i];
}else{
push(words, s);
}
}
push(words, s);//Used to get last split
return words;
}
int main(){
char string[] = "My awesome string.";
std::cout << mySplit(string)[2];
std::cin.get();
return 0;
}
Я считаю, что никто еще не опубликовал это решение. Вместо прямого использования разделителей он в основном делает то же самое, что и boost:: split(), то есть позволяет передать предикат, который возвращает true, если char является разделителем, а false в противном случае. Я думаю, что это дает программисту гораздо больше контроля, и, главное, вам не нужно повышать.
template <class Container, class String, class Predicate>
void split(Container& output, const String& input,
const Predicate& pred, bool trimEmpty = false) {
auto it = begin(input);
auto itLast = it;
while (it = find_if(it, end(input), pred), it != end(input)) {
if (not (trimEmpty and it == itLast)) {
output.emplace_back(itLast, it);
}
++it;
itLast = it;
}
}
Затем вы можете использовать его следующим образом:
struct Delim {
bool operator()(char c) {
return not isalpha(c);
}
};
int main() {
string s("#include<iostream>\n"
"int main() { std::cout << \"Hello world!\" << std::endl; }");
vector<string> v;
split(v, s, Delim(), true);
/* Which is also the same as */
split(v, s, [](char c) { return not isalpha(c); }, true);
for (const auto& i : v) {
cout << i << endl;
}
}
Мой код:
#include <list>
#include <string>
template<class StringType = std::string, class ContainerType = std::list<StringType> >
class DSplitString:public ContainerType
{
public:
explicit DSplitString(const StringType& strString, char cChar, bool bSkipEmptyParts = true)
{
size_t iPos = 0;
size_t iPos_char = 0;
while(StringType::npos != (iPos_char = strString.find(cChar, iPos)))
{
StringType strTemp = strString.substr(iPos, iPos_char - iPos);
if((bSkipEmptyParts && !strTemp.empty()) || (!bSkipEmptyParts))
push_back(strTemp);
iPos = iPos_char + 1;
}
}
explicit DSplitString(const StringType& strString, const StringType& strSub, bool bSkipEmptyParts = true)
{
size_t iPos = 0;
size_t iPos_char = 0;
while(StringType::npos != (iPos_char = strString.find(strSub, iPos)))
{
StringType strTemp = strString.substr(iPos, iPos_char - iPos);
if((bSkipEmptyParts && !strTemp.empty()) || (!bSkipEmptyParts))
push_back(strTemp);
iPos = iPos_char + strSub.length();
}
}
};
Пример:
#include <iostream>
#include <string>
int _tmain(int argc, _TCHAR* argv[])
{
DSplitString<> aa("doicanhden1;doicanhden2;doicanhden3;", ';');
for each (std::string var in aa)
{
std::cout << var << std::endl;
}
std::cin.get();
return 0;
}
Настройте на getline
с символом '' как токен.
У меня совершенно другой подход, чем у других решений, который предлагает большую ценность, так как другим решениям по-разному не хватает, но, конечно, есть и свои недостатки. Вот рабочая реализация с примером размещения <tag></tag>
вокруг слов.
Для начала, эта проблема может быть решена с помощью одного цикла, без дополнительной памяти, и с помощью рассмотрения только четырех логических случаев. Концептуально нас интересуют границы. Наш код должен отражать это: позвольте перебирать строку и смотреть на два символа за раз, имея в виду, что у нас есть особые случаи в начале и конце строки.
Недостатком является то, что мы должны написать реализацию, которая является несколько многословной, но в основном удобной для примера.
Положительным моментом является то, что мы написали реализацию, поэтому ее очень легко настроить в соответствии с конкретными потребностями, такими как разграничение левых и записывающих границ слова, использование любого набора разделителей или обработка других случаев, таких как неограниченные или ошибочные позиции.
using namespace std;
#include <iostream>
#include <string>
#include <cctype>
typedef enum boundary_type_e {
E_BOUNDARY_TYPE_ERROR = -1,
E_BOUNDARY_TYPE_NONE,
E_BOUNDARY_TYPE_LEFT,
E_BOUNDARY_TYPE_RIGHT,
} boundary_type_t;
typedef struct boundary_s {
boundary_type_t type;
int pos;
} boundary_t;
bool is_delim_char(int c) {
return isspace(c); // also compare against any other chars you want to use as delimiters
}
bool is_word_char(int c) {
return ' ' <= c && c <= '~' && !is_delim_char(c);
}
boundary_t maybe_word_boundary(string str, int pos) {
int len = str.length();
if (pos < 0 || pos >= len) {
return (boundary_t){.type = E_BOUNDARY_TYPE_ERROR};
} else {
if (pos == 0 && is_word_char(str[pos])) {
// if the first character is word-y, we have a left boundary at the beginning
return (boundary_t){.type = E_BOUNDARY_TYPE_LEFT, .pos = pos};
} else if (pos == len - 1 && is_word_char(str[pos])) {
// if the last character is word-y, we have a right boundary left of the null terminator
return (boundary_t){.type = E_BOUNDARY_TYPE_RIGHT, .pos = pos + 1};
} else if (!is_word_char(str[pos]) && is_word_char(str[pos + 1])) {
// if we have a delimiter followed by a word char, we have a left boundary left of the word char
return (boundary_t){.type = E_BOUNDARY_TYPE_LEFT, .pos = pos + 1};
} else if (is_word_char(str[pos]) && !is_word_char(str[pos + 1])) {
// if we have a word char followed by a delimiter, we have a right boundary right of the word char
return (boundary_t){.type = E_BOUNDARY_TYPE_RIGHT, .pos = pos + 1};
}
return (boundary_t){.type = E_BOUNDARY_TYPE_NONE};
}
}
int main() {
string str;
getline(cin, str);
int len = str.length();
for (int i = 0; i < len; i++) {
boundary_t boundary = maybe_word_boundary(str, i);
if (boundary.type == E_BOUNDARY_TYPE_LEFT) {
// whatever
} else if (boundary.type == E_BOUNDARY_TYPE_RIGHT) {
// whatever
}
}
}
Как видите, код очень прост для понимания и точной настройки, а фактическое использование кода очень короткое и простое. Использование C++ не должно мешать нам писать самый простой и легко настраиваемый код, даже если это означает, что не используется STL. Я бы подумал, что это пример того, что Линус Торвальдс мог бы назвать "вкусом", так как мы устранили всю логику, которая нам не нужна, при написании в стиле, который, естественно, позволяет обрабатывать больше случаев, когда и если нужно обрабатывать их возникает.
Что могло бы улучшить этот код, так это использование enum class
, принимающего указатель функции на is_word_char
в maybe_word_boundary
вместо is_word_char
вызова is_word_char
и передачи лямбда- is_word_char
.
Используя std::string_view
и библиотеку Eric Niebler range-v3
:
https://wandbox.org/permlink/kW5lwRCL1pxjp2pW
#include <iostream>
#include <string>
#include <string_view>
#include "range/v3/view.hpp"
#include "range/v3/algorithm.hpp"
int main() {
std::string s = "Somewhere down the range v3 library";
ranges::for_each(s
| ranges::view::split(' ')
| ranges::view::transform([](auto &&sub) {
return std::string_view(&*sub.begin(), ranges::distance(sub));
}),
[](auto s) {std::cout << "Substring: " << s << "\n";}
);
}
#include <iostream>
#include <string>
#include <deque>
std::deque<std::string> split(
const std::string& line,
std::string::value_type delimiter,
bool skipEmpty = false
) {
std::deque<std::string> parts{};
if (!skipEmpty && !line.empty() && delimiter == line.at(0)) {
parts.push_back({});
}
for (const std::string::value_type& c : line) {
if (
(
c == delimiter
&&
(skipEmpty ? (!parts.empty() && !parts.back().empty()) : true)
)
||
(c != delimiter && parts.empty())
) {
parts.push_back({});
}
if (c != delimiter) {
parts.back().push_back(c);
}
}
if (skipEmpty && !parts.empty() && parts.back().empty()) {
parts.pop_back();
}
return parts;
}
void test(const std::string& line) {
std::cout << line << std::endl;
std::cout << "skipEmpty=0 |";
for (const std::string& part : split(line, ':')) {
std::cout << part << '|';
}
std::cout << std::endl;
std::cout << "skipEmpty=1 |";
for (const std::string& part : split(line, ':', true)) {
std::cout << part << '|';
}
std::cout << std::endl;
std::cout << std::endl;
}
int main() {
test("foo:bar:::baz");
test("");
test("foo");
test(":");
test("::");
test(":foo");
test("::foo");
test(":foo:");
test(":foo::");
return 0;
}
Вывод:
foo:bar:::baz
skipEmpty=0 |foo|bar|||baz|
skipEmpty=1 |foo|bar|baz|
skipEmpty=0 |
skipEmpty=1 |
foo
skipEmpty=0 |foo|
skipEmpty=1 |foo|
:
skipEmpty=0 |||
skipEmpty=1 |
::
skipEmpty=0 ||||
skipEmpty=1 |
:foo
skipEmpty=0 ||foo|
skipEmpty=1 |foo|
::foo
skipEmpty=0 |||foo|
skipEmpty=1 |foo|
:foo:
skipEmpty=0 ||foo||
skipEmpty=1 |foo|
:foo::
skipEmpty=0 ||foo|||
skipEmpty=1 |foo|
моя общая реализация для string
и u32string
~, используя подпись boost::algorithm::split
.
template<typename CharT, typename UnaryPredicate>
void split(std::vector<std::basic_string<CharT>>& split_result,
const std::basic_string<CharT>& s,
UnaryPredicate predicate)
{
using ST = std::basic_string<CharT>;
using std::swap;
std::vector<ST> tmp_result;
auto iter = s.cbegin(),
end_iter = s.cend();
while (true)
{
/**
* edge case: empty str -> push an empty str and exit.
*/
auto find_iter = find_if(iter, end_iter, predicate);
tmp_result.emplace_back(iter, find_iter);
if (find_iter == end_iter) { break; }
iter = ++find_iter;
}
swap(tmp_result, split_result);
}
template<typename CharT>
void split(std::vector<std::basic_string<CharT>>& split_result,
const std::basic_string<CharT>& s,
const std::basic_string<CharT>& char_candidate)
{
std::unordered_set<CharT> candidate_set(char_candidate.cbegin(),
char_candidate.cend());
auto predicate = [&candidate_set](const CharT& c) {
return candidate_set.count(c) > 0U;
};
return split(split_result, s, predicate);
}
template<typename CharT>
void split(std::vector<std::basic_string<CharT>>& split_result,
const std::basic_string<CharT>& s,
const CharT* literals)
{
return split(split_result, s, std::basic_string<CharT>(literals));
}
Это расширение одного из лучших ответов. Теперь он поддерживает установку максимального количества возвращаемых элементов, N. Последний бит строки будет в элементе Nth. Параметр MAXELEMENTS не является обязательным, если он установлен по умолчанию 0, он будет возвращать неограниченное количество элементов.: -)
.h:
class Myneatclass {
public:
static std::vector<std::string>& split(const std::string &s, char delim, std::vector<std::string> &elems, const size_t MAXELEMENTS = 0);
static std::vector<std::string> split(const std::string &s, char delim, const size_t MAXELEMENTS = 0);
};
.cpp
std::vector<std::string>& Myneatclass::split(const std::string &s, char delim, std::vector<std::string> &elems, const size_t MAXELEMENTS) {
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim)) {
elems.push_back(item);
if (MAXELEMENTS > 0 && !ss.eof() && elems.size() + 1 >= MAXELEMENTS) {
std::getline(ss, item);
elems.push_back(item);
break;
}
}
return elems;
}
std::vector<std::string> Myneatclass::split(const std::string &s, char delim, const size_t MAXELEMENTS) {
std::vector<std::string> elems;
split(s, delim, elems, MAXELEMENTS);
return elems;
}
если вы хотите разделить строку на некоторые символы, которые вы можете использовать
#include<iostream>
#include<string>
#include<vector>
#include<iterator>
#include<sstream>
#include<string>
using namespace std;
void replaceOtherChars(string &input, vector<char> ÷rs)
{
const char divider = dividers.at(0);
int replaceIndex = 0;
vector<char>::iterator it_begin = dividers.begin()+1,
it_end= dividers.end();
for(;it_begin!=it_end;++it_begin)
{
replaceIndex = 0;
while(true)
{
replaceIndex=input.find_first_of(*it_begin,replaceIndex);
if(replaceIndex==-1)
break;
input.at(replaceIndex)=divider;
}
}
}
vector<string> split(string str, vector<char> chars, bool missEmptySpace =true )
{
vector<string> result;
const char divider = chars.at(0);
replaceOtherChars(str,chars);
stringstream stream;
stream<<str;
string temp;
while(getline(stream,temp,divider))
{
if(missEmptySpace && temp.empty())
continue;
result.push_back(temp);
}
return result;
}
int main()
{
string str ="milk, pigs.... hot-dogs ";
vector<char> arr;
arr.push_back(' '); arr.push_back(','); arr.push_back('.');
vector<string> result = split(str,arr);
vector<string>::iterator it_begin= result.begin(),
it_end= result.end();
for(;it_begin!=it_end;++it_begin)
{
cout<<*it_begin<<endl;
}
return 0;
}
Спасибо @Jairo Abdiel Toribio Cisneros. Он работает для меня, но ваша функция возвращает некоторый пустой элемент. Поэтому для возврата без пустого я редактировал со следующим:
std::vector<std::string> split(std::string str, const char* delim) {
std::vector<std::string> v;
std::string tmp;
for(std::string::const_iterator i = str.begin(); i <= str.end(); ++i) {
if(*i != *delim && i != str.end()) {
tmp += *i;
} else {
if (tmp.length() > 0) {
v.push_back(tmp);
}
tmp = "";
}
}
return v;
}
Использование:
std::string s = "one:two::three";
std::string delim = ":";
std::vector<std::string> vv = split(s, delim.c_str());
очень поздно для вечеринки, которую я знаю, но я думал о самом элегантном способе сделать это, если вам дали ряд разделителей, а не пробелы, и использовали не что иное, как стандартную библиотеку.
Вот мои мысли:
Чтобы разбить слова на строковый вектор на последовательность разделителей:
template<class Container>
std::vector<std::string> split_by_delimiters(const std::string& input, const Container& delimiters)
{
std::vector<std::string> result;
for (auto current = begin(input) ; current != end(input) ; )
{
auto first = find_if(current, end(input), not_in(delimiters));
if (first == end(input)) break;
auto last = find_if(first, end(input), is_in(delimiters));
result.emplace_back(first, last);
current = last;
}
return result;
}
чтобы разделить другой путь, предоставив последовательность допустимых символов:
template<class Container>
std::vector<std::string> split_by_valid_chars(const std::string& input, const Container& valid_chars)
{
std::vector<std::string> result;
for (auto current = begin(input) ; current != end(input) ; )
{
auto first = find_if(current, end(input), is_in(valid_chars));
if (first == end(input)) break;
auto last = find_if(first, end(input), not_in(valid_chars));
result.emplace_back(first, last);
current = last;
}
return result;
}
is_in и not_in определены таким образом:
namespace detail {
template<class Container>
struct is_in {
is_in(const Container& charset)
: _charset(charset)
{}
bool operator()(char c) const
{
return find(begin(_charset), end(_charset), c) != end(_charset);
}
const Container& _charset;
};
template<class Container>
struct not_in {
not_in(const Container& charset)
: _charset(charset)
{}
bool operator()(char c) const
{
return find(begin(_charset), end(_charset), c) == end(_charset);
}
const Container& _charset;
};
}
template<class Container>
detail::not_in<Container> not_in(const Container& c)
{
return detail::not_in<Container>(c);
}
template<class Container>
detail::is_in<Container> is_in(const Container& c)
{
return detail::is_in<Container>(c);
}
Моя реализация может быть альтернативным решением:
std::vector<std::wstring> SplitString(const std::wstring & String, const std::wstring & Seperator)
{
std::vector<std::wstring> Lines;
size_t stSearchPos = 0;
size_t stFoundPos;
while (stSearchPos < String.size() - 1)
{
stFoundPos = String.find(Seperator, stSearchPos);
stFoundPos = (stFoundPos == std::string::npos) ? String.size() : stFoundPos;
Lines.push_back(String.substr(stSearchPos, stFoundPos - stSearchPos));
stSearchPos = stFoundPos + Seperator.size();
}
return Lines;
}
Тестовый код:
std::wstring MyString(L"Part 1SEPsecond partSEPlast partSEPend");
std::vector<std::wstring> Parts = IniFile::SplitString(MyString, L"SEP");
std::wcout << L"The string: " << MyString << std::endl;
for (std::vector<std::wstring>::const_iterator it=Parts.begin(); it<Parts.end(); ++it)
{
std::wcout << *it << L"<---" << std::endl;
}
std::wcout << std::endl;
MyString = L"this,time,a,comma separated,string";
std::wcout << L"The string: " << MyString << std::endl;
Parts = IniFile::SplitString(MyString, L",");
for (std::vector<std::wstring>::const_iterator it=Parts.begin(); it<Parts.end(); ++it)
{
std::wcout << *it << L"<---" << std::endl;
}
Выход тестового кода:
The string: Part 1SEPsecond partSEPlast partSEPend
Part 1<---
second part<---
last part<---
end<---
The string: this,time,a,comma separated,string
this<---
time<---
a<---
comma separated<---
string<---
Не то, чтобы нам нужно больше ответов, но это то, что я придумал после того, как вдохновил Эвана Терана.
std::vector <std::string> split(const string &input, auto delimiter, bool skipEmpty=true) {
/*
Splits a string at each delimiter and returns these strings as a string vector.
If the delimiter is not found then nothing is returned.
If skipEmpty is true then strings between delimiters that are 0 in length will be skipped.
*/
bool delimiterFound = false;
int pos=0, pPos=0;
std::vector <std::string> result;
while (true) {
pos = input.find(delimiter,pPos);
if (pos != std::string::npos) {
if (skipEmpty==false or pos-pPos > 0) // if empty values are to be kept or not
result.push_back(input.substr(pPos,pos-pPos));
delimiterFound = true;
} else {
if (pPos < input.length() and delimiterFound) {
if (skipEmpty==false or input.length()-pPos > 0) // if empty values are to be kept or not
result.push_back(input.substr(pPos,input.length()-pPos));
}
break;
}
pPos = pos+1;
}
return result;
}
Уже есть много хороших ответов на эти вопросы, это всего лишь крошечная деталь.
Разделение строки для вывода - это одно, но если вы разделите в контейнер, например vector
, вызов reserve()
может повлиять на производительность, потому что разделение приведет к "параллельным" выделениям кусков разных размеров.
Даже если изящество может пострадать от него, немного предшествующий анализ можно рассмотреть:
#include <algorithm>
size_t n = std::count(s.begin(), s.end(), ' ');
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <iterator>
#include <vector>
int main() {
using namespace std;
int n=8;
string sentence = "10 20 30 40 5 6 7 8";
istringstream iss(sentence);
vector<string> tokens;
copy(istream_iterator<string>(iss),
istream_iterator<string>(),
back_inserter(tokens));
for(int i=0;i<n;i++){
cout<<tokens.at(i);
}
}
Здесь мой подход, разрезанный и разбитый:
string cut (string& str, const string& del)
{
string f = str;
if (in.find_first_of(del) != string::npos)
{
f = str.substr(0,str.find_first_of(del));
str = str.substr(str.find_first_of(del)+del.length());
}
return f;
}
vector<string> split (const string& in, const string& del=" ")
{
vector<string> out();
string t = in;
while (t.length() > del.length())
out.push_back(cut(t,del));
return out;
}
Кстати, если есть что-то, что я могу сделать, чтобы оптимизировать это.
void splitString(string str, char delim, string array[], const int arraySize)
{
int delimPosition, subStrSize, subStrStart = 0;
for (int index = 0; delimPosition != -1; index++)
{
delimPosition = str.find(delim, subStrStart);
subStrSize = delimPosition - subStrStart;
array[index] = str.substr(subStrStart, subStrSize);
subStrStart =+ (delimPosition + 1);
}
}
Для смехотворно большой и, вероятно, избыточной версии, попробуйте много для циклов.
string stringlist[10];
int count = 0;
for (int i = 0; i < sequence.length(); i++)
{
if (sequence[i] == ' ')
{
stringlist[count] = sequence.substr(0, i);
sequence.erase(0, i+1);
i = 0;
count++;
}
else if (i == sequence.length()-1) // Last word
{
stringlist[count] = sequence.substr(0, i+1);
}
}
Это некрасиво, но по большому счету (запрет пунктуации и множество других ошибок) это работает!
while (iss) { string subs; iss >> subs; cout << "Substring: " << sub << endl; }