基于 C++ 流的 I/O 库允许您执行 I/O 操作,而不必知道有关目标或源的详细信息。流的目标或源可以是字符串、文件、内存缓冲区等等。
标准库提供的流类组织在一个层次结构和一组头中,如图 5-1 所示。
图 5-1。
The hierarchy of stream-related classes
更准确地说,该库定义了名为basic_ios、basic_ostream、basic_istringstream等的模板,所有模板都基于一种字符类型。层次结构中的所有类,除了ios_base,都是这些模板化类的typedef,模板类型为char。比如std::ostream就是std::basic_ostream<char>的一个typedef。对于称为wios、wostream、wofstream等的wchar_t字符类型,有对应的typedef。本章剩余部分仅使用图 5-1 中所示的char typedef s。
除了图中的表头,还有<iostream>。有点令人困惑的是,这并没有真正定义std::iostream本身,因为这是由<istream>完成的。相反,<iostream>包括<ios>、<streambuf>、<istream>、<ostream>和<iosfwd>,同时自身增加了标准输入和输出流(w ) cin、(w ) cout、(w ) cerr、(w ) clog。后两个分别用于输出错误和日志信息。它们的目的地是特定于实现的。
该库还提供了std::basic_streambuf、basic_filebuf和basic_stringbuf模板及其各种typedef,加上istreambuf_iterator和ostreambuf_iterator。这些是流缓冲区,是其他流类实现的基础,比如ostream、ifstream等等。在这一章的结尾会对它们进行简要的讨论。
头文件<iosfwd>包含所有标准 I/O 库类型的前向声明。将它包含在其他头文件中是很有用的,而不必包含您需要的所有类型的完整模板定义。
以下助手类型在<ios>中定义:
在<ios>中定义的ios_base类是所有输入和输出流类的基类。它跟踪格式化选项和标志,以操纵数据的读写方式。提供了以下方法:
还可以通过流 I/O 操纵器来修改标志,这将在下一节中讨论。
表 5-2。
std::ios_base::fmtflags Masks Defined in <ios>
表 5-1。
std::ios_base::fmtflags Formatting Flags Defined in <ios>
操纵器允许你使用operator<<和operator>>而不是flags(fmtflags)或setf()来改变旗帜。
<ios>头为表 5-1 : std::scientific、std::left等中定义的所有标志定义了全局std范围内的 I/O 操纵器。对于属于表 5-2 中定义的掩码的标志,I/O 操纵器使用该掩码。比如std::dec其实叫ios_base::setf(dec, basefield)。
对于boolalpha、showbase、showpoint、showpos、skipws、uppercase、unitbuf,也可以使用负面操纵器,它们的名称相同,但以no为前缀:例如std::noboolalpha。
除了std::fixed和scientific之外,还有std::hexfloat ( scientific | fixed)和std::defaultfloat(无floatfield标志设置)机械手。
此外,<iomanip>标题定义了以下操纵器:
这段代码还需要<locale>:
在美国系统上,输出如下:
Left: $1.23__
Right: __$1.23
Internal: 0x___7b
在<ios>中定义的ios类继承自ios_base,并提供了许多检查和修改流状态的方法,它是表 5-3 中列出的状态标志的按位组合。
表 5-3。
std::ios_base::iostate State Constants Defined in <ios>
提供了以下与状态相关的方法:
| 方法 | 描述 | | --- | --- | | `good()``eof()``bad()`T3】 | 如果分别没有设置`badbit`、`failbit`和`eofbit`,设置了`eofbit`,设置了`badbit`,或者设置了`failbit`或`badbit`,则返回`true`。 | | `operator!` | 相当于`fail()`。 | | `operator bool` | 相当于`!fail()`。 | | `rdstate()` | 返回当前的`ios_base::iostate`状态。 | | `clear(state)` | 如果附加了有效的流缓冲区,则将流的状态更改为给定的状态(见下文);否则将其设置为`state | badbit`。 | | `setstate(state)` | 调用`clear(state | rdstate())`。 |除了这些与状态相关的方法之外,以下附加方法由ios定义:
std::ios的默认初始化有以下效果:
- 标志被设置为
skipws | dec。 - 精度设置为 6。
- 字段宽度设置为 0。
- 填充字符被设置为
widen(' ')。 - 如果附加了有效的流缓冲区(见下文),则将状态设置为
goodbit,否则设置为badbit。
默认情况下,流操作通过设置流的状态位(good、bad、fail和eof)来报告错误,但它们不会抛出异常。不过,可以使用exceptions()方法来启用异常。它要么返回当前异常掩码,要么接受一个。该掩码是std::ios_base::iostate状态标志的按位组合(见表 5-3 )。对于掩码中设置为 1 的每个状态标志,当该状态位为流设置时,流将引发异常。
例如,下面的代码试图使用文件流打开一个不存在的文件(将在本章后面详细解释)。不会引发任何异常;只有流的失败位被设置为 1:
如果您想使用异常,代码可以重写如下:
一个可能的输出可能是
ios_base::failbit set: iostream stream error
ostream类支持对基于char的流进行格式化和非格式化输出。格式化输出意味着所写内容的格式会受到格式化选项的影响,例如字段的宽度、浮点数的十进制位数等等。格式化输出通常也会受到流的locale的影响,如第 6 章所述。无格式输出只需要按原样写入字符或字符缓冲区。
ostream提供了一个swap()方法和下面的高级输出操作。如果没有提到返回类型,操作返回一个ostream&,允许操作被链接:
表 5-4。
std::ios_base::seekdir Constants Defined in <ios>
<ostream>还定义了以下额外的 I/O 操纵器:
<iostream>头提供了以下全局ostream实例:
cout/wcout:输出到标准 C 输出流stdoutcerr/wcerr:标准 C 错误流的输出,stderrclog/wclog:标准 C 错误流的输出,stderr
(w)cout自动绑定到(w)cin。这意味着对(w)cin的输入操作导致(w)cout刷新其缓冲区。(w)cout也自动绑定到(w)cerr,因此(w)cerr上的任何输出操作都会导致(w)cout刷新。
std::ios_base提供了一个名为sync_with_stdio()的静态方法,用于在每次输出操作后将这些全局ostream与底层 C 流同步。这确保了它们使用相同的缓冲区,允许您安全地混合 C++ 和 C 风格的输出。它还保证了标准流是线程安全的:也就是说,没有数据竞争。尽管如此,字符交错仍然是可能的。
Note
当使用标准流cout、cerr、clog和cin(稍后讨论)时,您不必考虑与平台相关的行尾字符。例如,在 Windows 上,一行通常以\r\n结尾,而在 Linux 上以\n结尾。然而,翻译会自动发生,所以您可以总是使用\n。
以下示例演示了三种不同的输出方法:
std::cout << "PI = " << 3.1415 << std::endl;
std::cout.put('\t');
std::cout.write("C++", 3);
istream类支持来自基于char的流的格式化和非格式化输入。它提供swap()及以下高级输入操作。除非另有说明,否则操作返回一个istream&,这有助于链接:
<istream>还定义了以下额外的 I/O 操纵器:
<iostream>头提供了以下全局istream实例:
cin/wcin:从标准 C 输入流中读取,stdin
ios_base::sync_with_stdio()功能也会影响(w)cin。参见前面对cout、cerr、clog的解释。
如前所述,istream提供了一个getline()方法来提取字符。不幸的是,你必须传递给它一个适当大小的char*缓冲区。<string>头定义了一个更容易使用的std::getline()方法,它接受一个std::string作为目标缓冲区。下面的例子说明了它的用法。
int anInt;
double aDouble;
std::cout << "Enter an integer followed by some whitespace\n"
<< "and a double, and press enter: ";
std::cin >> anInt >> aDouble;
std::cout << "You entered: ";
std::cout << "Integer = " << anInt << ", Double = " << aDouble << std::endl;
std::string message;
std::cout << "Enter a string. End input with a * and enter: ";
std::getline(std::cin >> std::ws, message, '*');
std::cout << "You entered: '" << message << "'" << std::endl;
下面是该程序的一个可能输出:
Enter an integer followed by some whitespace
and a double, and press enter: 1 3.2 ↩
You entered: Integer = 1, Double = 3.2
Enter a string. End input with a * and enter: This is ↩
a multiline test* ↩
You entered: 'This is ↩
a multiline test'
iostream类,在<istream>中定义(不在<iostream>中!),继承自ostream和istream,提供高级输入输出操作。它跟踪流中的两个独立位置:一个输入位置和一个输出位置。这就是为什么ostream有tellp()和seekp()方法,而istream有tellg()和seekg() : iostream包含所有四个方法,所以它们需要一个不同的名字。除了继承的功能之外,它不提供额外的功能。
字符串流允许您在字符串上使用流 I/O。该库提供了istringstream(输入,继承自istream)、ostringstream(输出,继承自ostream)、stringstream(输入输出,继承自iostream)。继承图见图 5-1 。这三个类都有一组相似的构造函数:
-
[i|o]stringstream(ios_base::openmode): Constructs a new string stream with the givenopenmode, a bitwise combination of the flags defined in Table 5-5表 5-5。
| 开放模式 | 描述 | | --- | --- | | `app` | 追加的简称。在每次写入前查找到流的末尾。 | | `binary` | 以二进制模式打开的流。如果未指定,则以文本模式打开流。差异参见文件流部分。 | | `in / out` | 分别为读/写而打开的流。 | | `trunc` | 打开流后移除流的内容。 | | `ate` | 打开流后查找到流的末尾。 |std::ios_base::openmodeConstants Defined in<ios> -
[i|o]stringstream(string&, ios_base::openmode):用给定字符串的副本作为初始流内容,用给定的openmode构造一个新的字符串流 -
[i|o]stringstream([i|o]stringstream&&):移动构造器
前两个构造函数中的openmode有一个默认值:out代表ostringstream,in代表istringstream,out|in代表stringstream。对于ostringstream和istringstream,给定的openmode总是和默认的组合在一起;比如对于ostringstream,实际的openmode是给定 _openmode |ios_base::out。
这三个类只添加了两个方法:
string str():返回底层字符串对象的副本void str(string&):将底层字符串对象设置为给定对象的副本
std::ostringstream oss;
oss << 123 << " " << 3.1415;
std::string myString = oss.str();
std::cout << "ostringstream contains: '" << myString << "'" << std::endl;
std::istringstream iss(myString);
int myInt; double myDouble;
iss >> myInt >> myDouble;
std::cout << "int = " << myInt << ", double = " << myDouble << std::endl;
文件流允许您对文件使用流 I/O。该库提供了一个ifstream(输入,继承自istream)、ofstream(输出,继承自ostream)、fstream(输入输出,继承自iostream)。继承图见图 5-1 。这三个类都有一组相似的构造函数:
[i|o]fstream(filename, ios_base::openmode):构造一个文件流,用给定的openmode打开给定的文件。文件可以指定为const char*或std::string&。[i|o]fstream([i|o]fstream&&):移动构造器。
这三个类都添加了以下方法:
open(filename, ios_base::openmode):打开一个类似于第一个构造函数的文件is_open():如果打开文件进行输入和/或输出,则返回trueclose():关闭当前打开的文件
构造函数和open()方法中的openmode(见表 5-5 )有一个默认:ofstream用out,ifstream用in,fstream用out|in。对于ofstream和ifstream,给定的openmode总是和默认的组合在一起;例如:对于ofstream,实际openmode是给定 _openmode |ios_base::out。
如果指定了ios_base::in标志,无论是否与ios_base::out结合,您试图打开的文件必须已经存在。以下代码打开一个用于输入和输出的文件,如果该文件尚不存在,则创建该文件:
如果一个文件以文本模式打开,而不是二进制模式,库被允许翻译某些特殊字符来匹配平台如何使用这些字符。例如,在 Windows 上,行通常以\r\n结尾,而在 Linux 上,它们通常以\n结尾。当一个文件在文本模式下打开时,你并不是自己在 Windows 上读/写\r;库为您处理这种翻译。
与其他组合的输入和输出流(如stringstream)相比,fstream类支持输入和输出,处理当前位置的方式不同。文件流只有一个位置,因此输出和输入位置总是相同的。
Tip
文件流的析构函数自动关闭文件。
下面的示例类似于前面给出的字符串流示例,但使用了一个文件。在这个例子中,ofstream是使用close()显式关闭的,ifstream是通过ifs的析构函数隐式关闭的:
const std::string filename = "output.txt";
std::ofstream ofs(filename);
ofs << 123 << " " << 3.1415;
ofs.close();
std::ifstream ifs(filename);
int myInt; double myDouble;
ifs >> myInt >> myDouble;
std::cout << "int = " << myInt << ", double = " << myDouble << std::endl;
您可以编写自己版本的流输出和提取操作符operator<<和operator>>。下面是一个关于Person类的两个操作符的例子,使用std::quoted()操作符来处理名字中的空格:
std::ostream& operator<<(std::ostream& os, const Person& person) {
os << std::quoted(person.GetFirstName()) << ' '
<< std::quoted(person.GetLastName());
return os;
}
std::istream& operator>>(std::istream& is, Person& person) {
std::string firstName, lastName;
is >> std::quoted(firstName) >> std::quoted(lastName);
person.SetFirstName(firstName); person.SetLastName(lastName);
return is;
}
这些运算符可以如下使用(<sstream>也是必需的):
除了在第 3 和 4 章节中讨论的其他迭代器之外,<iterator>头定义了两个流迭代器std::istream_iterator和std::ostream_iterator。
ostream_iterator是一个输出迭代器,能够使用operator<<向ostream输出某种类型的对象序列。要输出的对象的类型被指定为模板类型参数。有一个构造函数接受对要使用的ostream的引用和一个可选的分隔符,该分隔符在每次输出后被写入流中。
结合第 4 章讨论的算法,流迭代器非常强大。例如,下面的代码片段使用std::copy()算法将double的vector写入控制台,其中每个double后跟一个制表符(另外还需要<vector>和<algorithm>):
std::vector<double> vec{ 1.11, 2.22, 3.33, 4.44 };
std::copy(cbegin(vec), cend(vec),
std::ostream_iterator<double>(std::cout, "\t"));
istream_iterator是一个输入迭代器,能够通过使用operator>>逐个提取对象来迭代istream中的某种类型的对象。要从流中提取的对象的类型被指定为模板类型参数。有三个构造函数:
istream_iterator():默认构造函数,导致迭代器指向流的末尾istream_iterator(istream&):构造一个迭代器,从给定的istream中提取对象istream_iterator(istream_iterator&):复制构造函数
就像一个ostream_iterator,istream_iterator s 结合算法非常厉害。以下示例结合使用for_each()算法和istream_iterator从标准输入流中读取未指定数量的double值,并将它们相加以计算平均值(还需要<algorithm>):
std::istream_iterator<double> begin(std::cin), end;
double sum = 0.0; int count = 0;
std::for_each(begin, end, [&](double value){ sum += value; ++count;});
std::cout << sum / count << std::endl;
在 Windows 上按 Ctrl+Z 或在 Linux 上按 Ctrl+D 终止输入,然后按 Enter。
第二个例子使用一个istream_iterator从控制台读取不确定数量的double和一个ostream_iterator将读取的double写入由制表符分隔的stringstream(另外需要<sstream>和<algorithm>):
std::ostringstream oss;
std::istream_iterator<double> begin(std::cin), end;
std::copy(begin, end, std::ostream_iterator<double>(oss, "\t"));
std::cout << oss.str() << std::endl;
流类不直接处理目标,如内存中的字符串、磁盘上的文件等。相反,他们使用由std::basic_streambuf<CharType>定义的流缓冲区的概念。提供两个typedef、std::streambuf和std::wstreambuf,模板类型分别为char或wchar_t。文件流使用std::(w)filebuf,字符串流使用std::(w)stringbuf,两者都继承自(w)streambuf。
每个流都有一个与之相关联的流缓冲区,您可以使用rdbuf()获得指向该缓冲区的指针。对rdbuf(streambuf*)的调用返回当前关联的流缓冲区,并将其更改为给定的流缓冲区。
流缓冲区可用于编写流重定向器类,将一个流重定向到另一个流。作为一个基本的例子,下面的代码片段将所有的std::cout输出重定向到一个文件(另外还需要<fstream>):
Caution
当更改一个标准流的缓冲区时,不要忘记在终止应用程序之前恢复旧的缓冲区,就像上一个示例中所做的那样。否则,您的代码可能会因某些库实现而崩溃。
它还可以用于实现一个 tee 类,该类将输出重定向到两个或多个目标流。另一个用途是轻松读取整个文件:
std::ifstream ifs("test.txt");
std::stringstream buffer;
buffer << ifs.rdbuf();
流缓冲区的确切行为取决于实现。直接使用流缓冲区是一个高级主题,由于页面限制,我们不能进一步详细讨论。
除了在第 2 章中解释的文件实用程序外,<cstdio>头文件还定义了 C 风格的 I/O 库,包括基于字符的 I/O ( getc()、putc()),...)和格式化的 I/O ( printf(),scanf(),...).所有 C 风格的 I/O 功能都包含在类型安全的 C++ 流中,它也有更好的定义,可移植的错误处理。 1 这一节确实讨论了std::printf()和std::scanf()函数族,而且只讨论这些,因为它们的格式语法紧凑,有时比 C++ 流更方便。
以下printf()系列函数在<cstdio>中定义:
std::printf(const char* format, ...)
std::fprintf(FILE* file, const char* format, ...)
std::snprintf(char* buffer, size_t bufferSize, const char* format, ...)
std::sprintf(char* buffer, const char* format, ...)
它们将格式化的输出分别写入标准输出、文件、给定大小的缓冲区或缓冲区,并返回写出的字符数。最后一个sprintf(),不如snprintf()安全。它们在format字符串后都有数量可变的参数。也有以v为前缀的版本接受va_list作为参数:例如vprintf(const char* format, va_list)。对于前三种,还提供了宽字符版本:(v)wprintf()、(v)fwprintf()和(v)swprintf()。
如何格式化输出由给定的format字符串控制。除了以%开头的序列之外,它的所有字符都按原样写出。格式化选项的基本语法是%后跟一个转换说明符。这告诉printf()如何解释变长参数列表中的下一个值。传递给printf()的参数必须与format中的%指令顺序相同。表 5-6 解释了可用的转换说明符。列出的预期参数类型适用于不使用长度修饰符的情况(稍后讨论)。
表 5-6。
Available Conversion Specifiers for printf()-Like Functions
Caution
C 风格的 I/O 函数不是类型安全的。如果您的转换说明符要求将参数值解释为double,那么该参数必须是真的double(而不是,例如,float或整数)。如果传递了错误的类型,它会编译并运行,但这很少会有好结果。这也意味着永远不要将 C++ std::string原样作为字符串转换说明符的参数传递:而是使用c_str(),如下例所示。
下面的例子打印了美国传统民歌“99 瓶啤酒”的歌词(假设有一个using namespace std):
string bottles = "bottles of beer";
char on_wall[99];
for (int i = 99; i > 0; --i) {
snprintf(on_wall, sizeof(on_wall), "%s on the wall", bottles.c_str());
printf("%d %s, %d %s.\n", i, on_wall, i, bottles.c_str());
printf("Take one down, pass it around, %d %s.\n", i-1, on_wall);
}
格式化选项比到目前为止讨论的基本转换要强大得多。%指令的完整语法如下:
%<flags><width><precision><length_modifier><conversion>
随着
-
<flags>: Zero or more flags that change the meaning of the conversion specifier. See Table 5-7.表 5-7。
Available Flags
| 旗 | 描述 | | --- | --- | | `-` | 左对齐输出。默认情况下,输出右对齐。 | | `+` | 始终输出数字的符号,即使是正数。 | | 空格字符 | 如果要输出的数字是非负的或者没有字符,则在输出前加上一个空格。如果还指定了`+`,则忽略。 | | `#` | 输出一个所谓的另类形式。对于`x`和`X`,如果数字不为零,则结果以`0x`或`0X`为前缀。对于所有浮点说明符(`a`、`A`、`e`、`E`、`f`、`F`、`g`和`G`),输出总是包含一个小数点字符。对于`g`和`G`,尾随零不会被删除。对于 o,精度增加,因此输出的第一个数字是零。 | | `0` | 对于所有的整数和浮点转换说明符(`d`、`i`、`o`、`u`、`x`、`X`、`a`、`A`、`e`、`E`、`f`、`F`、`g`和`G`),用零代替空格进行填充。如果也指定了`-`,或者对于所有整数说明符与精度的组合,则忽略此选项。 | -
<width>:可选最小字段宽度(不截断:仅填充)。如果转换后的值的字符数少于指定的宽度,则应用填充。默认情况下,空格用于填充。<width>可以是非负整数,也可以是*,这意味着从参数列表中的整数参数中获取宽度。此宽度必须在要格式化的值之前。 -
<precision>:一个点,后面跟一个可选的非负整数(如果没有指定,则假定为 0),或者一个*,这也意味着从参数列表中的一个整数参数中获取精度。精度是可选的,它决定了以下内容:- s 的最大字节数,默认情况下,应该是以零结尾的字符数组。
- 所有整数转换说明符(d、I、o、u、X 和 X)的最小输出位数。默认值:1。
- 对于大多数浮点转换说明符(A、A、E、E、F 和 F),小数点后要输出的位数。如果未指定,默认精度为 6。
- g 和 g 的最大有效位数。默认值也是 6。
-
<length_modifier>: An optional modifier that alters the type of the argument to be passed. Table 5-8 gives an overview of all supported modifiers for numeric conversions. For character and strings (candsconversion specifiers, respectively), thellength modifier (note: this is the letterl) changes the expected input type fromintandchar*towint_tandwchar_t*, respectively.2表 5-8。
Length Modifiers for All Numeric Conversion Specifiers
| 修饰语 | `d`,`i` | `o`、`u`、`x`、`X` | `n` | `a`、`A`、`e`、`E`、`f`、`F`、`g`、`G` | | --- | --- | --- | --- | --- | | (无) | `int` | `unsigned int` | `int*` | `double` | | `hh` | `char` | `unsigned char` | `char*` | | | `h` | `short` | `unsigned short` | `short*` | | | `l` | `long` | `unsigned long` | `long*` | | | `ll` | `long long` | `unsigned long long` | `long long*` | | | `j` | `intmax_t` | `uintmax_t` | `intmax_t*` | | | `z` | `size_t` | `size_t` | `size_t*` | | | `t` | `ptrdiff_t` | `ptrdiff_t` | `ptrdiff_t*` | | | `L` | | | | `long double` | -
<conversion>:唯一必需的组件,指定要应用于参数的转换。(见表 5-6 。)
表 5-8 中的修饰符决定了必须按指示传递的输入类型。std::intmax_t、uintmax_、t在<cstdint>(见第章 1 )中定义,size_t、ptrdiff_t在<cstddef>中定义。还要注意的是long和 l ong long修饰符使用字母l,而不是数字1。
以下scanf()系列函数在<cstdio>中定义:
std::scanf(const char* format, ...)
std::fscanf(FILE* file, const char* format, ...)
std::sscanf(const char* buffer, const char* format, ...)
它们分别从标准输入、文件或缓冲区中读取。除了这些在format字符串后面有可变数量的参数的函数之外,还有一些名称以v为前缀并接受va_list作为参数的版本:例如,vscanf(const char* format, va_list)。还提供了宽字符版本:(v)wscanf()、(v)fwscanf()和(v)swscanf()。
它们都根据给定的format字符串读取格式化数据。使用的scanf()格式语法类似于前面看到的printf()格式语法。格式字符串中的所有字符只是用来与输入进行比较,除了以%开头的序列。这些%指令导致值被解析并按顺序存储在函数参数所指向的位置。基本语法是一个%符号,后跟表 5-9 中的一个转换说明符。最后一列显示了未指定长度修饰符时的参数类型(见表 5-10 )。
表 5-10。
Available Length Modifiers for the Numeric Conversion Specifiers of scanf()-Like Functions
表 5-9。
Available Conversion Specifiers for scanf()-Like Functions
对于除转换说明符c、s或[...]之外的所有指令,任何空白字符都会被跳过,直到第一个非空白字符出现。当到达输入字符串的末尾、出现流输入错误或出现解析错误时,解析会停止。返回值等于指定值的数量,或者如果在开始第一次转换前发生输入故障,则返回值为EOF。如果到达流的末尾或出现解析错误,则赋值的数量将小于指令的数量:例如,如果在第一次转换期间出现这种情况,则赋值的数量为零。
%指令的完整语法如下:
%<*><width><length_modifier><conversion>
与:
<*>:一个可选的*符号,使scanf()从输入中解析数据,而不把它存储在任何参数中。<width>:可选最大字段宽度,以字符为单位。<length_modifier>:可选长度修改量:见表 5-10 。当应用于c、s或[...]说明符时,l(字母l)将所需的输入类型从char**修改为wchar_t**。<conversion>:必选。指定要应用的转换;见表 5-9 。
表 5-10 和表 5-8 之间唯一不明显的区别是,默认情况下,浮点参数必须指向一个float而不是一个double。
std::string s = "int: +123; double: -2.34E-3; chars: abcdef";
int i = 0; double d = 0.0; char chars[4] = { 0 };
std::sscanf(s.data(), "int: %i; double: %lE; chars: %[abc]", &i, &d, chars);
std::printf("int: %+i; double: %.2lE; chars: %s", i, d, chars);
Footnotes 1
一些库实现使用errno(参见第 8 章)来报告 C 风格 I/O 函数的错误,包括printf()和scanf()函数:请查阅您的库文档以确认。
wint_t在<cwchar>中定义,是一个足够大的整型的typedef,可以容纳任何宽字符(wchar_t值)和至少一个不是有效宽字符的值(WEOF)。







