У меня есть функция, которая разбивает строку на различные секции и затем анализирует их, но при преобразовании строки в char * я получаю неправильный вывод.
int parseJob(char * buffer)
{ // Parse raw data, should return individual jobs
const char* p;
int rows = 0;
for (p = strtok( buffer, "~" ); p; p = strtok( NULL, "~" )) {
string jobR(p);
char* job = &jobR[0];
parseJobParameters(job); // At this point, the data is still in good condition
}
return (1);
}
int parseJobParameters(char * buffer)
{ // Parse raw data, should return individual job parameters
const char* p;
int rows = 0;
for (p = strtok( buffer, "|" ); p; p = strtok( NULL, "|" )) { cout<<p; } // At this point, the data is malformed.
return (1);
}
Я не знаю, что происходит между первой функцией, вызывающей вторую, но она искажает данные.
Как видно из приведенного примера кода, используется тот же метод для преобразования строки в char *, и он отлично работает.
Я использую Visual Studio 2012 / C ++, любые рекомендации и примеры кода будут с благодарностью.
«Физическая» причина, по которой ваш код не работает, не имеет ничего общего с std::string
или C ++. Это не сработает и в чистом C. strtok
это функция, которая хранит свое промежуточное состояние анализа в некоторой глобальной переменной. Это сразу означает, что вы не можете использовать strtok
анализировать более одной строки за раз. Запуск второго сеанса синтаксического анализа перед завершением первого приведет к переопределению внутренних данных, хранящихся в первом сеансе синтаксического анализа, и, следовательно, уничтожит их без возможности восстановления. Другими словами, strtok
разбирать сессии не должно пересекаться. В вашем коде они перекрываются.
Также в C ++ 03 появилась идея использования std::string
с strtok
напрямую обречен с самого начала. Внутренняя последовательность хранится в std::string
не гарантируется, что будет нулевым. Это означает, что в целом &jobR[0]
не является C-строкой. Это не может быть использовано с strtok
, Чтобы преобразовать std::string
к C-струне вы должны использовать c_str()
, Но C-строка возвращается c_str()
не модифицируется.
В C ++ 11 нулевое завершение должно быть видно через []
оператор, но все же, кажется, нет необходимости хранить объект терминатора смежно с фактической строкой, поэтому &jobR[0]
все еще не является C-строкой даже в C ++ 11. C-строка возвращается c_str()
или же data()
не модифицируется.
Вы не можете использовать strtok()
анализировать несколько строк одновременно, как вы делаете. Первый звонок parseJobParameters()
в первой итерации цикла parseJob()
изменит внутренний буфер, который strtok()
указывает на, таким образом, итерация второго цикла parseJob()
больше не будет обрабатывать исходные данные. Вы должны переписать свой код, чтобы не использовать вложенные вызовы strtok()
больше, например:
#include <vector>
#include <string>
void split(std::string s, const char *delims, std::vector &vec)
{
// alternatively, use s.find_first_of() and s.substr() instead...
for (const char* p = strtok(s.c_str(), delims); p != NULL; p = strtok(NULL, delims))
{
vec.push_back(p);
}
}
int parseJob(char * buffer)
{
std::vector<std::string> jobs;
split(buffer, "~", jobs);
for (std::vector<std::string>::iterator i = jobs.begin(); i != jobs.end(); ++i)
{
parseJobParameters(i->c_str());
}
return (1);
}
int parseJobParameters(char * buffer)
{
std::vector<std::string> params;
split(buffer, "|", params);
for (std::vector<std::string>::iterator i = params.begin(); i != params.end(); ++i)
{
std::cout << *i;
}
return (1);
}
Хотя это даст вам адрес первого символа в строке char* job = &jobR[0];
, это не дает вам правильную строку в стиле C. Вы ДОЛЖЕН использование char* job = jobR.c_str();
Я вполне уверен, что это решит вашу проблему, но, конечно, может быть что-то не так с тем, как вы читаете buffer
это передается parseJob
в том числе.
Редактировать: конечно, вы также вызываете strtok из функции, которая использует strtok. Внутри strtok выглядит примерно так:
char *strtok(char *str, char *separators)
{
static char *last;
char *found = NULL;
if (!str) str = last;
... do searching for needle, set found to beginning of non-separators ...
if (found)
{
*str = 0; // mark end of string.
}
last = str;
return found;
}
Поскольку «последний» перезаписывается при вызове parseParameters
, вы не можете использовать strtok(NULL, ... )
когда вы вернетесь к parseJobs