Sscanf()?

#1
Sscanf(L"", L"%s", szText);
returns 1. Is it by design? If so, can it be used to advantage? The comparable swscanf() statement returns 0 and the comparable sscanf() statement returns -1.
 
#3
On Sun, 14 Sep 2008 12:50:38 -0500, rconn <> wrote:


>vefatica wrote:
>
>
>---Quote---
>> Sscanf(L"", L"%s", szText);
>>
>> returns 1. Is it by design? If so, can it be used to advantage? The
>> comparable swscanf() statement returns 0 and the comparable sscanf()
>> statement returns -1.
>---End Quote---
>WAD. It's not returning 0 arguments, it's returning 1 empty string.
How is it that this one returns 2?

Sscanf(L"", L"%s %s", szStr1, szStr2);
 

rconn

Administrator
Staff member
May 14, 2008
10,614
97
#4
vefatica wrote:

> On Sun, 14 Sep 2008 12:50:38 -0500, rconn <> wrote:
>
>
> Quote:
> >vefatica wrote:
> >
> >
> >---Quote---
> >> Sscanf(L"", L"%s", szText);
> >>
> >> returns 1. Is it by design? If so, can it be used to advantage? The
> >> comparable swscanf() statement returns 0 and the comparable sscanf()
> >> statement returns -1.
> >---End Quote---
> >WAD. It's not returning 0 arguments, it's returning 1 empty string.
>
> How is it that this one returns 2?
>
> Sscanf(L"", L"%s %s", szStr1, szStr2);
Because it returns *two* empty strings.

If you want the swscanf behavior, use that instead. I wrote Sscanf
because swscanf and sscanf didn't meet my particular needs. (I wanted
the strings to always be initialized, which swscanf and sscanf wouldn't do.)

Rex Conn
JP Software
 
#5
On Sun, 14 Sep 2008 15:23:52 -0500, rconn <> wrote:


>> How is it that this one returns 2?
>>
>> Sscanf(L"", L"%s %s", szStr1, szStr2);
>---End Quote---
>Because it returns *two* empty strings.
>
>If you want the swscanf behavior, use that instead. I wrote Sscanf
>because swscanf and sscanf didn't meet my particular needs. (I wanted
>the strings to always be initialized, which swscanf and sscanf wouldn't do.)
The only clue I had as to how Sscanf() works was TakeCmd.h's

"Like the RTL sscanf()"

Sscanf(L"", L"%d %s", &int1, szStr1) returns 1.

Sscanf(L"", L"%d,%s", &int1, szStr1) returns 0.

Are you saying the string will be initialized if Sscanf() reaches the point
where it's looking for something to put in it?

I want to parse an option like

/X[=n[,str]]

which I've isolated. If "/X" is found, how would you proceed?
 

rconn

Administrator
Staff member
May 14, 2008
10,614
97
#6
vefatica wrote:

> On Sun, 14 Sep 2008 15:23:52 -0500, rconn <> wrote:
>
>
> Quote:
> >> How is it that this one returns 2?
> >>
> >> Sscanf(L"", L"%s %s", szStr1, szStr2);
> >---End Quote---
> >Because it returns *two* empty strings.
> >
> >If you want the swscanf behavior, use that instead. I wrote Sscanf
> >because swscanf and sscanf didn't meet my particular needs. (I wanted
> >the strings to always be initialized, which swscanf and sscanf
> wouldn't do.)
>
> The only clue I had as to how Sscanf() works was TakeCmd.h's
If it was just like sscanf, there wouldn't be any reason for Sscanf to
exist. If you want to do something using the identical sscanf behavior,
use sscanf.

Sscanf behavior is intricately tied to the TCC / TCMD code; it will
*never* change.


> Are you saying the string will be initialized if Sscanf() reaches the point
> where it's looking for something to put in it?
>
> I want to parse an option like
>
> /X[=n[,str]]
>
> which I've isolated. If "/X" is found, how would you proceed?
I have no idea what you want to do here ...

Rex Conn
JP Software
 
#7
On Sun, 14 Sep 2008 20:07:12 -0500, rconn <> wrote:

If you use Sscanf() when parsing command line parameters then I should be able
to make it work in a plugin. I try to use your SDK functions rather than drag
in huge things from the CRT (swscanf() is 10KB).


>I have no idea what you want to do here ...
Suppose you had a command with syntax

COMMAND [/X[=n[,str]]] ...

an INT where n should go, a WCHAR[] where str should go, and default values for
both (to be used if values aren't spec'd). Suppose you've isolated a command
line argument (say with NthArgument) and you want to see if it's the "/X" option
and if it is, do all the right things. What would you recommend? I can always
stand to learn from an expert.

If you don't like my example, how about something like

/SIZE=rows,columns
 

rconn

Administrator
Staff member
May 14, 2008
10,614
97
#8
vefatica wrote:

> On Sun, 14 Sep 2008 20:07:12 -0500, rconn <> wrote:
> Quote:
> >I have no idea what you want to do here ...
>
> Suppose you had a command with syntax
>
> COMMAND [/X[=n[,str]]] ...
>
> an INT where n should go, a WCHAR[] where str should go, and default
> values for
> both (to be used if values aren't spec'd). Suppose you've isolated a command
> line argument (say with NthArgument) and you want to see if it's the
> "/X" option
> and if it is, do all the right things. What would you recommend? I can
> always
> stand to learn from an expert.
If you've extracted the argument with NthArgument, you won't even see
the ",str" part because the "," will be treated as a delimiter. You'd
either have to parse the line an argument at a time (saving the previous
state) or use something like :

Sscanf( line, L"=%d,%s", &n, str );

The return value in this case will be irrelevant -- either "str" has a
value, or it doesn't.

However, in this instance I wouldn't use Sscanf at all; I'd parse the
line character-by-character. None of the sscanf variants behave well
when you have optional arguments (especially if you have optional
arguments in the middle of the line).

Rex Conn
JP Software
 
#9
On Sun, 14 Sep 2008 22:06:37 -0500, rconn <> wrote:


>vefatica wrote:
>
>
>---Quote---
>> On Sun, 14 Sep 2008 20:07:12 -0500, rconn <> wrote:
>> Quote:
>> >I have no idea what you want to do here ...
>>
>> Suppose you had a command with syntax
>>
>> COMMAND [/X[=n[,str]]] ...
>>
>> an INT where n should go, a WCHAR[] where str should go, and default
>> values for
>> both (to be used if values aren't spec'd). Suppose you've isolated a command
>> line argument (say with NthArgument) and you want to see if it's the
>> "/X" option
>> and if it is, do all the right things. What would you recommend? I can
>> always
>> stand to learn from an expert.
>---End Quote---
>If you've extracted the argument with NthArgument, you won't even see
>the ",str" part because the "," will be treated as a delimiter. You'd
>either have to parse the line an argument at a time (saving the previous
>state) or use something like :
>
> Sscanf( line, L"=%d,%s", &n, str );
I'm far from expert at NthArgument(). I always use 0x8800 to make it similar to
argv[] (and CommandLineToArgvW()). I'm basically doing what you suggested
above. Is it true in your Sscanf() example above that if "=" is found, n will
be initialized to 0 and if "=" isn't found, n won't be initialized?
 
May 30, 2008
42
0
#10
From: vefatica
Sent: Sunday, September 14, 2008 4:07 PM
Subject: RE: [Plugins-t-451] Sscanf()?

>
> I want to parse an option like
>
> /X[=n[,str]]
>
> which I've isolated. If "/X" is found, how would you proceed?
For what it's worth, my approach would be:

namespace ParseResult
{
enum Type
{
NotMatched,
MatchedSimple,
MatchedN,
MatchedNAndStr,
};
}

ParseResult::Type parse_option_X(
const string &arg, // e.g. /X=3,Hello
int &n, // gets 3
string &str) // gets "Hello"
{
string::size_type equals = arg.find('=');

if (arg.substr(0, equals) != "/X")
return ParseResult::NotMatched;

if (equals == string::npos)
return ParseResult::MatchedSimple;

arg = arg.substr(equals + 1);

string::size_type comma = arg.find(',');

if (comma == string::npos)
{
n = atoi(arg.c_str());
str = "";
return ParseResult::MatchedN;
}
else
{
n = atoi(arg.substr(0, comma).c_str());
str = arg.substr(comma + 1);
return ParseResult::MatchedNAndStr;
}
}

..or if you prefer not to use STL std::string:

ParseResult::Type parse_option_X(
const char *arg, // e.g. /X=3,Hello
int &n, // gets 3
char *&str) // gets "Hello"
{
const char *equals = strchr(arg, '=');

if (equals == NULL)
return (strcmp(arg, "/X") == 0)
? ParseResult::MatchedSimple
: ParseResult::NotMatched;
else if (((equals - arg) != 2)
|| (memcmp(arg, "/X", 2) != 0))
return ParseResult::NotMatched;

arg = equals + 1;

const char *comma = strchr(arg, ',');

if (comma == NULL)
{
n = atoi(arg);
str = NULL;
return ParseResult::MatchedN;
}
else
{
// NB: If you're not worried about locales where ',' is a decimal
// separator, you can just pass arg into atoi() directly; it'll stop
// at the first non-numeric character it sees.

vector<char> tmp_buf;

tmp_buf.resize((int)(comma - arg + 1));

memcpy(&tmp_buf[0], arg, (int)(comma - arg));

n = atoi(&tmp_buf[0]);
str = strdup(comma + 1); // or replace with your favorite allocator
return ParseResult::MatchedNAndStr;
}
}

I prefer to stay in full control :-) I find parsing by hand to be more
flexible and more powerful, except of course for using parser-generator
tools. For larger, more complicated parsing jobs I often write a tokenizer
so that I can do the parsing based on tokens. This is a small enough parser
that just doing it this way is cheap enough.

Jonathan Gilbert
 
#11
On Mon, 15 Sep 2008 10:18:23 -0500, logic <> wrote:


>---Quote---
>>
>> I want to parse an option like
>>
>> /X[=n[,str]]
>>
>> which I've isolated. If "/X" is found, how would you proceed?
>---End Quote---
>For what it's worth, my approach would be:
That'd require a different parse_option_* function for each option. All that
needs to be different is the way of handling the "data" which may follow the
option (a Sscanf() statement perhaps).

I see if the command line token represents a valid option:

UINT WhichOption(WCHAR *p)
{
WCHAR *pOpt[nOptions] = {..., L"FO", ...};
UINT i;
for ( i=0; i<nOptions; i++ )
if ( !wcsnicmp(p, pOpt, lstrlen(pOpt)) )
return i;
return 0;
}

If the option is valid, I get a pointer to its data (after the "=" if present,
terminating NUL otherwise) and parse the data with an appropriate Sscanf()
statement:

for ( i=1; i<argc; i++ )
{
if ( argv[0] != L'/' )
break;

WCHAR *pData = wcschr(argv, L'=');
if ( !pData ) pData = argv + lstrlen(argv);
else pData += 1;

switch ( WhichOption(1+argv) )
{
// ...
case E_FONT : // 5
if ( !Sscanf(pData, L"%u,%s", &fontsize, szFontName) )
fontsize = DEFAULT_FONT_SIZE;
if ( !szFontName[0] )
lstrcpy(szFontName, DEFAULT_FONT_NAME);
break;
// ...

It works well, having figured out when Sscanf() initializes (zeroes) its target
locations.