Click here to Skip to main content
15,881,852 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
See more:
Hello, I have and I have to check if its in a particular format and print. But my code is giving out some runtime error.

here's my code.This is a line from a file that has to b read:
C++
bool ParseFile (CString const& line, CV3& point) const
 1)for a file which has string like this :="618.888,201.88"

 CString searchString;
 searchString.Format(_T("%%lf%s%%lf"), lineSepChar[seperator]);
//the %s takes the value from the seperator, say comma 
if(swscanf_s(line, searchString , &point.y, &point.x) == 2 )
// the point is a vector class and has 3 variables x, y and z
return true; 
}
// this works fine 

But when for a string:
C++
bool ParseFile (CString const& line, CV3& point) const
2)for a file which has string like this :="618.888,201.88,abcdefg,hijk"

 CString searchString;
 searchString.Format("%%lf%s%%lf%s%%s%s%%s"),s_lineSepChar[m_srcSep], s_lineSepChar[m_srcSep], s_lineSepChar[m_srcSep]);

C++
 if(swscanf_s(line, searchStr, &pt.y, &pt.x, str1, 50, str2, 50 ) == 4 )
return true; 

But for this second example both str1 and str2 take the value of "abcdefg,hijk" and the return value is false ...

But if I change
C++
swscanf_s(line, searchStr, &pt.y, &pt.x, str1, 50, str2, 50 ) == 4 

to
C++
swscanf_s(line, searchStr, &pt.y, &pt.x, str1, 50, str2, 50 ) == 3 

The return value is true, its the duplicate values for str1 and str2;
Posted
Updated 31-May-12 0:45am
v6

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:
C++
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:
C++
std::istringstream input( "LHA,600,3000" );
auto first_data = read_next_beer_data( input );
Or even with:
C++
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.
 
Share this answer
 
Comments
nv3 29-May-12 16:45pm    
I agree Ash. I don't like sscanf either. Always get confused with its behavior and always there is just a tiny bit of functionality missing. Therefore, I prefer hand written tokenizers. Got my 5.
Sumal.V 31-May-12 5:13am    
That's a meaningful example, understandable code and gives awareness of drinking :)But I must use sscanf functions, as I'm adding additional functions on a existing program and I'm supposed to follow the uniformity. :(
But thank you :)
Aescleal 31-May-12 6:07am    
It's a shame that the technical lead or project manager has this attitude. If you don't use newer, safer ways of programming then you're just making it harder to maintain.

Good luck, which I really hope you don't need!
Richard MacCutchan 31-May-12 6:15am    
+4. You really deserve a 5 for this excellent sample and detailed description, but I just hate all those underscores. :)
Aescleal 31-May-12 8:06am    
Underscores are great - you can run variable names with underscores through a spell checker while you generally can't with camel case names.
I'd suggest splitting the string into a container (e.g., std::list), removing the entries you don't want from the container, and then re-joining the container.
Start here: STL split string function[^].

Hope this helps,
Pablo.
 
Share this answer
 
You have asked a similar question yesterday. Please observe that when working with swscanf_s you have to specify a buffer length for every string variable in the following parameter, e.g.:

if (swscanf_s (line, searchStr, str1, 50, str2, 50, str3, 50, doub1, chart1) == 7 )


Replace 50 with the buffer length of your str1, ... sttr3 buffers.

EDIT: Now that you have shown more of your code, I see another problem. You can't simply allocate a CString variable and use it as buffer. Instead use the following technique:

TCHAR str1 [50];
TCHAR str2 [50];
TCHAR str3 [50];
double doub1;
char chart1;
swscanf_s (line, _T("%[^,],%[^,],%[^,],%lf,%c", str1, 50, str2, 50, str3, 50, &doub1, &chart1)


That will give you the first five values on the line. If you want to assign str1 ... 3 to a CString that's fine, for example:

CString s1 = str1;


There are other techniques with CString that would make it possible to have CString allocate the buffer for you, but they are still too advanced for your programming level.
 
Share this answer
 
v2
Comments
Sumal.V 29-May-12 8:42am    
Oh sorry, I'm really not getting the hang of sscanf functions :(
Yes if I give buffer values, the code works well..
In the same program, There is an error again:-
CString searchStr, lineval;
lineval = "21:00:55.035,$GPGGA,210055.00,6102.0039902,N,$GPGGA,210054,6102.00388,N";

searchStr.Format(_T("%%s,%%s,%%s,%%lf,%%c,%%s,%%s,%%s,%%lf,%%c"));
CString str1, str2, str3;
double doub1;
char chart1;
swscanf_s (line, searchStr, str1, 50, str2, 50, str3, 50, &doub1, chart1) ;
What is wrong here? It doesn't read the values first... Thanks for ur help
nv3 29-May-12 8:55am    
First of, why do you use Format to assign a simple string?

CString searchStr (_T("%s,%s, ..."));

would have sufficed. Then you fell into the same trap as the last time: The %s format type reads everything up to the next white space, so it consumes your entire input. So either separate arguments in your input by spaces or use format types like %[^,] to read all up to the next comma. To 'eat' the comma you must place it also in your format specification, for example:

"%[^,],%[^,],%[^,]"

to read three strings that are separated by commas. Got it?
Sumal.V 29-May-12 9:05am    
Well its not a simple string . I have open a .csv file containg thousands of lines, each line of the format something like this :-

21:00:55 $GPGGA 210055 6102.00399 N 107.5920998 E 2 9 1.2 23.57 M 47.57 M 10 1011*4E $GPGGA 210054 6102.00388 N 107.62266 E 2 10 1 25.33 M 47.57 M 13 0777*76 $HEHDT 0.4 T*2B 0.67.

so I have to get the first 5 column values say and then perform calculation on 4th column - find sum and average, column wise.
Sumal.V 29-May-12 9:07am    
Again, as u said, I changed the format to :
lineval : is same
searchStr=_T("%s,%s,%s,%lf,%c,%s,%s,%s,%lf,%c");
swscanf_s (line, searchStr, str1, 50, str2, 50, str3, 50, &doub1, &chart1) ;
//none of the variables get any value...
nv3 29-May-12 9:21am    
I would have expected that at least str1 gets a value. If not, I suspect that something is wrong with your line buffer, e.g. could be that you read it as 8-bit characters while the rest of your program expects Unicode?

It might help if you use "improve question" and paste the latest version of your code, include the code that reads "line".

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900