简体   繁体   English

使用 scanf 读入 std::string

[英]Read into std::string using scanf

As the title said, I'm curious if there is a way to read a C++ string with scanf.正如标题所说,我很好奇是否有办法使用 scanf 读取 C++ 字符串。

I know that I can read each char and insert it in the deserved string, but I'd want something like:我知道我可以读取每个字符并将其插入应得的字符串中,但我想要类似的东西:

string a;
scanf("%SOMETHING", &a);

gets() also doesn't work. gets()也不起作用。

Thanks in advance!提前致谢!

this can work这可以工作

char tmp[101];
scanf("%100s", tmp);
string a = tmp;

There is no situation under which gets() is to be used!不存在使用gets()情况! It is always wrong to use gets() and it is removed from C11 and being removed from C++14.使用gets()总是错误的,它从C11 中删除并从C++14 中删除。

scanf() doens't support any C++ classes. scanf()不支持任何 C++ 类。 However, you can store the result from scanf() into a std::string :但是,您可以将scanf()的结果存储到std::string

Editor's note: The following code is wrong, as explained in the comments .编者注:以下代码有误,如注释中所述。 See the answers by Patato , tom , and Daniel Trugman for correct approaches.有关正确方法,请参阅PatatotomDaniel Trugman的答案。

std::string str(100, ' ');
if (1 == scanf("%*s", &str[0], str.size())) {
    // ...
}

I'm not entirely sure about the way to specify that buffer length in scanf() and in which order the parameters go (there is a chance that the parameters &str[0] and str.size() need to be reversed and I may be missing a . in the format string).我不完全确定在scanf()指定缓冲区长度的方式以及参数的顺序(参数&str[0]str.size()有可能需要反转,我可能在格式字符串中缺少. )。 Note that the resulting std::string will contain a terminating null character and it won't have changed its size.请注意,生成的std::string将包含一个终止空字符,并且不会更改其大小。

Of course, I would just use if (std::cin >> str) { ... } but that's a different question.当然,我只会使用if (std::cin >> str) { ... }但这是一个不同的问题。

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.您可以使用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;

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.对象std::string不知道未通过导出的官方 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:如果我们将原始调用: 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;

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.调用resize以确保您的缓冲区足够大。 Use a #define for the maximal length (see step 2 to understand why):对最大长度使用#define (请参阅第 2 步了解原因):
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):使用scanf同时使用“宽度修饰符”限制扫描字符串的大小并检查返回值(返回值是扫描的标记数):
#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));

You can construct an std::string of an appropriate size and read into its underlying character storage:您可以构造一个适当大小的 std::string 并读入其底层字符存储:

std::string str(100, ' ');
scanf("%100s", &str[0]);
str.resize(strlen(str.c_str()));

The call to str.resize() is critical, otherwise the length of the std::string object will not be updated.对 str.resize() 的调用至关重要,否则 std::string 对象的长度将不会更新。 Thanks to Daniel Trugman for pointing this out.感谢Daniel Trugman指出这一点。

(There is no off-by-one error with the size reserved for the string versus the width passed to scanf , because since C++11 it is guaranteed that the character data of std::string is followed by a null terminator so there is room for size+1 characters.) (为字符串保留的大小与传递给scanf的宽度没有逐一错误,因为从 C++11 开始,保证 std::string 的字符数据后跟一个空终止符,所以有是 size+1 个字符的空间。)

The below snippet works下面的片段有效

string s(100, '\0');
scanf("%s", s.c_str());

Here a version without limit of length (in case of the length of the input is unknown).这里是一个没有长度限制的版本(在输入长度未知的情况下)。

std::string read_string() {
  std::string s; unsigned int uc; int c;
  // ASCII code of space is 32, and all code less or equal than 32 are invisible.
  // For EOF, a negative, will be large than 32 after unsigned conversion
  while ((uc = (unsigned int)getchar()) <= 32u);
  if (uc < 256u) s.push_back((char)uc);
  while ((c = getchar()) > 32) s.push_back((char)c);
  return s;
}

For performance consideration, getchar is definitely faster than scanf , and std::string::reserve could pre-allocate buffers to prevent frequent reallocation.出于性能考虑, getchar肯定比scanf快,并且std::string::reserve可以预先分配缓冲区以防止频繁重新分配。

int n=15; // you are going to scan no more than n symbols
std::string str(n+1); //you can't scan more than string contains minus 1
scanf("%s",str.begin()); // scanf only changes content of string like it's array
str=str.c_str() //make string normal, you'll have lots of problems without this string
string temp;
scanf("%s",&temp.c_str());

i hope it'll work! 我希望它能工作!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM