Read C++ string with scanf

后端 未结 7 1102
离开以前
离开以前 2020-11-28 12:35

As the title said, I\'m curious if there is a way to read a C++ string with scanf.

I know that I can read each char and insert it in the deserved string, but I\'d wa

7条回答
  •  余生分开走
    2020-11-28 13:06

    After helping a friend who got here, I realized the answers here are not good enough.

    Problem explained:

    You CAN populate the underlying buffer of an std::string using scanf, but(!) the managed std::string object will NOT be aware of the change.

    const char *line="Daniel 1337"; // The line we're gonna parse
    
    std::string token;
    token.reserve(64); // You should always make sure the buffer is big enough
    
    sscanf(line, "%s %*u", token.data());
    std::cout << "Managed string: '" << token
              << " (size = " << token.size() << ")" << std::endl;
    std::cout << "Underlying buffer: " << token.data()
              << " (size = " << strlen(token.data()) << ")" << std::endl;
    

    Outputs:

    Managed string:  (size = 0)
    Underlying buffer: Daniel (size = 6)
    

    So, what happened here? The object std::string is not aware of changes not performed through the exported, official, API.

    When we write to the object through the underlying buffer, the data changes, but the string object is not aware of that.

    If we were to replace the original call: token.reseve(64) with token.resize(64), a call that changes the size of the managed string, the results would've been different:

    const char *line="Daniel 1337"; // The line we're gonna parse
    
    std::string token;
    token.resize(64); // You should always make sure the buffer is big enough
    
    sscanf(line, "%s %*u", token.data());
    std::cout << "Managed string: " << token
              << " (size = " << token.size() << ")" << std::endl;
    std::cout << "Underlying buffer: " << token.data()
              << " (size = " << strlen(token.data()) << ")" << std::endl;
    

    Outputs:

    Managed string: Daniel (size = 64)
    Underlying buffer: Daniel (size = 6)
    

    Once again, the result is sub-optimal. The output is correct, but the size isn't.

    Solution:

    If you really want to make do this, follow these steps:

    1. Call resize to make sure your buffer is big enough. Use a #define for the maximal length (see step 2 to understand why):
    std::string buffer;
    buffer.resize(MAX_TOKEN_LENGTH);
    
    1. Use scanf while limiting the size of the scanned string using "width modifiers" and check the return value (return value is the number of tokens scanned):
    #define XSTR(__x) STR(__x)
    #define STR(__x) #x
    ...
    int rv = scanf("%" XSTR(MAX_TOKEN_LENGTH) "s", &buffer[0]);
    
    1. Reset the managed string size to the actual size in a safe manner:
    buffer.resize(strnlen(buffer.data(), MAX_TOKEN_LENGTH));
    

提交回复
热议问题