C ++ Как получить подстроку после символа с помощью substr? [Дубликат]

Почему ваш код не работает

Как объяснялось во введении HTML-учебника , HTMLWorker устарели много лет назад. Он не предназначен для преобразования полных HTML-страниц. Он не знает, что на странице HTML есть раздел и ; он просто разбирает весь контент. Он предназначен для анализа небольших фрагментов HTML, и вы можете определять стили с помощью класса StyleSheet; Настоящий CSS не поддерживался.

Затем появился XML Worker. XML Worker предназначался как универсальная среда для анализа XML. В качестве доказательства концепции мы решили написать некоторые функциональные возможности XHTML для PDF, но мы не поддерживали все теги HTML. Например: формы вообще не поддерживались, и было очень сложно поддерживать CSS, который используется для размещения контента. Формы в HTML очень сильно отличаются от форм в PDF. Также существует несоответствие между архитектурой iText и архитектурой HTML + CSS. Постепенно мы расширили XML Worker, в основном на основе запросов от клиентов, но XML Worker стал монстром со многими щупальцами.

В конце концов, мы решили переписать iText с нуля, с требованиями для преобразования HTML + CSS в разум. Это привело к iText 7 . В дополнение к iText 7 мы создали несколько надстроек, наиболее важным из которых является pdfHTML .

Как решить проблему

Использование в последней версии iText (iText 7.1.0 + pdfHTML 2.0.0) код для преобразования HTML из вопроса в PDF сводится к этому фрагменту:

public static final String SRC = "src/main/resources/html/sample.html";
public static final String DEST = "target/results/sample.pdf";
public void createPdf(String src, String dest) throws IOException {
    HtmlConverter.convertToPdf(new File(src), new File(dest));
}

Результат выглядит следующим образом:

Как вы можете видеть, это в значительной степени результат, которого вы ожидаете. Так как iText 7.1.0 / pdfHTML 2.0.0, шрифтом по умолчанию является Times-Roman. CSS уважается: изображение теперь плавает справа.

Некоторые дополнительные мысли.

Разработчики часто не возражают против перехода на новую версию iText, когда я даю совет обновить до iText 7 / pdfHTML 2. Разрешить мне ответить на три основных аргумента, которые я слышу:

Мне нужно использовать бесплатный iText, а iText 7 не является бесплатным / файл pdfHTML add -on является закрытым источником.

iText 7 выпущен с использованием AGPL, так же как iText 5 и XML Worker. AGPL позволяет бесплатное использование в смысле бесплатно в контексте проектов с открытым исходным кодом. Если вы распространяете закрытый исходный / проприетарный продукт (например, используете iText в контексте SaaS), вы не можете использовать iText бесплатно; в этом случае вам необходимо приобрести коммерческую лицензию. Это уже было верно для iText 5; это все еще верно для iText 7. Что касается версий до iText 5: , вы не должны использовать их вообще . Что касается pdfHTML: первые версии действительно были доступны только в виде программного обеспечения с закрытым исходным кодом. У нас была тяжелая дискуссия в iText Group: с одной стороны, были люди, которые хотели избежать массового злоупотребления со стороны компаний, которые не слушают своих разработчиков, когда те разработчики сообщают, что это открытый источник не является так же как и бесплатно. Разработчики рассказывали нам, что их босс заставил их поступать не так, и что они не смогли убедить своего босса приобрести коммерческую лицензию. С другой стороны, были люди, которые утверждали, что мы не должны наказывать разработчиков за неправильное поведение их боссов. В конце концов, люди в пользу open sourcing pdfHTML, то есть разработчики iText, выиграли этот аргумент. Убедитесь, что они не ошибаются и правильно используют iText: соблюдайте AGPL, если вы используете iText бесплатно ; убедитесь, что ваш босс покупает коммерческую лицензию, если вы используете iText в контексте с закрытым исходным кодом.

Мне нужно поддерживать устаревшую систему, и мне нужно использовать старую версию iText.

Серьезно? Техническое обслуживание также предполагает применение обновлений и переход на новые версии программного обеспечения, которое вы используете. Как вы можете видеть, код, необходимый при использовании iText 7 и pdfHTML, очень прост и менее подвержен ошибкам, чем тот, который нужен ранее. Проект миграции не должен занять слишком много времени.

Я только что начал, и я не знал о iText 7; Я узнал только после того, как закончил свой проект.

Вот почему я отправляю этот вопрос и отвечаю. Подумайте о себе как о программисте eXtreme. Отбросьте весь свой код и начните заново. Вы заметите, что это не так много работы, как вы себе представляли, и вы будете лучше спать, зная, что вы сделали ваш проект надежным, потому что iText 5 постепенно прекращается. Мы по-прежнему предлагаем поддержку для оплаты клиентов, но в конечном итоге мы прекратим поддерживать iText 5 вообще.

375
задан DavidRR 4 April 2013 в 19:04
поделиться

30 ответов

Ваш простой случай можно легко построить с помощью метода std::string::find . Однако посмотрите Boost.Tokenizer . Здорово. У Boost обычно есть очень крутые струнные инструменты.

131
ответ дан Konrad Rudolph 26 August 2018 в 22:55
поделиться

Если известна максимальная длина входной строки, подлежащей токенизации, ее можно использовать и реализовать очень быструю версию. Я набросал основную идею ниже, которая была вдохновлена ​​как strtok (), так и структурой «суффикс-массив», описанной в главе «Программирование Perls» Джона Бентли 2-го издания, глава 15. Класс C ++ в этом случае дает только некоторую организацию и удобство использования. Показанная реализация может быть легко расширена для удаления ведущих и завершающих пробельных символов в токенах.

В принципе, можно заменить символы разделителя на строки-завершающие «\ 0» -символы и установить указатели на маркеры с помощью модифицированная строка. В крайнем случае, когда строка состоит только из разделителей, получается строка длиной плюс 1 результирующие пустые маркеры.

Заголовочный файл:

class TextLineSplitter
{
public:

    TextLineSplitter( const size_t max_line_len );

    ~TextLineSplitter();

    void            SplitLine( const char *line,
                               const char sep_char = ',',
                             );

    inline size_t   NumTokens( void ) const
    {
        return mNumTokens;
    }

    const char *    GetToken( const size_t token_idx ) const
    {
        assert( token_idx < mNumTokens );
        return mTokens[ token_idx ];
    }

private:
    const size_t    mStorageSize;

    char           *mBuff;
    char          **mTokens;
    size_t          mNumTokens;

    inline void     ResetContent( void )
    {
        memset( mBuff, 0, mStorageSize );
        // mark all items as empty:
        memset( mTokens, 0, mStorageSize * sizeof( char* ) );
        // reset counter for found items:
        mNumTokens = 0L;
    }
};

Файл реализации:

TextLineSplitter::TextLineSplitter( const size_t max_line_len ):
    mStorageSize ( max_line_len + 1L )
{
    // allocate memory
    mBuff   = new char  [ mStorageSize ];
    mTokens = new char* [ mStorageSize ];

    ResetContent();
}

TextLineSplitter::~TextLineSplitter()
{
    delete [] mBuff;
    delete [] mTokens;
}


void TextLineSplitter::SplitLine( const char *line,
                                  const char sep_char   /* = ',' */,
                                )
{
    assert( sep_char != '\0' );

    ResetContent();
    strncpy( mBuff, line, mMaxLineLen );

    size_t idx       = 0L; // running index for characters

    do
    {
        assert( idx < mStorageSize );

        const char chr = line[ idx ]; // retrieve current character

        if( mTokens[ mNumTokens ] == NULL )
        {
            mTokens[ mNumTokens ] = &mBuff[ idx ];
        } // if

        if( chr == sep_char || chr == '\0' )
        { // item or line finished
            // overwrite separator with a 0-terminating character:
            mBuff[ idx ] = '\0';
            // count-up items:
            mNumTokens ++;
        } // if

    } while( line[ idx++ ] );
}

Сценарий использования будет :

// create an instance capable of splitting strings up to 1000 chars long:
TextLineSplitter spl( 1000 );
spl.SplitLine( "Item1,,Item2,Item3" );
for( size_t i = 0; i < spl.NumTokens(); i++ )
{
    printf( "%s\n", spl.GetToken( i ) );
}
Выход

:

Item1

Item2
Item3
0
ответ дан Angel Sinigersky 26 August 2018 в 22:55
поделиться

вы можете воспользоваться boost :: make_find_iterator. Что-то похожее на это:

template<typename CH>
inline vector< basic_string<CH> > tokenize(
    const basic_string<CH> &Input,
    const basic_string<CH> &Delimiter,
    bool remove_empty_token
    ) {

    typedef typename basic_string<CH>::const_iterator string_iterator_t;
    typedef boost::find_iterator< string_iterator_t > string_find_iterator_t;

    vector< basic_string<CH> > Result;
    string_iterator_t it = Input.begin();
    string_iterator_t it_end = Input.end();
    for(string_find_iterator_t i = boost::make_find_iterator(Input, boost::first_finder(Delimiter, boost::is_equal()));
        i != string_find_iterator_t();
        ++i) {
        if(remove_empty_token){
            if(it != i->begin())
                Result.push_back(basic_string<CH>(it,i->begin()));
        }
        else
            Result.push_back(basic_string<CH>(it,i->begin()));
        it = i->end();
    }
    if(it != it_end)
        Result.push_back(basic_string<CH>(it,it_end));

    return Result;
}
1
ответ дан Arash 26 August 2018 в 22:55
поделиться

Не обижайтесь, но для такой простой проблемы вы делаете вещи способом слишком сложными. Существует много причин использовать Boost . Но для чего-то такого простого, это как удар по мухе с салазками 20 #.

void
split( vector<string> & theStringVector,  /* Altered/returned value */
       const  string  & theString,
       const  string  & theDelimiter)
{
    UASSERT( theDelimiter.size(), >, 0); // My own ASSERT macro.

    size_t  start = 0, end = 0;

    while ( end != string::npos)
    {
        end = theString.find( theDelimiter, start);

        // If at end, use length=maxLength.  Else use length=end-start.
        theStringVector.push_back( theString.substr( start,
                       (end == string::npos) ? string::npos : end - start));

        // If at end, use start=maxSize.  Else use start=end+delimiter.
        start = (   ( end > (string::npos - theDelimiter.size()) )
                  ?  string::npos  :  end + theDelimiter.size());
    }
}

Например (для случая Дуга),

#define SHOW(I,X)   cout << "[" << (I) << "]\t " # X " = \"" << (X) << "\"" << endl

int
main()
{
    vector<string> v;

    split( v, "A:PEP:909:Inventory Item", ":" );

    for (unsigned int i = 0;  i < v.size();   i++)
        SHOW( i, v[i] );
}

И да, мы могли бы have split () возвращает новый вектор, а не передает его. Тривиально обернуть и перегрузить. Но в зависимости от того, что я делаю, я часто считаю, что лучше использовать ранее существовавшие объекты, а не создавать новые. (До тех пор, пока я не забываю опорожнить вектор между ними!) [/ ​​G6]

Ссылка: http://www.cplusplus.com/reference/string/string/ .

(я изначально писал ответ на вопрос Дуга: C ++ Strings Modifying and Extracting на основе разделителей (закрыто) . Но поскольку Мартин Йорк закрыл этот вопрос указателем здесь ... Я просто обобщу свой код.)

46
ответ дан Community 26 August 2018 в 22:55
поделиться

Boost имеет сильную функцию разделения: boost :: algorithm :: split .

Пример программы:

#include <vector>
#include <boost/algorithm/string.hpp>

int main() {
    auto s = "a,b, c ,,e,f,";
    std::vector<std::string> fields;
    boost::split(fields, s, boost::is_any_of(","));
    for (const auto& field : fields)
        std::cout << "\"" << field << "\"\n";
    return 0;
}

Выход:

"a"
"b"
" c "
""
"e"
"f"
""
34
ответ дан congusbongus 26 August 2018 в 22:55
поделиться

Я знаю, что вы попросили C ++-решение, но вы можете подумать об этом:

Qt

#include <QString>

...

QString str = "The quick brown fox"; 
QStringList results = str.split(" "); 

Преимущество над Boost в этом примере заключается в том, что он является прямым к одному сопоставлению с кодом вашего сообщения.

Подробнее в Документация Qt

23
ответ дан Daniel Brunner 26 August 2018 в 22:55
поделиться
9
ответ дан DannyK 26 August 2018 в 22:55
поделиться

Вот подход, который позволяет вам контролировать, включены ли пустые токены (например, strsep) или исключены (например, strtok).

#include <string.h> // for strchr and strlen

/*
 * want_empty_tokens==true  : include empty tokens, like strsep()
 * want_empty_tokens==false : exclude empty tokens, like strtok()
 */
std::vector<std::string> tokenize(const char* src,
                                  char delim,
                                  bool want_empty_tokens)
{
  std::vector<std::string> tokens;

  if (src and *src != '\0') // defensive
    while( true )  {
      const char* d = strchr(src, delim);
      size_t len = (d)? d-src : strlen(src);

      if (len or want_empty_tokens)
        tokens.push_back( std::string(src, len) ); // capture token

      if (d) src += len+1; else break;
    }

  return tokens;
}
2
ответ дан Darren Smith 26 August 2018 в 22:55
поделиться

Здесь много слишком сложных предложений. Попробуйте это простое решение std :: string:

using namespace std;

string someText = ...

string::size_type tokenOff = 0, sepOff = tokenOff;
while (sepOff != string::npos)
{
    sepOff = someText.find(' ', sepOff);
    string::size_type tokenLen = (sepOff == string::npos) ? sepOff : sepOff++ - tokenOff;
    string token = someText.substr(tokenOff, tokenLen);
    if (!token.empty())
        /* do something with token */;
    tokenOff = sepOff;
}
4
ответ дан David919 26 August 2018 в 22:55
поделиться

pystring - небольшая библиотека, которая реализует связку строковых функций Python, включая метод split:

#include <string>
#include <vector>
#include "pystring.h"

std::vector<std::string> chunks;
pystring::split("this string", chunks);

// also can specify a separator
pystring::split("this-string", chunks, "-");
16
ответ дан dbr 26 August 2018 в 22:55
поделиться

Еще один быстрый способ - использовать getline. Что-то вроде:

stringstream ss("bla bla");
string s;

while (getline(ss, s, ' ')) {
 cout << s << endl;
}

Если вы хотите, вы можете сделать простой метод split(), возвращая vector<string>, что действительно полезно.

88
ответ дан Dominic Rodger 26 August 2018 в 22:55
поделиться

Я думал, что это был оператор >> в строковых потоках:

string word; sin >> word;
4
ответ дан Felix Dombek 26 August 2018 в 22:55
поделиться

Класс Boost tokenizer может сделать эту вещь довольно простой:

#include <iostream>
#include <string>
#include <boost/foreach.hpp>
#include <boost/tokenizer.hpp>

using namespace std;
using namespace boost;

int main(int, char**)
{
    string text = "token, test   string";

    char_separator<char> sep(", ");
    tokenizer< char_separator<char> > tokens(text, sep);
    BOOST_FOREACH (const string& t, tokens) {
        cout << t << "." << endl;
    }
}

Обновлен для C ++ 11:

#include <iostream>
#include <string>
#include <boost/tokenizer.hpp>

using namespace std;
using namespace boost;

int main(int, char**)
{
    string text = "token, test   string";

    char_separator<char> sep(", ");
    tokenizer<char_separator<char>> tokens(text, sep);
    for (const auto& t : tokens) {
        cout << t << "." << endl;
    }
}
181
ответ дан Ferruccio 26 August 2018 в 22:55
поделиться

Для простых вещей я просто использую следующее:

unsigned TokenizeString(const std::string& i_source,
                        const std::string& i_seperators,
                        bool i_discard_empty_tokens,
                        std::vector<std::string>& o_tokens)
{
    unsigned prev_pos = 0;
    unsigned pos = 0;
    unsigned number_of_tokens = 0;
    o_tokens.clear();
    pos = i_source.find_first_of(i_seperators, pos);
    while (pos != std::string::npos)
    {
        std::string token = i_source.substr(prev_pos, pos - prev_pos);
        if (!i_discard_empty_tokens || token != "")
        {
            o_tokens.push_back(i_source.substr(prev_pos, pos - prev_pos));
            number_of_tokens++;
        }

        pos++;
        prev_pos = pos;
        pos = i_source.find_first_of(i_seperators, pos);
    }

    if (prev_pos < i_source.length())
    {
        o_tokens.push_back(i_source.substr(prev_pos));
        number_of_tokens++;
    }

    return number_of_tokens;
}

Тщательно оговорка: я пишу программное обеспечение обработки данных в реальном времени, где данные поступают через двоичные файлы, сокеты или какой-либо вызов API (I / O, камера). Я никогда не использую эту функцию для чего-то более сложного или критического по времени, чем чтение внешних файлов конфигурации при запуске.

4
ответ дан jilles de wit 26 August 2018 в 22:55
поделиться

MFC / ATL имеет очень хороший токенизатор. Из MSDN:

CAtlString str( "%First Second#Third" );
CAtlString resToken;
int curPos= 0;

resToken= str.Tokenize("% #",curPos);
while (resToken != "")
{
   printf("Resulting token: %s\n", resToken);
   resToken= str.Tokenize("% #",curPos);
};

Output

Resulting Token: First
Resulting Token: Second
Resulting Token: Third
4
ответ дан Jim In Texas 26 August 2018 в 22:55
поделиться

. Ответ Адама Пирса предоставляет токенизатор с ручным движением, взятый в const char*. Это немного более проблематично в отношении итераторов, потому что , увеличивающий конечный итератор string, не определен . Тем не менее, учитывая string str{ "The quick brown fox" }, мы можем это сделать:

auto start = find(cbegin(str), cend(str), ' ');
vector<string> tokens{ string(cbegin(str), start) };

while (start != cend(str)) {
    const auto finish = find(++start, cend(str), ' ');

    tokens.push_back(string(start, finish));
    start = finish;
}

Live Example


Если вы ищете абстрактную сложность с помощью стандартных функций, поскольку On Freund предлагает strtok - это простая опция:

vector<string> tokens;

for (auto i = strtok(data(str), " "); i != nullptr; i = strtok(nullptr, " ")) tokens.push_back(i);

Если у вас нет доступа к C ++ 17 вам нужно подставить data(str), как в этом примере: http://ideone.com/8kAGoa

Хотя в этом примере не показано strtok не нужно использовать один и тот же разделитель для каждого токена. Наряду с этим преимуществом, однако, существует несколько недостатков:

  1. strtok не может использоваться одновременно на нескольких strings: либо необходимо передать nullptr для продолжения токенизации текущего string или новый char* tokenize должен быть передан (существуют некоторые нестандартные реализации, которые поддерживают это, например: strtok_s )
  2. Для по той же причине strtok нельзя использовать одновременно для нескольких потоков (это может быть определено как реализация, например: Реализация Visual Studio является потокобезопасной )
  3. Вызов strtok изменяет string он работает, поэтому он не может использоваться на const string s, const char* s или литеральных строках, чтобы токенизировать любой из них с помощью strtok или для работы с string, которому необходимо содержимое str необходимо скопировать, тогда копия могла бы работать на

Оба предыдущих метода не могут генерировать токенизированные vector на месте, что означает без абстрагируя их в вспомогательную функцию, они не могут Инициализировать const vector<string> tokens. Эта функциональность и способна принять любой разделитель пробелов в пробеле, может быть использована с помощью istream_iterator . Например: const string str{ "The quick \tbrown \nfox" } мы можем это сделать:

istringstream is{ str };
const vector<string> tokens{ istream_iterator<string>(is), istream_iterator<string>() };

Live Example

Необходимая конструкция istringstream для этой опции имеет гораздо большую стоимость, чем предыдущие 2 варианта, однако эта стоимость обычно скрыта за счет распределения string.


Если ни один из вышеперечисленных вариантов недостаточно гибкий для ваших нужд токенизации, гибкая опция использует regex_token_iterator , конечно же, с этой гибкостью приносит больший расход, но опять же это, вероятно, скрыто в стоимости распределения string. Скажем, например, мы хотим tokenize на основе неэкранированных запятых, также использующих белое пространство, учитывая следующий ввод: const string str{ "The ,qu\\,ick ,\tbrown, fox" } мы можем это сделать:

const regex re{ "\\s*((?:[^\\\\,]|\\\\.)*?)\\s*(?:,|$)" };
const vector<string> tokens{ sregex_token_iterator(cbegin(str), cend(str), re, 1), sregex_token_iterator() };

Live Example

4
ответ дан Jonathan Leffler 26 August 2018 в 22:55
поделиться

boost::tokenizer является вашим другом, но подумайте о том, чтобы сделать ваш код переносимым со ссылкой на проблемы интернационализации (i18n) с помощью wstring / wchar_t вместо устаревшего string / char.

#include <iostream>
#include <boost/tokenizer.hpp>
#include <string>

using namespace std;
using namespace boost;

typedef tokenizer<char_separator<wchar_t>,
                  wstring::const_iterator, wstring> Tok;

int main()
{
  wstring s;
  while (getline(wcin, s)) {
    char_separator<wchar_t> sep(L" "); // list of separator characters
    Tok tok(s, sep);
    for (Tok::iterator beg = tok.begin(); beg != tok.end(); ++beg) {
      wcout << *beg << L"\t"; // output (or store in vector)
    }
    wcout << L"\n";
  }
  return 0;
}
0
ответ дан kay 26 August 2018 в 22:55
поделиться

Вы можете использовать потоки, итераторы и алгоритм копирования для этого достаточно прямо.

#include <string>
#include <vector>
#include <iostream>
#include <istream>
#include <ostream>
#include <iterator>
#include <sstream>
#include <algorithm>

int main()
{
  std::string str = "The quick brown fox";

  // construct a stream from the string
  std::stringstream strstr(str);

  // use stream iterators to copy the stream to the vector as whitespace separated strings
  std::istream_iterator<std::string> it(strstr);
  std::istream_iterator<std::string> end;
  std::vector<std::string> results(it, end);

  // send the vector to stdout.
  std::ostream_iterator<std::string> oit(std::cout);
  std::copy(results.begin(), results.end(), oit);
}
80
ответ дан KeithB 26 August 2018 в 22:55
поделиться
102
ответ дан Mark 26 August 2018 в 22:55
поделиться
/// split a string into multiple sub strings, based on a separator string
/// for example, if separator="::",
///
/// s = "abc" -> "abc"
///
/// s = "abc::def xy::st:" -> "abc", "def xy" and "st:",
///
/// s = "::abc::" -> "abc"
///
/// s = "::" -> NO sub strings found
///
/// s = "" -> NO sub strings found
///
/// then append the sub-strings to the end of the vector v.
/// 
/// the idea comes from the findUrls() function of "Accelerated C++", chapt7,
/// findurls.cpp
///
void split(const string& s, const string& sep, vector<string>& v)
{
    typedef string::const_iterator iter;
    iter b = s.begin(), e = s.end(), i;
    iter sep_b = sep.begin(), sep_e = sep.end();

    // search through s
    while (b != e){
        i = search(b, e, sep_b, sep_e);

        // no more separator found
        if (i == e){
            // it's not an empty string
            if (b != e)
                v.push_back(string(b, e));
            break;
        }
        else if (i == b){
            // the separator is found and right at the beginning
            // in this case, we need to move on and search for the
            // next separator
            b = i + sep.length();
        }
        else{
            // found the separator
            v.push_back(string(b, i));
            b = i;
        }
    }
}

Библиотека ускорения хороша, но они не всегда доступны. Выполнение такого рода вещей вручную также является хорошим упражнением на мозг. Здесь мы просто используем алгоритм std :: search () из STL, см. Приведенный выше код.

0
ответ дан Murphy78 26 August 2018 в 22:55
поделиться

Нет прямого способа сделать это. Обратитесь к этому исходному коду проекта кода , чтобы узнать, как построить для этого класс.

1
ответ дан Niyaz 26 August 2018 в 22:55
поделиться

Кажется странным для меня, что со всеми нами уверенными ботаниками здесь, на SO, никто не представил версию, которая использует сводную таблицу сгенерированного времени для разделителя (пример реализации дальше вниз). Использование таблицы поиска и итераторов должно эффективно бить std :: regex, если вам не нужно бить регулярное выражение, просто используйте его, его стандарт как C ++ 11 и супер гибкий.

Some уже предложили регулярное выражение, но для noobs здесь представлен упакованный пример, который должен делать именно то, что ожидает OP:

std::vector<std::string> split(std::string::const_iterator it, std::string::const_iterator end, std::regex e = std::regex{"\\w+"}){
    std::smatch m{};
    std::vector<std::string> ret{};
    while (std::regex_search (it,end,m,e)) {
        ret.emplace_back(m.str());              
        std::advance(it, m.position() + m.length()); //next start position = match position + match length
    }
    return ret;
}
std::vector<std::string> split(const std::string &s, std::regex e = std::regex{"\\w+"}){  //comfort version calls flexible version
    return split(s.cbegin(), s.cend(), std::move(e));
}
int main ()
{
    std::string str {"Some people, excluding those present, have been compile time constants - since puberty."};
    auto v = split(str);
    for(const auto&s:v){
        std::cout << s << std::endl;
    }
    std::cout << "crazy version:" << std::endl;
    v = split(str, std::regex{"[^e]+"});  //using e as delim shows flexibility
    for(const auto&s:v){
        std::cout << s << std::endl;
    }
    return 0;
}

Если нам нужно быть быстрее и принять ограничение, что все символы должны быть 8 бит, мы может отображать таблицу во время компиляции с использованием метапрограммирования:

template<bool...> struct BoolSequence{};        //just here to hold bools
template<char...> struct CharSequence{};        //just here to hold chars
template<typename T, char C> struct Contains;   //generic
template<char First, char... Cs, char Match>    //not first specialization
struct Contains<CharSequence<First, Cs...>,Match> :
    Contains<CharSequence<Cs...>, Match>{};     //strip first and increase index
template<char First, char... Cs>                //is first specialization
struct Contains<CharSequence<First, Cs...>,First>: std::true_type {}; 
template<char Match>                            //not found specialization
struct Contains<CharSequence<>,Match>: std::false_type{};

template<int I, typename T, typename U> 
struct MakeSequence;                            //generic
template<int I, bool... Bs, typename U> 
struct MakeSequence<I,BoolSequence<Bs...>, U>:  //not last
    MakeSequence<I-1, BoolSequence<Contains<U,I-1>::value,Bs...>, U>{};
template<bool... Bs, typename U> 
struct MakeSequence<0,BoolSequence<Bs...>,U>{   //last  
    using Type = BoolSequence<Bs...>;
};
template<typename T> struct BoolASCIITable;
template<bool... Bs> struct BoolASCIITable<BoolSequence<Bs...>>{
    /* could be made constexpr but not yet supported by MSVC */
    static bool isDelim(const char c){
        static const bool table[256] = {Bs...};
        return table[static_cast<int>(c)];
    }   
};
using Delims = CharSequence<'.',',',' ',':','\n'>;  //list your custom delimiters here
using Table = BoolASCIITable<typename MakeSequence<256,BoolSequence<>,Delims>::Type>;

При этом создание функции getNextToken очень просто:

template<typename T_It>
std::pair<T_It,T_It> getNextToken(T_It begin,T_It end){
    begin = std::find_if(begin,end,std::not1(Table{})); //find first non delim or end
    auto second = std::find_if(begin,end,Table{});      //find first delim or end
    return std::make_pair(begin,second);
}

Использование этого также легко :

int main() {
    std::string s{"Some people, excluding those present, have been compile time constants - since puberty."};
    auto it = std::begin(s);
    auto end = std::end(s);
    while(it != std::end(s)){
        auto token = getNextToken(it,end);
        std::cout << std::string(token.first,token.second) << std::endl;
        it = token.second;
    }
    return 0;
}

Вот живой пример: http://ideone.com/GKtkLQ

2
ответ дан odinthenerd 26 August 2018 в 22:55
поделиться

Вот реальный простой:

#include <vector>
#include <string>
using namespace std;

vector<string> split(const char *str, char c = ' ')
{
    vector<string> result;

    do
    {
        const char *begin = str;

        while(*str != c && *str)
            str++;

        result.push_back(string(begin, str));
    } while (0 != *str++);

    return result;
}
159
ответ дан Oktalist 26 August 2018 в 22:55
поделиться

Если вы хотите использовать C, вы можете использовать функцию strtok .

Следует обратить внимание на многопоточность при использовании.

4
ответ дан On Freund 26 August 2018 в 22:55
поделиться

Это простое решение STL (~ 5 строк!) с использованием std::find и std::find_first_not_of, которое обрабатывает повторы разделителя (например, пробелы или периоды), а также ведущие и конечные разделители:

#include <string>
#include <vector>

void tokenize(std::string str, std::vector<string> &token_v){
    size_t start = str.find_first_not_of(DELIMITER), end=start;

    while (start != std::string::npos){
        // Find next occurence of delimiter
        end = str.find(DELIMITER, start);
        // Push back the token found into vector
        token_v.push_back(str.substr(start, end-start));
        // Skip all occurences of the delimiter to find new start
        start = str.find_first_not_of(DELIMITER, end);
    }
}

Попробуйте live !

17
ответ дан Parham 26 August 2018 в 22:55
поделиться

Вы можете просто использовать библиотеку регулярных выражений и решить это с помощью регулярных выражений.

Использовать выражение (\ w +) и переменную в \ 1 (или $ 1 в зависимости от библиотечная реализация регулярных выражений).

5
ответ дан Peter Mortensen 26 August 2018 в 22:55
поделиться

Проверьте этот пример. Это может помочь вам.

#include <iostream>
#include <sstream>

using namespace std;

int main ()
{
    string tmps;
    istringstream is ("the dellimiter is the space");
    while (is.good ()) {
        is >> tmps;
        cout << tmps << "\n";
    }
    return 0;
}
8
ответ дан sohesado 26 August 2018 в 22:55
поделиться

Простой код C ++ (стандартный C ++ 98), принимает несколько разделителей (заданных в std :: string), использует только векторы, строки и итераторы.

#include <iostream>
#include <vector>
#include <string>
#include <stdexcept> 

std::vector<std::string> 
split(const std::string& str, const std::string& delim){
    std::vector<std::string> result;
    if (str.empty())
        throw std::runtime_error("Can not tokenize an empty string!");
    std::string::const_iterator begin, str_it;
    begin = str_it = str.begin(); 
    do {
        while (delim.find(*str_it) == std::string::npos && str_it != str.end())
            str_it++; // find the position of the first delimiter in str
        std::string token = std::string(begin, str_it); // grab the token
        if (!token.empty()) // empty token only when str starts with a delimiter
            result.push_back(token); // push the token into a vector<string>
        while (delim.find(*str_it) != std::string::npos && str_it != str.end())
            str_it++; // ignore the additional consecutive delimiters
        begin = str_it; // process the remaining tokens
        } while (str_it != str.end());
    return result;
}

int main() {
    std::string test_string = ".this is.a.../.simple;;test;;;END";
    std::string delim = "; ./"; // string containing the delimiters
    std::vector<std::string> tokens = split(test_string, delim);           
    for (std::vector<std::string>::const_iterator it = tokens.begin(); 
        it != tokens.end(); it++)
            std::cout << *it << std::endl;
}
0
ответ дан vsoftco 26 August 2018 в 22:55
поделиться

Вот пример класса токенизатора, который может делать то, что вы хотите

//Header file
class Tokenizer 
{
    public:
        static const std::string DELIMITERS;
        Tokenizer(const std::string& str);
        Tokenizer(const std::string& str, const std::string& delimiters);
        bool NextToken();
        bool NextToken(const std::string& delimiters);
        const std::string GetToken() const;
        void Reset();
    protected:
        size_t m_offset;
        const std::string m_string;
        std::string m_token;
        std::string m_delimiters;
};

//CPP file
const std::string Tokenizer::DELIMITERS(" \t\n\r");

Tokenizer::Tokenizer(const std::string& s) :
    m_string(s), 
    m_offset(0), 
    m_delimiters(DELIMITERS) {}

Tokenizer::Tokenizer(const std::string& s, const std::string& delimiters) :
    m_string(s), 
    m_offset(0), 
    m_delimiters(delimiters) {}

bool Tokenizer::NextToken() 
{
    return NextToken(m_delimiters);
}

bool Tokenizer::NextToken(const std::string& delimiters) 
{
    size_t i = m_string.find_first_not_of(delimiters, m_offset);
    if (std::string::npos == i) 
    {
        m_offset = m_string.length();
        return false;
    }

    size_t j = m_string.find_first_of(delimiters, i);
    if (std::string::npos == j) 
    {
        m_token = m_string.substr(i);
        m_offset = m_string.length();
        return true;
    }

    m_token = m_string.substr(i, j - i);
    m_offset = j;
    return true;
}

Пример:

std::vector <std::string> v;
Tokenizer s("split this string", " ");
while (s.NextToken())
{
    v.push_back(s.GetToken());
}
22
ответ дан vzczc 26 August 2018 в 22:55
поделиться

Решение с использованием regex_token_iterator s:

#include <iostream>
#include <regex>
#include <string>

using namespace std;

int main()
{
    string str("The quick brown fox");

    regex reg("\\s+");

    sregex_token_iterator iter(str.begin(), str.end(), reg, -1);
    sregex_token_iterator end;

    vector<string> vec(iter, end);

    for (auto a : vec)
    {
        cout << a << endl;
    }
}
23
ответ дан w.b 26 August 2018 в 22:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: