1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

Sscanf()?

Discussion in 'Plugins' started by vefatica, Sep 14, 2008.

  1. vefatica

    Joined:
    May 20, 2008
    Messages:
    7,794
    Likes Received:
    29
    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.
     
  2. rconn

    rconn Administrator
    Staff Member

    Joined:
    May 14, 2008
    Messages:
    9,732
    Likes Received:
    81
    vefatica wrote:

    WAD. It's not returning 0 arguments, it's returning 1 empty string.

    Rex Conn
    JP Software
     
  3. vefatica

    Joined:
    May 20, 2008
    Messages:
    7,794
    Likes Received:
    29
    On Sun, 14 Sep 2008 12:50:38 -0500, rconn <> wrote:


    How is it that this one returns 2?

    Sscanf(L"", L"%s %s", szStr1, szStr2);
     
  4. rconn

    rconn Administrator
    Staff Member

    Joined:
    May 14, 2008
    Messages:
    9,732
    Likes Received:
    81
    vefatica wrote:

    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. vefatica

    Joined:
    May 20, 2008
    Messages:
    7,794
    Likes Received:
    29
    On Sun, 14 Sep 2008 15:23:52 -0500, rconn <> wrote:


    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?
     
  6. rconn

    rconn Administrator
    Staff Member

    Joined:
    May 14, 2008
    Messages:
    9,732
    Likes Received:
    81
    vefatica wrote:

    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.


    I have no idea what you want to do here ...

    Rex Conn
    JP Software
     
  7. vefatica

    Joined:
    May 20, 2008
    Messages:
    7,794
    Likes Received:
    29
    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).


    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
     
  8. rconn

    rconn Administrator
    Staff Member

    Joined:
    May 14, 2008
    Messages:
    9,732
    Likes Received:
    81
    vefatica wrote:

    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. vefatica

    Joined:
    May 20, 2008
    Messages:
    7,794
    Likes Received:
    29
    On Sun, 14 Sep 2008 22:06:37 -0500, rconn <> wrote:


    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?
     
  10. logic

    Joined:
    May 30, 2008
    Messages:
    42
    Likes Received:
    0
    From: vefatica
    Sent: Sunday, September 14, 2008 4:07 PM
    Subject: RE: [Plugins-t-451] Sscanf()?

    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. vefatica

    Joined:
    May 20, 2008
    Messages:
    7,794
    Likes Received:
    29
    On Mon, 15 Sep 2008 10:18:23 -0500, logic <> wrote:


    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.
     

Share This Page