scanf on an istream object

前端 未结 3 1988
清歌不尽
清歌不尽 2020-11-29 08:45

NOTE: I\'ve seen the post What is the cin analougus of scanf formatted input? before asking the question and the post doesn\'t solve my problem here. The post seeks for C++-

相关标签:
3条回答
  • 2020-11-29 09:20

    I do not recommend you to mix C++ input output and C input output. No that they are really incompatible but they could just plain interoperate wrong.

    For example Oracle docs recommend not to mix it http://www.oracle.com/technetwork/articles/servers-storage-dev/mixingcandcpluspluscode-305840.html

    But no one stops you from reading data into the buffer and parsing it with standard c functions like sscanf.

    ...
    string curString;
    int a, b;
    ...
    
    std::getline(inputStream, curString);
    
    int sscanfResult == sscanf(curString.cstr(), "%d:%d", &a, &b);
    
    if (2 != sscanfResult)
       throw "error";
    ...
    

    But it won't help in some situations when your stream is just one long contiguous sequence of symbols(like some string turned into memory stream).

    Making your own fscanf from scratch or porting(?) the original CRT function actually isn't the worst possible idea. Just make sure you have tested it thoroughly(low level custom char manipulation was always a source of pain in C).

    I've never really tried the boost\spirit and such parsing infrastructure could really be an overkill for your project. But boost libraries are usually well tested and designed. You could at least try to use it.

    0 讨论(0)
  • 2020-11-29 09:27

    You should never, under any circumstances, use scanf or its relatives for anything, for three reasons:

    1. Many format strings, including for instance all the simple uses of %s, are just as dangerous as gets.
    2. It is almost impossible to recover from malformed input, because scanf does not tell you how far in characters into the input it got when it hit something unexpected.
    3. Numeric overflow triggers undefined behavior: yes, that means scanf is allowed to crash the entire program if a numeric field in the input has too many digits.

    Prior to C++11, the C++ specification defined istream formatted input of numbers in terms of scanf, which means that last objection is very likely to apply to them as well! (In C++11 the specification is changed to use strto* instead and to do something predictable if that detects overflow.)

    What you should do instead is: read entire lines of input into std::string objects with getline, hand-code logic to split them up into fields (I don't remember off the top of my head what the C++-string equivalent of strsep is, but I'm sure it exists) and then convert numeric strings to machine numbers with the strtol/strtod family of functions.

    I cannot emphasize this enough: THE ONLY 100% RELIABLE WAY TO CONVERT STRINGS TO NUMBERS IN C OR C++, unless you are lucky enough to have a C++ runtime that is already C++11-conformant in this regard, IS WITH THE strto* FUNCTIONS, and you must use them correctly:

    errno = 0;
    result = strtoX(s, &ends, 10); // omit 10 for floats
    if (s == ends || *ends || errno)
      parse_error();
    

    (The OpenBSD manpages, linked above, explain why you have to do this fairly convoluted thing.)

    (If you're clever, you can use ends and some manual logic to skip that colon, instead of strsep.)

    0 讨论(0)
  • 2020-11-29 09:27

    Based on @tmyklebu's comment, I implemented streamScanf which wraps istream as FILE* via fopencookie: https://github.com/likan999/codejam/blob/master/Common/StreamScanf.cpp

    0 讨论(0)
提交回复
热议问题