Подтвердить что ты не робот

Использование strtok с std::string

У меня есть строка, которую я хотел бы tokenize. Но для функции C strtok() моя строка должна быть char*. Как я могу сделать это просто?

Я пробовал:

token = strtok(str.c_str(), " "); 

который терпит неудачу, потому что он превращает его в const char*, а не a char*

4b9b3361

Ответ 1

#include <iostream>
#include <string>
#include <sstream>
int main(){
    std::string myText("some-text-to-tokenize");
    std::istringstream iss(myText);
    std::string token;
    while (std::getline(iss, token, '-'))
    {
        std::cout << token << std::endl;
    }
    return 0;
}

Или, как уже упоминалось, используйте boost для большей гибкости.

Ответ 2

  • Если boost доступен в вашей системе (я думаю, что это стандарт на большинстве дистрибутивов Linux в наши дни), он имеет Tokenizer класс, который вы можете использовать.

  • Если нет, тогда быстрый Google превратит ручной токенизатор для std::string, который вы, вероятно, можете просто скопировать и вставить. Это очень коротко.

  • И, если вам не нравится ни одно из этих, то здесь функция split(), которую я написал, чтобы облегчить мою жизнь. Он разбивает цепочку на кусочки, используя любой из символов в "delim" в качестве разделителей. Пьесы добавляются к вектору "частей":

    void split(const string& str, const string& delim, vector<string>& parts) {
      size_t start, end = 0;
      while (end < str.size()) {
        start = end;
        while (start < str.size() && (delim.find(str[start]) != string::npos)) {
          start++;  // skip initial whitespace
        }
        end = start;
        while (end < str.size() && (delim.find(str[end]) == string::npos)) {
          end++; // skip to end of word
        }
        if (end-start != 0) {  // just ignore zero-length strings.
          parts.push_back(string(str, start, end-start));
        }
      }
    }
    

Ответ 3

Дублируйте строку, отметьте ее, а затем освободите ее.

char *dup = strdup(str.c_str());
token = strtok(dup, " ");
free(dup);

Ответ 4

Существует более элегантное решение.

С помощью std::string вы можете использовать resize() для выделения подходящего большого буфера и & s [0], чтобы получить указатель на внутренний буфер.

В этот момент многие прекрасные люди будут прыгать и кричать на экране. Но это факт. Около 2 лет назад

рабочая группа библиотеки решила (встреча в Лиллехаммере), которая, как и для std::vector, std::string, также должна формально, не только на практике, иметь гарантированный непрерывный буфер.

Другая проблема заключается в том, что strtok() увеличивает размер строки. В документации MSDN говорится:

Каждый вызов strtok изменяет strToken, вставив нулевой символ после токена, возвращенного этим вызовом.

Но это неверно. Фактически функция заменяет первое вхождение символа разделителя на \0. Никаких изменений в размере строки. Если у нас есть эта строка:

один два три четыре

мы получим

один \0two\0 - три\0-четыре

Итак, мое решение очень просто:


std::string str("some-text-to-split");
char seps[] = "-";
char *token;

token = strtok( &str[0], seps );
while( token != NULL )
{
   /* Do your thing */
   token = strtok( NULL, seps );
}

Прочитайте обсуждение http://www.archivum.info/comp.lang.c++/2008-05/02889/does_std::string_have_something_like_CString::GetBuffer p >

Ответ 5

Я предполагаю, что язык C, или С++...

strtok, IIRC, замените разделители на \0. Это то, что он не может использовать строку const. Чтобы обходной путь "быстро", если строка не огромна, вы можете просто strdup() ее. Это разумно, если вам нужно сохранить строку неизменной (что предлагает const)...

С другой стороны, вы можете использовать другой токенизатор, возможно, ручной показ, менее сильный по данному аргументу.

Ответ 6

Предполагая, что с помощью "string" вы говорите о std::string в С++, вы можете взглянуть на Tokenizer пакет в Boost.

Ответ 7

EDIT: использование const cast только используется для демонстрации эффекта strtok() при применении к указателю, возвращаемому строкой:: c_str().

Вы не должны использовать strtok(), так как он изменяет токенизированную строку, которая может привести к нежелательному, если не undefined, поведению, поскольку строка C "принадлежит" экземпляру строки.

#include <string>
#include <iostream>

int main(int ac, char **av)
{
    std::string theString("hello world");
    std::cout << theString << " - " << theString.size() << std::endl;

    //--- this cast *only* to illustrate the effect of strtok() on std::string 
    char *token = strtok(const_cast<char  *>(theString.c_str()), " ");

    std::cout << theString << " - " << theString.size() << std::endl;

    return 0;
}

После вызова strtok() пробел был "удален" из строки или отклонен до символа, не подлежащего печати, но длина остается неизменной.

>./a.out
hello world - 11
helloworld - 11

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

Ответ 8

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

Но оба вышеизложенного уже покрыты. Итак, как третий вариант C-Like, я предлагаю скопировать std::string в буфер для модификации.

std::string   data("The data I want to tokenize");

// Create a buffer of the correct length:
std::vector<char>  buffer(data.size()+1);

// copy the string into the buffer
strcpy(&buffer[0],data.c_str());

// Tokenize
strtok(&buffer[0]," ");

Ответ 9

Если вы не против открытого источника, вы можете использовать классы subbuffer и subparser из https://github.com/EdgeCast/json_parser. Исходная строка остается нетронутой, нет выделения и копирования данных. Я не компилировал следующее, чтобы могли быть ошибки.

std::string input_string("hello world");
subbuffer input(input_string);
subparser flds(input, ' ', subparser::SKIP_EMPTY);
while (!flds.empty())
{
    subbuffer fld = flds.next();
    // do something with fld
}

// or if you know it is only two fields
subbuffer fld1 = input.before(' ');
subbuffer fld2 = input.sub(fld1.length() + 1).ltrim(' ');

Ответ 10

В С++ 17 str::string получает перегрузку data(), которая возвращает указатель на модифицируемый буфер, поэтому строку можно использовать в strtok напрямую без каких-либо взломов:

#include <string>
#include <iostream>
#include <cstring>
#include <cstdlib>

int main()
{
    ::std::string text{"pop dop rop"};
    char const * const psz_delimiter{" "};
    char * psz_token{::std::strtok(text.data(), psz_delimiter)};
    while(nullptr != psz_token)
    {
        ::std::cout << psz_token << ::std::endl;
        psz_token = std::strtok(nullptr, psz_delimiter);
    }
    return EXIT_SUCCESS;
}

выход

pop
dop
rop

Ответ 11

Сбой, потому что str.c_str() возвращает константную строку, но char * strtok (char * str, const char * delimiters ) требует изменяемой строки. Поэтому вам нужно использовать inorder * const_cast <char>, чтобы сделать его voletile. Я даю вам полную, но небольшую программу для токенизации строки с помощью функции C strtok().

   #include <iostream>
   #include <string>
   #include <string.h> 
   using namespace std;
   int main() {
       string s="20#6 5, 3";
       // strtok requires volatile string as it modifies the supplied string in order to tokenize it 
       char *str=const_cast< char *>(s.c_str());    
       char *tok;
       tok=strtok(str, "#, " );     
       int arr[4], i=0;    
       while(tok!=NULL){
           arr[i++]=stoi(tok);
           tok=strtok(NULL, "#, " );
       }     
       for(int i=0; i<4; i++) cout<<arr[i]<<endl;


       return 0;
   }

ПРИМЕЧАНИЕ: strtok может не подходить для всех ситуаций, так как строка, переданная в функцию, изменяется, разбиваясь на более мелкие строки. Пожалуйста, обратитесь, чтобы получить лучшее понимание функциональности strtok.

Как работает strtok

Добавлено несколько операторов печати, чтобы лучше понять, какие изменения происходят в строке при каждом вызове strtok, и как она возвращает токен.

#include <iostream>
#include <string>
#include <string.h> 
using namespace std;
int main() {
    string s="20#6 5, 3";
    char *str=const_cast< char *>(s.c_str());    
    char *tok;
    cout<<"string: "<<s<<endl;
    tok=strtok(str, "#, " );     
    cout<<"String: "<<s<<"\tToken: "<<tok<<endl;   
    while(tok!=NULL){
        tok=strtok(NULL, "#, " );
        cout<<"String: "<<s<<"\t\tToken: "<<tok<<endl;
    }
    return 0;
}

Выход:

string: 20#6 5, 3

String: 206 5, 3    Token: 20
String: 2065, 3     Token: 6
String: 2065 3      Token: 5
String: 2065 3      Token: 3
String: 2065 3      Token: 

strtok выполняет итерацию по строке. Сначала вызывается поиск символа, не являющегося удалителем (в нашем случае это 2), и он помечается как начало токена, затем продолжается поиск разделителя и заменяется нулевым символом (# заменяется в фактической строке) и возвращается начало, которое указывает на символ начала токена (т.е. он возвращает токен 20, который завершается нулем). При последующем вызове он начинает сканирование со следующего символа и возвращает токен, если найден еще ноль. соответственно возвращает токены 6, 5, 3.