Reputation: 3311
I'm trying to read a file which has input(time and price) as: 12:23:31
67 12:31:23 78 [...]
I created a struct
which holds values of hour,
minutes and seconds. I used strtok
to tokenize the individual values
and use atof
to store them. However, I'm getting an error when I try
to tokenize the time: cannot convert std::string' to 'char*' for argument 1 to 'char*'
struct time
{
int hours;
int minutes;
int seconds;
double price;
};
int main()
{
string file, input;
time* time_array;
char* tok;
cout << "Enter a file name to read input: ";
cin >> file;
ifstream file_name(file.c_str());
file_name >> input;
file_name >> input;
//while(!file_name.eof())
for(int i = 0; i < 4; i++)
{
time_array = new time;
file_name >> input;
tok = strtok(input, ":"); //ERROR HERE
while(tok != NULL)
{
*time_array.hours = atof(tok[0]);
*time_array.minutes = atof(tok[1]);
*time_array.seconds = atof(tok[2]);
}
file_name >> input;
*time_array.prine = atof(input);
}
}
Upvotes: 0
Views: 2206
Reputation: 7157
The short answer is that you cannot directly use a std::string
with strtok
, as strtok
wants a string it can modify. Even if you use c_str()
to get a C-style string from a std::string
, it is still read only.
If you really want to use strtok
, you need to duplicate the string into a modifiable buffer, for example by:
char* str = strdup(input.c_str())
;
If you do this, make sure you call free(str)
at the end of the function, else you will get a memory leak!
Upvotes: 1
Reputation: 490158
I would not use strtok
for this job at all1. If you want to use C-like tools, then read the data with fscanf:
// note there here `file_name` needs to be a FILE * instead of an ifstream.
fscanf(file_name, "%f:%f:%f %f", &hours, &minutes, &seconds, &price);
Most people writing C++ would prefer something more typesafe though. One possibility would be to use essentially the same format string to read the data using Boost.format.
Another possibility would be to use stream extractors:
char ignore1, ignore2;
file >> hours >> ignore1 >> minutes >> ignore2 >> seconds >> price;
As to what this does/how it works: each extractor reads one item from the input stream. the extractors for float
each read a number. The extractors for char
each read one character. In this case, we expect to see: 99:99:99 99
, where 9
means "a digit". So, we read a number, a colon, a number, a colon, a number and another number (the extractor skips whitespace automatically). The two colons are read into char
variables, and can either be ignored, or you can check that they really are colons to verify that the input data was in the correct format.
Here's a complete, compileable demo of that technique:
#include <iostream>
int main() {
float hours, minutes, seconds, price;
char ignore1, ignore2;
std::cin >> hours >> ignore1 >> minutes >> ignore2 >> seconds >> price;
std::cout << "H:" << hours
<< " M:" << minutes
<< " S:" << seconds
<< " P:" << price << "\n";
return 0;
}
There are certainly a lot more possibilities, but at least those are a few reasonable ones.
strtok
, but there are some where I might be at least a little tempted, or wish strtok
weren't so badly designed so I could use it. In this case, however, I don't even see much reason to use anything similar to strtok
at all.Upvotes: 4
Reputation: 370172
strtok
doesn't take a string
as its argument - it takes a char*
. Like all functions in the cstring
header it's a C function that works with C strings - not C++ strings - and should generally not be used in C++.
Use the methods of the string class instead.
Upvotes: 1
Reputation:
Your simple case can easily be built using the string::find
method. However, take a look at Boost.Tokenizer.
strtok
will not work with std::string.c_str() because it returns const char*
. strtok
does not take a string
as an argument, but rather a char*
.
Upvotes: 0