C++ Как преобразовать строку в символ *
Мне нужно преобразовать строку в char * для использования в strtok_s, и я не смог понять это. c_str() конвертируется в const char *, что несовместимо.
Кроме того, если бы кто-нибудь мог объяснить мне, почему вторая функция strtok_s (внутри цикла) необходима, это было бы очень полезно. Почему мне нужно явно продвигать токен, а не, например, в цикле while, в котором он находится, который неявно извлекает каждую строку файла последовательно.
while( getline(myFile, line) ) { // Only one line anyway. . . is there a better way?
char * con = line.c_str();
token = strtok_s( con, "#", &next_token);
while ((token != NULL))
{
printf( " %s\n", token );
token = strtok_s( NULL, "#", &next_token);
}
}
связанный вопрос.
10 ответов
Использование strdup()
скопировать const char *
вернулся c_str()
в char *
(Запомни free()
это потом)
Обратите внимание, что strdup()
а также free()
функции C, а не C++, и вам лучше использовать методы std::string
вместо.
Второй strtok_s() необходим, потому что иначе ваш цикл не завершится (token
значение не изменится).
Как сказал Даниил, вы можете пойти с
strdup(line.c_str());
Что лучше, чем strcpy, который я изначально предложил, так как он выделяет необходимое пространство
Вы не можете преобразовать в char *
потому что это позволило бы вам написать std::string
внутренний буфер. Чтобы не делать std::string
Реализация видна, это не разрешено.
Вместо strtok
попробуйте более "C++- подобный" способ токенизации строк. Смотрите этот вопрос:
strtok()
это плохо спроектированная функция для начала. Проверьте свою документацию, чтобы увидеть, есть ли у вас лучшая. Кстати, никогда не используйте strtok()
в любой среде с многопоточностью, если ваши документы не говорят, что это безопасно, поскольку она сохраняет состояние между вызовами и изменяет строку, к которой она вызывается. Я предполагаю strtok_s()
это более безопасная версия, но она не будет действительно безопасной.
Чтобы преобразовать std::string
в char *
, ты можешь сделать:
char * temp_line = new char[line.size() + 1]; // +1 char for '\0' terminator
strcpy(temp_line, line.c_str());
и использовать temp_line
, Ваша установка может иметь strdup()
функция, которая будет дублировать вышеперечисленное.
Причина, по которой вам нужно два звонка strtok_s()
в том, что они делают разные вещи. Первый рассказывает strtok_s()
с какой строкой нужно работать, а вторая продолжается с той же строкой. Это причина для аргумента NULL; оно говорит strtok_s()
продолжать идти с оригинальной строкой.
Следовательно, вам нужен один вызов для получения первого токена, а затем - один для каждого последующего токена. Они могут быть объединены с чем-то вроде
char * temp_string_pointer = temp_line;
while ((token = strtok_s( con, "#", &next_token)) != NULL)
{
temp_string_pointer = NULL;
и так далее, так как это будет называть strtok_s()
один раз с указателем строки и после этого с NULL
, Не используйте temp_line для этого, так как вы хотите delete[] temp_line;
после обработки.
Вы можете думать, что это много возиться, но вот что strtok()
и родственники обычно влекут за собой.
strtok работает так:
Первый вызов возврата строки с начала unril разделитель или всю строку, если разделитель не был найден:
token = strtok_s(con, "#", &next_token);
Второй вызов с использованием NULL позволит вам продолжить анализ той же строки, чтобы найти следующий разделитель:
token = strtok_s(NULL, "#", &next_token);
Если вы достигнете конца строки, следующий вызов вернет NULL;
Всякий раз, когда у вас есть std::string
и что вам нужно, это (модифицируемый) массив символов, то std::vector<char>
это то, что вам нужно:
void f(char* buffer, std::size_t buffer_size);
void g(std::string& str)
{
std::vector<char> buffer(str.begin(),str.end());
// buffer.push_back('\0'); // use this if you need a zero-terminated string
f(&buffer[0], buffer.size()); // if you added zero-termination, consider it for the size
str.assign(buffer.begin(), buffer.end());
}
2-й вызов strtok находится внутри цикла. Он продвигает указатель токена так, что вы распечатываете токены один за другим, пока вы не распечатаете все из них, указатель станет нулевым, и вы выйдете из цикла.
Чтобы ответить на 1-ю часть вашего вопроса, как предлагали другие, c_str() дает вам только внутренний указатель буфера - вы не можете его изменить, поэтому он const. Если вы хотите изменить его, вам нужно выделить свой собственный буфер и скопировать в него содержимое строки.
Вы можете легко написать процедуру преобразования, которая будет маркировать строку и возвращать вектор подстрок:
std::vector<std::string> parse(const std::string& str, const char delimiter)
{
std::vector<std::string> r;
if(str.empty())
return r;
size_t prev = 0, curr = 0;
do
{
if(std::string::npos == (curr = str.find(delimiter, prev)))
curr = str.length();
r.push_back(str.substr(prev, curr - prev));
prev = curr + 1;
}
while(prev < (int)str.length());
return r;
}
Если вам действительно нужен доступ к внутреннему буферу строки, вот как: &*string.begin()
, Прямой доступ к строковому буферу полезен в некоторых случаях, здесь вы можете увидеть такой случай.
Я думаю, что вы можете сначала преобразовать строку в const char*, а затем скопировать const char * в буфер char * для дальнейшего использования.