问题说明:
您可以使用scanf 填充std::string 的底层缓冲区,但是(!)托管的std::string 对象将不知道更改。
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;
输出:
Managed string: (size = 0)
Underlying buffer: Daniel (size = 6)
那么,这里发生了什么?
std::string 对象不知道未通过导出的官方 API 执行的更改。
当我们通过底层缓冲区写入对象时,数据发生了变化,但字符串对象并没有意识到这一点。
如果我们将原始调用:token.reseve(64) 替换为 token.resize(64)(更改托管字符串大小的调用),结果会有所不同:
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;
输出:
Managed string: Daniel (size = 64)
Underlying buffer: Daniel (size = 6)
再一次,结果是次优的。输出正确,但大小不正确。
解决方案:
如果你真的想这样做,请按照以下步骤操作:
- 致电
resize 以确保您的缓冲区足够大。使用#define 作为最大长度(请参阅第 2 步了解原因):
std::string buffer;
buffer.resize(MAX_TOKEN_LENGTH);
- 使用
scanf,同时使用“宽度修饰符”限制扫描字符串的大小并检查返回值(返回值是扫描的令牌数):
#define XSTR(__x) STR(__x)
#define STR(__x) #x
...
int rv = scanf("%" XSTR(MAX_TOKEN_LENGTH) "s", &buffer[0]);
- 以安全的方式将托管字符串大小重置为实际大小:
buffer.resize(strnlen(buffer.data(), MAX_TOKEN_LENGTH));