Как использовать strtok для char*

В С++, чтобы отфильтровать разделитель с помощью strtok, источник должен быть массивом символов, в противном случае это дает мне ошибку seg. Как я могу использовать strtok для указателя на char?

Пример кода структурирования strtok:

#include <stdio.h>
#include <string.h>

int main () {
  char str[] ="- This, a sample string."; // this is the string i want to split. notice how it's an array
  char * pch;
  pch = strtok (str," ,.-");
  while (pch != NULL)
  {
    printf ("%s\n",pch);
    pch = strtok (NULL, " ,.-");
  }
  return 0;
}

Пример того, что я хочу сделать:

/* strtok example */
#include <stdio.h>
#include <string.h>

int main ()
{
  char* str ="- This, a sample string."; // since this is a pointer to char, it gives a segmentation fault after compiling, and executing.
  char * pch;
  pch = strtok (str," ,.-");
  while (pch != NULL)
  {
    printf ("%s\n",pch);
    pch = strtok (NULL, " ,.-");
  }
  return 0;
}

Не указывайте его на строковый литерал, который может и, скорее всего, будет находиться в постоянной памяти. Сначала создайте копию, например. используя strdup, потому что strtok может и, скорее всего, изменит строку.

Cheatah 23.04.2022 12:11
strtok изменяет сканируемую строку C, добавляя нулевые символы в конце каждого токена. Лучше не делать этого с массивами C-char внутри строк C++. Используйте копию.
Michel Billaud 23.04.2022 12:12

Не помечайте C для вопросов C++.

Eric Postpischil 23.04.2022 12:23
3 метода стилизации элементов HTML
3 метода стилизации элементов HTML
Когда дело доходит до применения какого-либо стиля к нашему HTML, существует три подхода: встроенный, внутренний и внешний. Предпочтительным обычно...
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
1
3
70
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы пытаетесь изменить строковый литерал (функция strtok изменяет исходную строку, вставляя нулевые символы '\0')

char* str ="- This, a sample string.";

Во-первых, в C++, в отличие от C, строковые литералы имеют типы константных массивов символов. Поэтому вам нужно написать объявление указателя в программе на C++ с квалификатором const.

const char* str ="- This, a sample string.";

Любая попытка изменить строковый литерал в C и C++ приводит к неопределенному поведению.

Например, в стандарте C написано (6.4.5 Строковые литералы)

7 It is unspecified whether these arrays are distinct provided their elements have the appropriate values. If the program attempts to modify such an array, the behavior is undefined.

Так что всегда лучше и в C объявлять указатели на строковые литералы с помощью квалификатора const.

Вместо strtok вы можете использовать, например, стандартную строковую функцию C strspn и strcspn.

Вот демонстрационная программа.

#include <iostream>
#include <iomanip>
#include <string_view>
#include <cstring>

int main()
{
    const char *s = "- This, a sample string.";
    const char *delim = " ., -";

    for (const char *p = s; *( p += strspn( p, delim ) ) != '\0'; )
    {
        auto n = strcspn( p, delim );

        std::string_view sv( p, n );

        std::cout << std::quoted( sv ) << ' ';

        p += n;
    }

    std::cout << '\n';
}

Вывод программы

"This" "a" "sample" "string"

Например, вы можете объявить вектор строковых представлений, например std::vector<std::string_view>, и хранить в нем каждую подстроку.

Например

#include <iostream>
#include <iomanip>
#include <string_view>
#include <vector>
#include <cstring>

int main()
{
    const char *s = "- This, a sample string.";
    const char *delim = " ., -";

    std::vector<std::string_view> v;

    for (const char *p = s; *( p += strspn( p, delim ) ) != '\0'; )
    {
        auto n = strcspn( p, delim );

        v.emplace_back( p, n );

        p += n;
    }

    for (auto sv : v)
    {
        std::cout << std::quoted( sv ) << ' ';
    }
    std::cout << '\n';
}

Вывод программы такой же, как показано выше.

Или, если компилятор не поддерживает C++ 17, то вместо вектора типа std::vector<std::string_view> можно использовать вектор типа std::vector<std::pair<const char *, size_t>>.

Например

#include <iostream>
#include <iomanip>
#include <utility>
#include <vector>
#include <cstring>

int main()
{
    const char *s = "- This, a sample string.";
    const char *delim = " ., -";

    std::vector<std::pair<const char *, size_t>> v;

    for (const char *p = s; *( p += strspn( p, delim ) ) != '\0'; )
    {
        auto n = strcspn( p, delim );

        v.emplace_back( p, n );

        p += n;
    }

    for (auto p : v)
    {
        std::cout.write( p.first, p.second ) << ' ';
    }
    std::cout << '\n';
}

Вывод программы

This a sample string

Или вы можете использовать вектор объектов типа std::string: std::vector<std::string>.

В C вы можете использовать массив переменной длины или динамически размещаемый массив с типом элемента структурного типа, который содержит два члена данных типа const char * и size_t аналогично классу C++ std::pair. Но чтобы определить массив, вам сначала нужно вычислить, сколько слов содержится в строковом литерале, используя тот же цикл for.

Вот демонстрационная программа C.

#include <stdio.h>
#include <string.h>

int main( void )
{
    const char *s = "- This, a sample string.";
    const char *delim = " ., -";

    size_t nmemb = 0;

    for (const char *p = s; *( p += strspn( p, delim ) ) != '\0'; )
    {
        ++nmemb;
        size_t n = strcspn( p, delim );
        p += n;
    }    

    struct SubString
    {
        const char *pos;
        size_t size;
    } a[nmemb];

    size_t i = 0;

    for (const char *p = s; *( p += strspn( p, delim ) ) != '\0'; )
    {
        size_t n = strcspn( p, delim );

        a[i].pos = p;
        a[i].size =n;
        ++i;
        p += n;
    }

    for ( i = 0; i < nmemb; i++ )
    {
        printf( "%.*s ", ( int )a[i].size, a[i].pos );
    } 

    putchar( '\n' );   
}

Вывод программы

This a sample string

Example of what I want to do:

char* str ="- This, a sample string."

Вы не можете делать то, что хотите, потому что строковые литералы не могут быть неявно преобразованы в указатель на неконстантный char в C++. Кроме того, strtok изменяет строку аргумента, а литералы srtring не должны изменяться в C++.

How to use strtok on c++ on char*

Если очень хочется, то можно сделать так:

char str_arr[] ="- This, a sample string.";
char* str = str_arr;

Но это будет скорее бессмысленный.


Чтобы токенизировать строковый литерал без его копирования в изменяемый массив, вы не должны использовать strtok.

Другие вопросы по теме