Your life would be so much easier if you forgot that
scanf
and all it's beastly children every existed. C++ has a far better way of reading formatted data - it's called a stream. Streams have extraction operations defined for them so you don't have to fanny about managing character buffers and god knows what else. So when you want to read a
string
, to you read a
string
. When you want to read an
int
, you read an
int
. And so on...
If you've got a CSV file that you want to parse here's a general way of reading and tokenising it:
- read a line from the file (using
std::getline
) into a
std::string
- replace all the commas in the line using (
std::replace
)
- build a
std::istringstream
from the line you've just done the replacement on
- extract the values you want from the
std::istringstream
So... if you've got a CSV in the following format:
- Airport code (three letter string)
- Price of a pint of beer (integer, number of pennies it costs to buy)
- Number of casualties caused per year when people see the price of a pint
Here's Ash's quick and dirty airport beer parser:
struct airport_beer_data
{
airport_beer_data():
price_per_pint_in_pennies_( 0 ),
heart_attacks_per_year_( 0 ) {}
std::string airport_code_;
int price_per_pint_in_pennies_;
int heart_attacks_per_year_;
};
airport_beer_data read_next_beer_data( std::istream &str )
{
airport_beer_data beer_data;
std::string line;
if( str )
{
if( std::getline( str, line ) && !line.empty() )
{
std::replace( begin( line ), end( line ), ',', ' ' );
std::istringstream line_parser( line );
line_parser >> beer_data.airport_code_
>> beer_data.price_per_pint_in_pennies_
>> beer_data.heart_attacks_per_year_;
}
}
return beer_data;
}
No manual sizing of buffers, no fiddling about with format strings, woo hoo!
You can test drive it using something like:
std::istringstream input( "LHA,600,3000" );
auto first_data = read_next_beer_data( input );
Or even with:
auto first_data = read_next_beer_data( std::cin );
if you want to enter test data directly from the command line.
For your problem the only line you'd have to change is the one that does the extraction (starts with
line_parser >>
), which is a lot easier than fiddling around with
scanf
. There's very little error checking but that's not hard to stick in (honest, just check the state of line_parser after the extractions).
So just say no to
scanf
and its mates, unless you're programming in C.