输入一个整数,使用异常处理,屏蔽输入小数,字符,python 字符串转小数时的错误

转自:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp
RegExp&构造函数创建了一个正则表达式对象,用于将文本与一个模式匹配。
有关正则表达式的介绍,请阅读&中的。
字面量, 构造函数和工厂符号都是可以的:
/pattern/flags
new RegExp(pattern [, flags])
RegExp(pattern [, flags])
pattern正则表达式的文本。flags
如果指定,标志可以具有以下值的任意组合:
g全局匹配;找到所有匹配,而不是在第一个匹配后停止i忽略大小写m多行; 将开始和结束字符(^和$)视为在多行上工作(也就是,分别匹配每一行的开始和结束(由 \n 或 \r 分割),而不只是只匹配整个输入字符串的最开始和最末尾处。uU&将模式视为Unicode序列点的序列y粘性匹配;&仅匹配目标字符串中此正则表达式的lastIndex属性指示的索引(并且不尝试从任何后续的索引匹配)。
有两种方法来创建一个RegExp对象:一是字面量、二是构造函数。要指示字符串,字面量的参数不使用引号,而构造函数的参数使用引号。因此,以下表达式创建相同的正则表达式:
new RegExp('ab+c', 'i');
new RegExp(/ab+c/, 'i');
当表达式被赋值时,字面量形式提供正则表达式的编译(compilation)状态,当正则表达式保持为常量时使用字面量。例如当你在循环中使用字面量构造一个正则表达式时,正则表达式不会在每一次迭代中都被重新编译(recompiled)。
而正则表达式对象的构造函数,如&new RegExp('ab+c')&提供了正则表达式运行时编译(runtime compilation)。如果你知道正则表达式模式将会改变,或者你事先不知道什么模式,而是从另一个来源获取,如用户输入,这些情况都可以使用构造函数。
从ECMAScript 6开始,当第一个参数为正则表达式而第二个标志参数存在时,new RegExp(/ab+c/, 'i')不再抛出&(&当从其他正则表达式进行构造时不支持标志&)的异常,取而代之,将使用这些参数创建一个新的正则表达式。
当使用构造函数创造正则对象时,需要常规的字符转义规则(在前面加反斜杠 \)。比如,以下是等价的:
var re = new RegExp("\\w+");
var re = /\w+/;
正则表达式中特殊字符的含义
字符类别(Character Classes)
(点号,小数点) 匹配任意单个字符,但是行结束符除外:\n&\r&\u2028&或&\u2029。
在字符集中,点( . )失去其特殊含义,并匹配一个字面点( . )。
需要注意的是,m&多行(multiline)标志不会改变点号的表现。因此为了匹配多行中的字符集,可使用[^]&(当然你不是打算用在旧版本 IE 中),它将会匹配任意字符,包括换行符。
例如,/.y/&匹配 "yes make my day" 中的&"my" 和 "ay",但是不匹配 "yes"。
匹配任意阿拉伯数字。等价于[0-9]。
例如,/\d/&或&/[0-9]/&匹配 "B2 is the suite number." 中的&'2'。&
匹配任意一个不是阿拉伯数字的字符。等价于[^0-9]。
例如,/\D/&或&/[^0-9]/&匹配 "B2 is the suite number." 中的&'B'。
匹配任意来自基本拉丁字母表中的字母数字字符,还包括下划线。等价于&[A-Za-z0-9_]。
例如,/\w/&匹配 "apple" 中的 'a',"$5.28" 中的 '5' 和 "3D" 中的 '3'。
匹配任意不是基本拉丁字母表中单词(字母数字下划线)字符的字符。等价于&[^A-Za-z0-9_]。
例如,/\W/&或&/[^A-Za-z0-9_]/&匹配 "50%" 中的 '%'。
匹配一个空白符,包括空格、制表符、换页符、换行符和其他 Unicode 空格。
等价于&[ \f\n\r\t\v\u00a0\ue\u\u\u2004 \u\u\ua\u\u202f\u205f \u3000]。
例如&/\s\w*/&匹配 "foo bar" 中的 ' bar'。
匹配一个非空白符。等价于&[^ \f\n\r\t\v\u00a0\ue\u\u\u2004&\u\u\ua\u\u202f\u205f\u3000]。
例如,/\S\w*/&匹配 "foo bar" 中的 'foo'。
匹配一个水平制表符(tab)
匹配一个回车符(carriage return)
匹配一个换行符(linefeed)
匹配一个垂直制表符(vertical tab)
匹配一个换页符(form-feed)
匹配一个退格符(backspace)(不要与&\b&混淆)
匹配一个 NUL 字符。不要在此后面跟小数点。
X&是 A - Z 的一个字母。匹配字符串中的一个控制字符。
例如,/\cM/&匹配字符串中的 control-M。
匹配编码为&hh&(两个十六进制数字)的字符。
匹配 Unicode 值为&hhhh&(四个十六进制数字)的字符。
对于那些通常被认为字面意义的字符来说,表示下一个字符具有特殊用处,并且不会被按照字面意义解释。
例如&/b/&匹配字符 'b'。在 b 前面加上一个反斜杠,即使用&/\b/,则该字符变得特殊,以为这匹配一个单词边界。
对于那些通常特殊对待的字符,表示下一个字符不具有特殊用途,会被按照字面意义解释。
例如,* 是一个特殊字符,表示匹配某个字符 0 或多次,如&/a*/&意味着 0 或多个 "a"。 为了匹配字面意义上的&*&,在它前面加上一个反斜杠,例如,/a\*/匹配 'a*'。
字符集合(Character Sets)
一个字符集合,也叫字符组。匹配集合中的任意一个字符。你可以使用连字符'-'指定一个范围。
例如,[abcd] 等价于 [a-d],匹配"brisket"中的'b'和"chop"中的'c'。
一个反义或补充字符集,也叫反义字符组。也就是说,它匹配任意不在括号内的字符。你也可以通过使用连字符 '-' 指定一个范围内的字符。
例如,[^abc]&等价于&[^a-c]。&第一个匹配的是 "bacon" 中的'o' 和 "chop" 中的 'h'。
边界(Boundaries)
匹配输入开始。如果多行(multiline)标志被设为 true,该字符也会匹配一个断行(line break)符后的开始处。
例如,/^A/&不匹配 "an A" 中的 "A",但匹配 "An A" 中的 "A"。
匹配输入结尾。如果多行(multiline)标志被设为 true,该字符也会匹配一个断行(line break)符的前的结尾处。
例如,/t$/&不匹配 "eater" 中的 "t",但匹配 "eat" 中的 "t"。
匹配一个零宽单词边界(zero-width word boundary),如一个字母与一个空格之间。 (不要和&[\b]&混淆)
例如,/\bno/&匹配 "at noon" 中的 "no",/ly\b/&匹配 "possibly yesterday." 中的 "ly"。
匹配一个零宽非单词边界(zero-width non-word boundary),如两个字母之间或两个空格之间。
例如,/\Bon/&匹配 "at noon" 中的 "on",/ye\B/&匹配 "possibly yesterday." 中的 "ye"。
分组(Grouping)与反向引用(back references)
匹配&x&并且捕获匹配项。 这被称为捕获括号(capturing parentheses)。
例如,/(foo)/&匹配且捕获 "foo bar." 中的 "foo"。被匹配的子字符串可以在结果数组的元素&[1], ..., [n]&中找到,或在被定义的&RegExp&对象的属性&$1, ..., $9&中找到。
捕获组(Capturing groups)有性能惩罚。如果不需再次访问被匹配的子字符串,最好使用非捕获括号(non-capturing parentheses),见下面。
n&是一个正整数。一个反向引用(back reference),指向正则表达式中第 n 个括号(从左开始数)中匹配的子字符串。
例如,/apple(,)\sorange\1/&匹配 "apple, orange, cherry, peach." 中的 "apple,orange,"。一个更全面的例子在该表格下面。
匹配&x&不会捕获匹配项。这被称为非捕获括号(non-capturing parentheses)。匹配项不能够从结果数组的元素&[1], ..., [n]&或已被定义的&RegExp&对象的属性&$1, ..., $9&再次访问到。
数量词(Quantifiers)
匹配前面的模式&x&0 或多次。
例如,/bo*/&匹配 "A ghost booooed" 中的 "boooo","A bird warbled" 中的 "b",但是不匹配 "A goat grunted"。
匹配前面的模式&x&1 或多次。等价于&{1,}。
例如,/a+/&匹配 "candy" 中的 "a","caaaaaaandy" 中所有的 "a"。
像上面的 * 和 + 一样匹配前面的模式&x,然而匹配是最小可能匹配。
例如,/".*?"/&匹配 '"foo" "bar"' 中的 '"foo"',而 * 后面没有 ? 时匹配 '"foo" "bar"'。
匹配前面的模式&x&0 或 1 次。
例如,/e?le?/&匹配 "angel" 中的 "el","angle" 中的 "le"。
如果在数量词&*、+、?&或&{}, 任意一个后面紧跟该符号(?),会使数量词变为非贪婪( non-greedy) ,即匹配次数最小化。反之,默认情况下,是贪婪的(greedy),即匹配次数最大化。
在使用于向前断言(lookahead assertions)时,见该表格中&(?=)、(?!)&和&(?:)&的说明。
只有当&x&后面紧跟着&y&时,才匹配&x。 例如,/Jack(?=Sprat)/&只有在 'Jack' 后面紧跟着 'Sprat' 时,才会匹配它。/Jack(?=Sprat|Frost)/&只有在 'Jack' 后面紧跟着 'Sprat' 或 'Frost' 时,才会匹配它。然而,'Sprat' 或 'Frost' 都不是匹配结果的一部分。
只有当&x&后面不是紧跟着&y&时,才匹配&x。例如,/\d+(?!\.)/&只有当一个数字后面没有紧跟着一个小数点时,才会匹配该数字。
/\d+(?!\.)/.exec("3.141")&匹配 141 而不是 3.141。
匹配&x&或&y
例如,/green|red/&匹配 "green apple" 中的 &green',"red apple." 中的 'red'。
n&是一个正整数。前面的模式&x&连续出现 n 次时匹配。
例如,/a{2}/&不匹配 "candy," 中的 "a",但是匹配 "caandy," 中的两个 "a",且匹配 "caaandy." 中的前两个 "a"。
n&是一个正整数。前面的模式&x&连续出现至少 n 次时匹配。
例如,/a{2,}/&不匹配 "candy" 中的 "a",但是匹配 "caandy" 和 "caaaaaaandy." 中所有的 "a"。
n&和&m&为正整数。前面的模式 x 连续出现至少 n 次,至多 m 次时匹配。
例如,/a{1,3}/&不匹配 "cndy",匹配 "candy," 中的 "a","caandy," 中的两个 "a",匹配 "caaaaaaandy" 中的前面三个 "a"。注意,当匹配 "caaaaaaandy" 时,即使原始字符串拥有更多的 "a",匹配项也是 "aaa"。
断言(Assertions)
仅匹配被y跟随的x。
举个例子,/Jack(?=Sprat)/,如果"Jack"后面跟着sprat,则匹配之。
/Jack(?=Sprat|Frost)/&,如果"Jack"后面跟着"Sprat"或者"Frost",则匹配之。但是,"Sprat" 和"Frost" 都不会在匹配结果中出现。
仅匹配不被y跟随的x。
举个例子,/\d+(?!\.)/&只会匹配不被点(.)跟随的数字。/\d+(?!\.)/.exec('3.141')&匹配"141",而不是"3.141"
[\t\n\v\f\r \u00a0\u\u\u\u\u\u200a\u200b\u\u3000]
For properties available on&RegExp&instances, see&.
&允许为所有正则对象添加属性。RegExp.lengthRegExp.length&值为 2。
Properties inherited from&:
For methods available on&RegExp&instances, see&.
全局对象&RegExp&自身没有方法, 不过它会继承一些方法通过原型链
Methods inherited from&:
RegExp 实例
注意,RegExp&对象的几个属性既有完整的长属性名,也有对应的类 Perl 的短属性名。两个属性都有着同样的值。JavaScript 的正则语法就是基于 Perl 的。
RegExp.prototype.constructor创建该正则对象的构造函数。是否开启全局匹配,也就是匹配目标字符串中所有可能的匹配项,而不是只进行第一次匹配。在匹配字符串时是否要忽略字符的大小写。下次匹配开始的字符串索引位置。是否开启多行模式匹配(影响 ^ 和 $ 的行为)。正则对象的源模式文本。&是否开启粘滞匹配。
Properties inherited from&:
在目标字符串中执行一次正则匹配操作。测试当前正则是否能匹配目标字符串。&返回一个字符串,其值为该正则对象的字面量形式。覆盖了Object.prototype.toSource&方法.返回一个字符串,其值为该正则对象的字面量形式。覆盖了&方法。
Methods inherited from&:
,&,&,&,&,&,&,&,&,&,&,&
例子:使用正则改变数据结构
下例使用&&方法 (继承自&)去匹配姓名&first last&输出新的格式&last,&first。脚本中使用&$1 和&$2&指明括号里先前的匹配.
var re = /(\w+)\s(\w+)/;
var str = "John Smith";
var newstr = str.replace(re, "$2, $1");
print(newstr);
显示 "Smith, John".
例子:在多行中使用正则表达式
var s = "Please yes\nmake my day!";
s.match(/yes.*day/);
例子: 使用带有 &sticky& 标志的正则表达式
该例展示了,如何在正则表达式上使用 sticky 标志,用来匹配多行输入的单独行。
var text = "First line\nsecond line";
var regex = /(\S+) line\n?/y;
var match = regex.exec(text);
print(match[1]);
可以使用&try { & } catch { & }&来测试运行时(run-time)是否支持&sticky&标志。这种情况下,必须使用&eval(&)&表达式或&RegExp(regex-string,&flags-string)&语法(这是由于&/regex/flags&表示法将会在编译时刻被处理,因此在&catch&语句块处理异常前就会抛出一个异常。例如:
var supports_sticky;
try { RegExp('','y'); supports_sticky = true; }
catch(e) { supports_sticky = false; }
alert(supports_sticky);
例子:使用正则表达式和 Unicode 字符
正如上面表格提到的,\w&或&\W&只会匹配基本的 ASCII 字符;如 'a' 到 'z'、 'A' 到 'Z'、 0 到 9 及 '_'。为了匹配其他语言中的字符,如西里尔(Cyrillic)或 希伯来语(Hebrew),要使用&\uhhhh,"hhhh" 表示以十六进制表示的字符的 Unicode 值。下例展示了怎样从一个单词中分离出 Unicode 字符。
var text = "Образец text на русском языке";
var regex = /[\u0400-\u04FF]+/g;
var match = regex.exec(text);
print(match[1]);
这里有一个外部资源,用来获取 Unicode 中的不同区块范围:
例子:从 URL 中提取子域名
var url = "http://xxx.domain.com";
print(/[^.]+/.exec(url)[0].substr(7));
SpecificationStatusComment
ECMAScript 1st Edition. Implemented in JavaScript 1.1
Initial definition.
浏览器兼容性
. This compatibility table still uses the old format, because we haven't yet converted the data it contains.&
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari
Basic support
Sticky flag ("y")
[1] Behind a flag.
[2] At least from version 41.
Gecko-特定的 注释
Starting with Gecko 34 (Firefox 34 / Thunderbird 34 / SeaMonkey 2.31), in the case of a capturing group with quantifiers preventing its exercise, the matched text for a capturing group is now&undefined&instead of an empty string:
// Firefox 33 or older
'x'.replace(/x(.)?/g, function(m, group) {
console.log("'group:" + group + "'");
}); // 'group:'
// Firefox 34 or newer
'x'.replace(/x(.)?/g, function(m, group) {
console.log("'group:" + group + "'");
}); // 'group:undefined'
Note that due to web compatibility,&RegExp.$N&will still return an empty string instead of&undefined&().
阅读(...) 评论()一、C++输入和输出概述
  1.1、流和缓冲区
  C++程序把输入和输出看作字节流。输入时,程序从输入流中抽取字节;输出时,程序将字节插入到输出流中。对于面相文本的程序,每个字节代表一个字符,更通俗地说,字节可以构成字符或数值数据的二进制表示。输入流中的字节可能来自键盘,也可能来自存储设备(如硬盘)或其他程序。输出流中的字节可以流向屏幕、打印机、存储设备或其他程序。流充当了程序和流源或流目标之间的桥梁。这使得C++程序可以以相同的方式对待来自键盘的输入和来自文件的输入。C++程序只检查字节流,而不需要知道字节来自何方。同理,通过使用流,C++程序处理输出的方式将独立于其去向。因此管理输入将包含两步:
  *将流与输入去向的程序关联起来。
  *将流与文件连接起来。
  换句话说,输入流需要两个连接,每端各一个。文件端部连接提供了流的来源,程序端连接将流的流出部分转储到程序中(文件端连接可以是文件,也可以是设备)。同样,对输出的管理包括将输出流连接到程序以及将输出目标与流关联起来。
  通常,通过使用缓冲区可以更高效地处理输入和输出。缓冲区是用作中介的内存块,它是将信息从设备传输到程序或从程序传输给设备的临时存储工具。
  1.2、流、缓冲区和iostream文件
  管理流和缓冲区的工作有点复杂,但iostream(以前为iostream.h)文件中包含一些专门设计用来实现、管理流和缓冲区的类。C++98版本C++I/O定义了一些类模板,以支持char和wchar_t数据;C++11添加了char16_t和char32_t具体化。通过使用typedef工具,C++使得这些模板char具体化能够模仿传统的非模板I/O实现。下面是其中的一些类:
  *streambuf类为缓冲区提供了内存,并提供了用于填充缓冲区、访问缓冲区内容、刷新缓冲区和管理缓冲区内存的类方法;
  *ios_base类表示流的一般特征,如是否可读取、是二进制流还是文本流等;
  *ios类基于ios_base,其中包含了一个指向streambuf对象的指针成员;
  *ostream类是从ios类派生而来的,提供了输出方法;
  *istream类也是从ios类派生而来的,提供了输入方法;
  *iostream类是基于istream和ostream类的,因此继承了输入方法和输出方法。
  要使用这些工具,必须使用适当的类对象。例如,使用ostream对象(如cout)来处理输出。创建这样的对象将打开一个流,自动创建缓冲区,并将其与流关联起来,同时使得能够使用类成员函数。
  补充:重定义I/O
  ISO/ANSI标准C++98对I/O作了两方面的修订。首相是从ostream.h到ostream的变化,用ostream将类放到了std名称空间中。其次,I/O类被重新编写。为成为国际语言,C++必须能够处理需要16位的国际字符集或更宽的字符类型。因此,该语言在传统的8位char(&窄&)类型的基础上添加了wchar_t(&宽&)字符类型;而C++11添加了类型char16_t和char32_t。每种类型都需要有自己的I/O工具。标准委员会被没有开发两套(现在为4套)独立的类,而是开发了一套I/O类模板,其中包括basic_istream&charT, traits&charT&&和basic_ostream&charT, traits&charT&&。traits&charT&模板是一个模板类,为字符类型定义了具体特性,如如何比较字符是否相等以及字符的EOF值等。该C++11标准提供了I/O的char和wchar_t具体化。例如,istream和ostream都是char具体化的typedef。同样,wistream和wostream都是wchar_t的具体化。例如,wcout对象用于输出宽字符流。头文件ostream中包含了这些定义。
  ios基类中的一些独立于类型的信息被移动到了新的ios_base类中,这包括各种格式化常量,例如ios::fixed(现在为ios_base::fixed)。另外,ios_base还包含了一些老式ios中没有的选项。
  C++的iostream类库管理了很多细节。例如,在程序中包含iostream文件将自动创建8个流对象(4个用于窄字符流,4个用于宽字符流)。
  *cin对象对应于标准输入流。在默认情况下,这个流被关联到标准输入设备(通常为键盘)。wcin对象于此类似,但处理的是wchar_t类型。
  *cout对象与标准输出流相对应。在默认情况下,这个流被关联到标准输出设备(通常为显示器)。wcout对象与此类似,但处理的事wchar_t类型。
  *cerr对象与标准错误流相对应,可用于显示错误消息。在默认情况下,这个流被关联到标准输出设备(通常为显示器)。这个流没有被缓冲,这意味着信息将被直接发送给屏幕,而不会等到缓冲区填满或新的换行符。wcerr对象与此类似,但处理的是wchar_t类型。
  *clog对象也对应着标准错误流。在默认情况下,这个流被关联到标准输出设备(通常为显示器)。这个流被缓冲。wclog对象于此类似,但处理的是wchar_t类型
  *对象代表流&&这意味着什么呢?当iostream文件为程序声明一个cout对象时,该对象将包含存储了与输出有关的信息的数据成员,如显示数据时使用的字段宽度、小数位数、显示整数时采用的计数方法以及描述用来处理输出流的缓冲区的streambuf对象的地址。下面的语句通过指向的streambuf对象将字符串"Hello world!"中的字符放到cout管理的缓冲区中:cout && "Hello world!";
  总之,流的一端与程序相连,另一端与标准输出相连,cout对象凭借streambuf对象的帮助,管理者流中的字节流。
  1.3、重定向
  标准输入和输出流通常连接着键盘和屏幕。但很多操作系统(包括UNIX,Linux和Windows)都支持重定向,这个工具使得能够改变标准输入和标准输出。
二、使用cout进行输出
  C++将输出看作字节流(根据平台的不同,可能是8位、16位或32位的字节,但都是字节),但在程序中,很多数据被组织成比字节更大的单位。例如,ing类型由16位或32位的二进制表示;double值由64位的二进制数表示。但在将字节流发送给屏幕时,希望每个字节表示一个字符值。因此,ostream类最重要的任务之一是将数值类型(如int或float)转换为以文本形式表示的字符流。也就是说,ostream类将数据内部表示(二进制位模式)转换为由字符字节组成的输出流(以后会有仿生移植物,使得能够直接翻译二进制数据)。为执行这些转换任务,ostream类提供了多个类方法。
  2.1、重载的&&运算符
  在C++中,与C一样,&&运算符的默认含义是按位左移运算符。但ostream类重新定义了&&运算符,方法是将其重载为输出。在这种情况下,&&叫做插入运算符,而不是左移运算符。插入运算符被重载,使之能够识别C++中所有的基本类型:
  *  long long(C++11);
  *  unsigned long long (C++11);
  对于上述每种类型,ostream类都提供了operator&&()函数的定义。因此,如果使用下面这样一条语句,而value是前面列出的类型之一,则C++程序将其对应于有相应的特征标的运算符函数:
  cout &&
  2.1.1 输出和指针
   ostream类还为下面的指针类型定义了插入运算符函数:
  *  const signed char *;
  *  const unsigned char *;
  *&  const char *;
  *  void *。
  C++使用指向字符串存储位置的指针来表示字符串。指针的形式可以是char数组、显式的char指针或用引号扩起来的字符串;并使用字符串中的终止空字符来确定何时停止显示字符。
  对于其他类型的指针,C++将其对应于void *,并打印地址的数值表示。如果要获取字符串的地址,则必须将其强制转换为其他类型,例如:    int num = 12;
    char * name = "Tom";
    cout && & // 打印变量num的地址
    cout &&//将会打印字符串&Tom&
    cout && (void *)//将会打印字符串&Tom&的地址
  2.1.2 拼接输出
  插入运算符的所有化身的返回类型都是ostream & 。也就是说,原型格式如下:
  ostream & operator&&(type);
  (其中,type是要显示的数据的类型)返回类型ostream &意味着使用该运算符将返回一个指向ostream对象的引用;函数定义指出,该引用指向用于调用该运算符的对象。换句话说,运算符函数的返回值为调用运算符的对象。这种特性使得能够通过插入来连接输出。
  2.2 其他的ostream方法
  除了各种operator&&()函数外,ostream类还提供了put()方法和write()方法,前者用于显示字符,后者用于显示字符串。
  最初,put()的原型如下:
    ostream & put(char);
  当前标准与此相同,但被模板化,以适用于wchar_t。可以用类方法表示法调用它:
    cout.put('W');
  其中,cout是调用方法的对象,put()是成员函数。和&&运算符函数一样,该函数也返回一个指向调用对象的引用,因此可以用它拼接输出:
    cout.put(''A).put('B');
  在原型合适的情况下,可以将数值型参数(如int)用于put(),让函数原型自动将参数转换为正确的char值。例如:
    cout.put(65);//显示A
    cout.put(66.3);//显示B
  write()方法显示整个字符串,其模板原型如下:
    basic_ostream&charT, traits& & write (const char_type* s, streamsize n);
  write()的第一个参数提供了要显示的字符串的地址,第二个参数指出要显示多少个字符。使用cout调用write()时,将调用char具体化,因此返回类型为ostream &。
1 #include &iostream&
3 int main()
using std::
using std::
const char * state1 = "Florida";
const char * state2 = "Kansas";
const char * state3 = "Euphoria";
std::size_t len = std::strlen(state2);
cout && "Increasing loop index:\n";
int i = 0;
for (i = 1; i & i ++) {
cout.write(state2, i);
cout && "Decreasing loop index:\n";
for (i = (int) i & 0; i--) {
cout.write(state2, i) &&
cout && "Exceeding string length:\n";
cout.write(state2, len + 5) &&
27 输出结果:
28 Increasing loop index:
34 Decreasing loop index:
41 Exceeding string length:
42 KansasEuph
  从上面的程序可以看出,write()方法并不会在遇到空字符时停止打印字符,而只是打印指定数目的字符,即使超出了字符串的边界。
  write()方法也可用于数值数据,可以将数字的地址强制转换为char *,然后传递给它:
#include &iostream&
int main()
long val = ;
std::cout.write((char *) & val, sizeof(val));
输出结果:
  这不会将数值转换为相应的字符,而是传输内存中存储的位表示。例如,4字节的long值将作为4个独立的字节被传输。输出设备将把每个字节作为ASCII码进行解释。
  2.3 刷新输出缓冲区    
  如果程序使用cout将字节发送给标准输出,情况将如何?由于ostream类对cout对象处理的输出进行缓冲,所以输出不会立即发送到目标地址,而是被存储在缓冲区中,直到缓冲区填满。然后,程序将刷新(flush)缓冲区,把内容发送出去,并清空缓冲,以存储新的数据。通常,缓冲区为512字节或其整数倍。当标准输出连接的是硬盘上的文件时,缓冲可以节省大量的时间。
  如果实现不能在所希望时刷新输出,可以使用两个控制符中的一个来强行进行刷新。空字符flush刷新缓冲区,而控制符endl刷新缓冲区并插入一个换行符。这两个控制符的使用方式与变量名相同:
    cout && "Hello world!" &&
    cout && "Wait for me." &&
  事实上,控制符也是函数。例如,可以直接调用flush()来刷新cout缓冲区:
    flush(cout);
  然而,ostream类对&&插入运算符进行了重载,使得下述表达式将被替换为函数调用flush(cout):
    cout &&
  因此,可以用更为方便的插入表示法来成功地进行刷新。
  2.4 使用cout进行格式化
  ostream插入运算符将值转换为文本格式。在默认情况下,格式化值的方式如下。
    *  对于char值,如果它代表的是可打印字符,则将被作为一个字符显示在宽度为一个字符的字段中。
    *  对于数值整型,将以十进制方式显示在一个刚好容纳该数字及负号(如果有的话)的字段中;
    *  字符串被显示在宽度等于该字符串长度的字段中。
  浮点数的默认行为有变化。下面详细说明了老式实现和新式实现之间的区别。
    *新式:浮点类型被显示为6位,末尾的0不显示(注意,显示的数字位数与数字被存储时精度设置没有任何关系)。数字以定点表示法显示还是科学计数法表示,取决于它的值。具体来说,当指数大于等于6或小于等于-5时,将使用科学计数法表示。另外,字段宽度恰好容纳数字和负号(如果有的话)。默认的行为对应于带%g说明符的标准C库函数fprintf()。
    *老式:浮点类型显示为带6位小数,末尾的0不显示(注意,显示的数字位数与数字被存储时的精度没有任何关系)。数字以定点表示法显示还是以科学计数法表示,取决于他的值。另外,字段宽度恰好容纳数字和负号(如果有的话)。
  因为每个值的显示宽度等于它的长度,因此必须显式地在值之间提供空格;否则,相邻的值将不会被分开。
  注意:并非所有的编译器都能生成符合当前C++标准格式的输出。另外,当前标准允许区域性变化。例如,欧洲实现可能遵循欧洲人的风格:使用逗号而不是句点来表示小数点。也就是说,2.54将被写成2,54 。区域库(头文件locale)提供了用特定的风格影响(imbuing)输入或输出流的机制,所以同一个编译器能够提供多个区域选项。本章使用美式格式。  
  (1)修改显示时使用的计数系统
  ostream类是从ios类派生来的,而后者是从ios_base派生来的。ios_base类存储了描述格式状态的信息。例如,一个类成员中某些位决定了使用的计数系统,而另一个成员则决定了字段宽度。通过使用控制符(manipulator),可以控制显示整数时使用的技术系统。通过使用ios_base的成员函数,可以控制字段宽度和小数位数。由于ios_base类是ostream的间接基类,因此可以将其方法用于ostream对象(或子代),如cout。
  注意:ios_base类中的成员和方法以前位于ios中。现在,ios_base是ios的基类。在新系统中,ios是包含char和wchar_t具体化的模板,而ios_base包含了非模板特性。
  要控制整数以十进制、十六进制还是八进制显示,可以使用dec、hex和oct控制符。例如,下面的函数调用将cout对象的计数系统格式状态设置为十六进制:
    hex(cout);
    完成上述设置后,程序将以十六进制形式打印整数值,直到将格式状态设置为其他选项为止。注意,控制符不是成员函数,因此不必通过对象来调用。
  虽然控制符实际上是函数,但它们通常的使用方式为:
    cout &&
    ostream类重载了&&运算符,这使得上述用法与函数调用hex(cout)等价。控制符位于名称空间std中。下面的代码演示了控制符的使用方法(注意,可以单独使用控制符,也可以将其作为一系列插入的部分):
#include &iostream&
int main()
using namespace
cout && "输入一个数字:";
std::string str(5,' ');
cout && "n" && str && "n*n\n";
cout && n && str && n*n && "(decimal)\n";
//设置成十六进制系统
cout && n &&
cout && n*n && "
(hexadecimal)\n";
//设置为八进制系统
cout && oct && n && str && n * n && "
(octal)\n";
dec(cout);
cout && n && str && n * n && "
(decimal)\n";
输出结果:
输入一个数字:12
144(decimal)
(hexadecimal)
  (2)调整字段宽度
  可以使用width成员函数将长度不同的数字放到宽度相同的字段中,该方法的原型为:
    int width();
    int width(int i);
  第一种格式返回字段宽度的当前设置;第二种格式将字段宽度设置为i个空格,并返回以前的字段宽度值。这使得能够保存以前的值,以便以后恢复宽度值时使用。
  width()方法之影响显示的下一个项目,然后字段宽度将恢复为默认值。由于width()是成员函数,因此必须使用对象来调用它。
  C++永远不会截短数据,因此如果试图在宽度为2的字段中打印一个7位值,C++将增宽字段,以容纳该数据。C/C++的原则是:显示所有的数据比保持列的整洁更重要;C++视内容重于形式。
1 #include &iostream&
3 int main()
using namespace
size_t w = cout.width(30);
cout && "Default field width = " && w && ":\n";
cout.width(5);
cout && "N" && ':';
cout.width(8);
cout && "N * N" && ":\n";
for (long i = 1; i &= 100; i *= 10) {
cout.width(5);
cout && i && ':';
cout.width(8);
cout && i * i && ":\n";
22 输出结果:
Default field width = 0:
  在上面的输出中,值在字段中右对齐。输出中包含空格,也就是说,cout通过加入空格来填满整个字段。右对齐时,空格被插入到值的左侧。用来填充的字符叫做填充字符(fill character)。右对齐是默认的。
  (3)填充字符
  在默认情况下,cout使用空格填充字段中未被使用的部分,可以使用fill()成员函数来改变填充字符。例如,下面的函数调用将填充字符改为星号:    cout.fill('*');
  这对于检查打印结果,防止接收方添加数字很有用。
1 #include &iostream&
3 int main()
using namespace
cout.fill('*');
const char * staff[2] = {"iPhone6s", "iPad2"};
long bonus[2] = {900,1350};
for (int i = 0; i & 2; i ++) {
cout.width(10);
cout && staff[i] && ": $";
cout.width(7);
cout && bonus[i] && "\n";
18 输出结果:
19 **iPhone6s: $****900
20 *****iPad2: $***1350
  (4)设置浮点数的显示精度
  浮点数精度的含义取决于输出模式。在默认情况下,它指的是显示的总位数。在定点模式和科学模式下,精度指的是小数点后面的位数。已经知道,C++的默认精度为6位(但末尾的0将不显示)。precision成员函数使得能够选择其他值。例如,下面的函数调用将cout的精度设置为2:
    cout.precision(2);
  和width()的情况不同,但与fill()相似,新的精度设置将一直有效,直到被重新设置。下面的程序说明了这一点:
1 #include &iostream&
3 int main()
using namespace
float price1 = 25.5;
float price2 = 1.9 + 8.0/9.0;
cout.width(10);
cout && "\"Apple\"" && ": $" && price1 && "\n";
cout.width(10);
cout && "\"Flower\"" && ": $" && price2 && "\n";
cout.precision(2);
cout.width(10);
cout && "\"Apple\"" && ": $" && price1 && "\n";
cout.width(10);
cout && "\"Flower\"" && ": $" && price2 && "\n";
22 输出结果:
"Apple": $25.5
"Flower": $2.78889
"Apple": $26
"Flower": $2.8
  注意,第3行没有打印小数点及其后面的内容。另外,第4行显示的总位数为2位。
  (5)打印末尾的0和小数点
  对于有些输出,希望保留末尾的0,iostream系列类没有提供专门用于完成这项任务的函数,但ios_base类提供了一个setf()函数(用于set标记),能够控制多种格式化特性。这个类还定义了多个常量,可以作为函数的参数。例如,下面的函数调用使cout显示末尾的小数点:
    cout.setf(ios_base::showpoint);
  使用默认的浮点格式时,上述语句还将导致末尾的0被显示出来。
  showpoint是ios_base类声明中定义的类级静态常量。类级意味着如果在成员函数定义的外面使用它,则必须在常量名前加上作用域运算符(::)。因此,ios_base::showpoint指的是在ios_base类中定义的一个常量。
1 #include &iostream&
3 int main()
using namespace
float price1 = 25.5;
float price2 = 1.9 + 8.0/9.0;
cout.setf(ios_base::showpoint);
cout.width(10);
cout && "\"Apple\"" && ": $" && price1 && "\n";
cout.width(10);
cout && "\"Flower\"" && ": $" && price2 && "\n";
cout.precision(2);
cout.width(10);
cout && "\"Apple\"" && ": $" && price1 && "\n";
cout.width(10);
cout && "\"Flower\"" && ": $" && price2 && "\n";
24 输出结果:
"Apple": $25.5000
"Flower": $2.78889
"Apple": $26.
"Flower": $2.8
  (6)再谈setf()  
  ios_base类有一个受保护的数据成员,其中的各位(这里叫做标记)分别控制着格式化的各个方面,例如计数系统、是否显示末尾的0等。打开一个标记称为设置标记(或位),并意味着相应的位被设置为1 。位标记是编程开关,相当于DIP开关以配置计算机硬件。例如,hex、dec和oct控制符调整控制计数系统的3个标记位。setf()函数提供了另一个调整标记位的途径。
  setf()函数有两个原型。第一个为:
    fmtflags setf(fmtflags);
  其中,fmtflags是bitmask类型的typedef名,用于存储格式标记。该名称是在ios_base类中定义的。这个版本的setf()用来设置单个位控制的格式信息。参数是一个fmtflags值,指出要设置哪一位。返回值的类型是fmtflags的数字,指出所有标记以前的设置。如果打算以后恢复原始设置,则可以保存这个值。ios_base类定义了代表位值得常量,下表列出了其中的一些定义:
ios_base::boolalpha
输入和输出bool值,可以为true和false
ios_base::showbase
对于输出,使用C++基数前缀(0,0x)
ios_base::showpoint
显示末尾的小数点
ios_base::uppercase
对于16进制输出,使用大写字母,E表示法
ios_base::showpos
在正数前面加上+
  注意:bitmask类型是一种用来存储各个位值的类型。它可以是整型、枚举,也可以是STL bitset容器。这里的主要思想是,每一位都是可以单独访问的,都有自己的含义。iostream软件包使用bitmask来存储状态信息。
  由于这些格式常量都是在ios_base类中定义的,因此使用它们时,必须加上作用域解析运算符。并且,修改将一直有效,直到被覆盖为止。注意,仅当基数为10才使用加号。C++将十六进制和八进制都是为无符号的,因此对于它们,无需使用符号。
  第二个setf()原型接受两个参数,并返回以前的设置:
    fmtflags setf (fmtflags, fmtflags);
  函数的这种重载格式用于设置由多位控制的格式选项。第一个参数和以前一样,也是一个包含了所需设置的fmtflags值。第二个参数指出要清除第一个参数中的哪些位。例如,将第3位设置为1表示以10为基数,将第4位设置为1表示以8为基数,将第5位设置为1表示以16为基数。假设输出是以10为基数的,而要将它设置为16位基数,则不仅需要将第5位设置为1,还需要将第3位设置为0&&这叫做清除位。
  ios_base类定义了可按这种方式处理的3组格式标记。每组标记都由一个可用作第二参数的常量和两三个可用作第一参数的常量组成。第二个参数清除一批相关的位,然后第一参数将其中一位设置为1 。上面的表列出了用作setf()的第二参数的常量名称、可用作第一参数的相关常量以及它们的含义。左对齐意味着将值放在字段的左端,右对齐表示将值放在字段的右端。内部对齐表示将符号或基数前缀放在字段左侧,余下的数字放在字段的右侧。
  在C++标准中,定点表示法和科学表示法都有下面的特征:
    *精度值得是小数位数,而不是总位数;
    *显示末尾的0;
  setf()函数是ios_base类的一个成员函数。由于这个类是ostream的基类,因此可以使用cout对象来调用该函数。例如:
    std::cout.setf(ios_base::hex, ios_base::basefield);
  (7)标准控制符
   使用setf()不是进行格式化的、对用户最为友好的方法,C++提供了多个控制符,能够调用setf(),并且自动提供正确的参数。例如,下面的语句打开左对齐和定点选项:
    cout && left &&
  下面的表列出来这些控制符:
setf(ios_base::boolalpha)
noboolalpha
unsetf(ios_base::boolalpha)
setf(ios_base::showbase)
noshowbase
unsetf(ios_base::showbase)
setf(ios_base::showpoint)
noshowpoint
unsetf(ios_base::showpoint)
setf(ios_base::showpos)
unsetf(ios_base::showpos)
setf(ios_base::uppercase)
nouppercase
unsetf(ios_base::uppercase)
setf(ios_base::internal, ios_base::adjustfield)
setf(ios_base::left, ios_base::adjustfield)
setf(ios_base::right, ios_base::adjustfield)
setf(ios_base::dec, ios_base::basefield)
setf(ios_base::oct, ios_base::basefield)
setf(ios_base::hex, ios_base::basefield)
setf(ios_base::fixed, ios_base::floatfield)
scientific
setf(ios_base::scientific, ios_base::floatfield)
  (8)头文件iomanip      
&  使用iostream工具来设置一些格式值不太方便。位简化工作,C++在头文件iomanip中提供了一些控制符,它们能够提供前面讨论过的服务,但表示起来更方便。3个最常用的控制符分别是setprecision()、setfill()、setw(),它们分别用来设置精度、填充字符和字段宽度。与前面讨论的控制符不同,这3个控制符带参数。setprecision()接受一个指定精度的整数参数;setfill()空字符接受一个指定填充字符的char参数;setw()控制符接受一个指定字段宽度的整数参数。由于它们都是控制符,因此可以用cout语句连接起来。
1 #include &iostream&
2 #include &iomanip&
3 #include &cmath&
5 int main()
using namespace
cout && right && fixed;
cout && setw(6) && "N" && setw(14) && "square root"
&& setw(15) && "fourth root\n";
for (int n = 10; n &= 100; n += 10) {
root = sqrt(double(n));
cout && setw(6) && setfill('.') && n && setfill(' ')
&& setw(12) && setprecision(3) && root
&& setw(14) && setprecision(4) && sqrt(root)
22 输出结果:
square root
fourth root
三、使用cin进行输入
  cin对象将标准输入表示为字节流。通常情况下,通过键盘来生成这种字符流。cin对象根据接收值的变量的类型,使用其方法将字符序列转换为所需的类型。
  通常,可以这样使用cin:
    cin && value_
  其中,value_holder为存储输入的内存单元,它可以是变量、引用、被解除引用的指针,也可以是类或结构的成员。cin解释输入的方式取决于value_holder的数据类型。istream类(在iostream头文件中定义)重载了抽取运算符&&,使之能够识别下面这些基本类型:
    *  signed char &
    *  unsigned char &
    *  char &
    *  short &
    *  unsigned short &
    *  int &
    *  unsigned int &
    *  long &
    *  unsigned long &
    *  long long & (C++11)
    *  unsigned long long & (C++11)
    *  float &
    *  double &
    *  long double &
  这些运算符函数被称为格式化输入函数(formatted input functiongs),因为它们可以将输入转换为目标指定的格式。
  典型的运算符函数的原型如下:
    istream & operator&&(int &);
  参数和返回值都是引用。引用参数意味着下面的语句将导致operator&&()函数处理变量staff_size本身,而不是像常规参数那样处理它的副本:
    cin && staff_
  由于参数类型是引用,因此cin能够直接修改用作参数的变量的值。抽取运算符将字符输入转换为指定类型的值。例如,假设staff_size的类型为int,则编译器将:    
    cin && staff_
    与下面的原型匹配:
    istream & operator&&(int &);
  并且,可以将hex、dec和oct控制符与cin一起使用,来制定将整数输入解释为十六进制、十进制和八进制格式。
  istream类还为下列字符指针类型重载了&&抽取运算符:
    *  signed char *;
    *  char *;
    *  unsigned char * 。
  对于这种类型的参数,抽取运算符将读取输入中的下一个单词,将它放置到指定的地址,并加上一个空值字符,使之成为一个字符串。
  每个抽取运算符都返回调用对象的引用,这使得能够将输入拼接起来,就像拼接输出那样。
  3.1 cin&&如何检查输入
  不同版本的抽取运算符查看输入流的方法是相通的。它们跳过空白(空格、换行符和制表符),直到遇到非空白字符。即使对于单字符模式(参数类型为char、unsigned char或signed char),情况也是如此,但对于C语言的字符输入函数,情况并非如此。在单字符模式下,&&运算符将读取该字符,将它放置到指定的位置。在其它模式下,&&运算符将读取一个指定类型的数据。也就是说,它读取从非空白字符开始,到与目标类型不匹配的第一个字符之间的全部内容。
  3.2 流状态
  cin或cout对象包含一个描述流状态(stream state)的数据成员(从ios_base类那里继承来的)。流状态(被定义为iostate类型,而iostate是一种bitmask类型)由3个ios_base元素组成:eofbit、badbit或failbit,其中每个元素都是一位,可以是1(设置)或0(清除)。当cin操作到达文件末尾时,它将设置eofbit;当cin操作未能读取到预期的字符时,它将设置failbit。I/O失败(如试图读取不可访问的文件或试图写入写保护的磁盘),也可能将failbit设置为1 。在一些无法诊断的失败破坏流时,badbit元素将被设置(实现没有必要就哪些情况下设置failbit,哪些情况下设置badbit达成一致)。当全部3个状态位设置为0时,说明一切顺利。程序可以检查流状态,并使用这种信息决定下一步做什么。
如果到达文件尾,则设置为1
如果流被破坏,则设置1;例如,文件读取错误
如果输入操作未能读取预期的字符或输出操作没有写入预期的字符,则设置为1
另一种表示0的方法
如果流可以使用(所有的位都被清除),则返回true
如果eofbit被设置,则返回true
如果badbit被设置,则返回true
如果badbit和failbit被设置,则返回true
返回流状态
exceptions()
返回一个位掩码,指出哪些标记号导致异常被引发
exceptions(isostate ex)
设置哪些状态将导致clear()引发异常;例如,如果ex是eofbit,则如果eofbit被设置,clear()将引发异常
clear(iostate ex)
将流状态设置为s;s的默认值为0(goodbit);如果(restate() & exceptions())!= 0,则引发异常basic_ios::failure
setstate(iostate s)
调用clear(rdstate() | s)。这将设置与s中设置的位对应的流状态位,其他流状态位保持不变
  (1)设置状态
   clear()和setstate()很相似。它们都充值状态,但采取的方式不同。clear()方法将状态设置为它的参数。因此,下面的调用将使用默认参数0,这将清除全部3个状态位(eofbit,badbit和failbit):
      clear ();
   同样,下面的调用将设置eofbit;也就是说,eofbit被设置,另外两个状态位被清除:
      clear(eofbit);
  而setstate()方法只影响其参数中已设置的位。因此,下面的调用将设置eofbit,而不会影响其他位:
      setstate(eofbit);
    因此,如果failbit被设置,则仍将被设置;
  setstate()的主要用途是为输入和输出函数提供一种修改状态的途径。
  (2)I/O异常
  exceptions()方法返回一个字段,它包含3位,分别对应eofbit、failbit和badbit。修改流状态涉及clear()或setstate(),这都将使用clear()。修改流状态后,clear()方法将当前的流状态与exceptions()返回的值进行比较。如果在返回值中某一位被设置,而当前状态中的对应位也被设置,则clear()将引发ios_base::failure异常。如果两个值都设置了badbit,将发生这种情况。如果exceptions()返回goodbit,则不会引发任何异常。ios_base::failure异常类是从std::exception类派生而来的,因此包含一个what()方法。
  exceptions()的默认设置为goodbit,也就是说,没有引发异常。但重载的exceptions(iostate)函数使得能够控制其行为:
    cin.exceptions(badbit); &//设置badbit位,使其能够抛出异常
  为运算符OR使得能够指定多位。例如,如果badbit或eofbit随后被设置,下面的语句将引发异常:
    cin.exceptions(badbit | eofbit);
1 #include &iostream&
3 int main()
using namespace
cin.exceptions(ios_base::badbit | ios_base::failbit);
while (cin && num) {
cout && num &&
} catch (ios_base::failure & ex) {
cout && ex.what();
19 输出结果:
23 ios_base::clear: unspecified iostream_category error
  (3)流状态的影响
&  只有在流状态良好(所有的位都被清除)的情况下,下面的测试才返回true:
    while(cin && input);
  如果测试失败,可以使用诸如eof()、bad()和fail()等成员函数来判断可能的原因。
1 #include &iostream&
3 int main()
using namespace
while (1) {
if (cin.eof()) {
cin.clear();
cout && "输入结束\n";
if (cin.bad()) {
cin.clear();
cout && "不好的输入\n";
if (cin.fail()) {
cin.clear();
cout && "糟糕的输入\n";
cout && num &&
30 输出结果:
38 糟糕的输入
  设置流状态位有一个非常重要的后果:流将对后面的输入或输出关闭,直到位被清除。
&  如果希望程序在流状态位被设置以后能够正确读取后面的输入,则必须讲流状态设置为良好。这可以通过调用clear()方法来实现。但是,这还不足以重新设置流状态。导致输入循环终止的不匹配输入仍留在输入队列中,程序必须跳过它。一种方法是一直读取字符,直到到达空白为止。isspace()函数是一个cctype函数,它在参数是空白字符时返回true。另一种方法是,丢弃行中的剩余部分,而不仅仅是下一个单词:
    while(cin.get() != '\n')
      
  假设循环是由于到达文件尾或者由于硬件故障而终止的,可以使用fail()方法来检查假设是否正确,来修复问题。由于历史原因,fail()在failbit或eofbit被设置时返回true,因此代码必须排除后一种情况。下面是一个排除这种情况的例子:
while (cin && input) {
cout && "最后一个输入的值 = " && input &&
cout && "Sum = " && sum &&
if (cin.fail() && !cin.eof()) {
cin.clear();
while (!isspace(cin.get())) {
cout && "不能继续\n";
cout && "请重新输入数据:\n";
  3.3 &其他istream 类方法
  get()和getline()方法提供下面的输入功能:
    *方法get(char &)和get(void)提供不跳过空白的单字符输入功能;
    *函数get(char *, int, char)和getline(char *, int, char)在默认情况下读取整行而不是一个单词。
  它们被称为非格式化输入函数,因为它们只读取字符输入,而不会跳过空白,也不进行数据转换。
  来看一下istream类的这两组成员函数。
  (1)单字符输入
  在使用char参数或没有参数的情况下,get()方法读取下一个输入字符,即使该字符是空格、制表符或换行符。get(char & ch)版本将输入字符赋给其参数,而get(void)版本将输入字符转换为整型(通常是int),并将其返回。
   1)成员函数get(char &)
  先来看get(char &)。假设程序中包含如下循环:
1 #include &iostream&
3 int main()
using namespace
int ct = 0;
cin.get(ch);
while (ch != '\n') {
cin.get(ch);
cout && ct &&
19 输出结果:
20 I C++ clearly //键盘输入
21 I C++ clearly13
  get(char &)成员函数返回一个指向用于调用它的istream对象的引用,这意味着可以拼接get(char &)后面的其他抽取。
  如果cin.get(char &)到达文件尾,它都不会给其参数赋值。另外,该方法还调用setstate(failbit),导致cin的测试结果为false:
    while(cin.get(ch)){
      //。。。。
  只要存在有效输入,cin.get(ch)的返回值都将是cin,此时的判定结果为true,因此循环将继续。到达文件结尾时,返回值判定为false,循环终止。
  2)成员函数get(void)
  get(void)成员函数还读取空白,但使用返回值来将输入传递给程序。
  get(void)成员函数的返回类型为int(或某种更大的整型,这取决于字符集和区域)。
  到达文件尾后,cin.get(void)都将返回值EOF&&头文件iostream提供的一个符号常量。这种设计特性使得可以这样来读取输入:
    while((ch = cin.get()) != EOF){
      //...
  这里应该将ch的类型声明为int,而不是char,因为值EOF可能无法使用char来表示。
  下面的表对单字符输入函数的特性进行了总结:
cin.get(ch)
ch=cin.get()
传输输入字符的方法
传给参数ch
将函数返回值赋给ch
字符输入时函数的返回值
指向istream对象的引用
字符编码(int值)
到达文件尾时函数的返回值
转换为false    
&  (2)采用哪种单字符输入形式
  假设可以选择使用&&、get(char &)或get(void),应使用哪一个呢?
  首先,应确定是否希望跳过空白。如果跳过空白更方便,则使用抽取运算符&&。
  如果希望程序检查每个字符,请使用get()方法。在get()方法中,get(char &)的接口更佳。get(void)的主要优点是,它与标准C语言中的getchar()函数极其类似,这意味着可以通过包含iostream(而不是stdio.h),并用cin.get()替换所有的getchar(),用cout.put(ch)替换所有的putchar(ch),来将C程序转换为C++程序。
  (3)字符串输入:getline(),get()和ignore()
   getline()成员函数和get()的字符串读取版本都读取字符串,它们的函数特征标相同(这是从更为通用的模板声明简化而来的):
      istream & get(char *, int, char);
      istream & get(char *, int);
      istream & getline(char *, int, char);
      istream & getline(char *, int);
  第一个参数是用于放置输入字符串的内存单元的地址。第二个参数比要读取的最大字符数大1(额外的一个字符用于存储结尾的空字符,以便将输入存储为一个字符串)。第三个参数制定用作分节符的字符,只有两个参数的版本将换行符用作分界符。上述函数都在读取最大数目的字符或遇到换行符后为止。
  例如,下面的代码将字符输入读取到字符数组line中:    char line[50];
    cin.get(line, 50);
  cin.get()函数将在到达第49个字符或者遇到换行符(默认情况)后停止将输入读取到数组中。get()和getline()之间的主要区别在于,get()将换行符留在输入流中,这样接下来的输入操作首先看到是将是换行符,而getline()抽取并丢弃输入流中的换行符。
1 #include &iostream&
3 int main()
using namespace
char line[50];
cin.get(line,50);
if (cin.get() == '\n') {
cout && "\"\\n\"\n";
cin.getline(line, 50);
if (cin.get() == '\n') {
cout && "\"\\n\"\n";
19 输出结果:
20 Good morning! //输入
22 Good evening! //输入
23 1 //输入
  接下来看接受三个参数的版本,第三个参数用于制定分界符。遇到分节符后,输入将停止,即使还未读取最大数目的字符。因此,在默认情况下,如果在读取指定数目的字符之前到达行尾,这两种方法都将停止读取输入。和默认情况一样,get()将分界字符留在输入队列中,而getline()不保留。
getline():
1 #include &iostream&
3 int main()
using namespace
char line[50];
cin.getline(line, 50, 'a');
cout && line &&
while (cin.get(ch)) {
17 输出结果:
18 heart //键盘输入
1 #include &iostream&
3 int main()
using namespace
char line[50];
cin.get(line, 50, 'a');
cout && line &&
while (cin.get(ch)) {
16 输出结果:
17 heart //键盘输入
  ignore()成员函数接受两个参数:一个是数字,指定读取的最大字符数;另一个是字符,用作输入分节符。例如下面的函数调用读取并丢弃接下来的255个字符或直到到达第一个换行符:
    cin.ignore(255,'\n');
  原型为两个参数的提供的默认值为1和EOF,该函数的返回类型为istream &:
    istream & ignore(int = 1, int = EOF);
  默认参数值EOF导致ignore()读取指定数目的字符或读取到文件尾。
  该函数返回调用对象,这使得能够拼接函数调用。
  (4)意外字符串输入
  get(char *, int)和getline()的某些输入形式的某些输入将影响流状态。与其他输入函数一样,这两个函数在遇到文件尾时将设置eofbit,遇到流被破坏时将设置badbit。另外两种特殊情况是无输入以及输入到达或超过函数调用指定的最大字符数。下面来看这些情况。
  对于上述两个方法,如果不能抽取字符,它们将把空字符放置到输入字符串中,并使setstate()设置failbit。方法在什么时候无法抽取字符呢?一种可能情况是输入方法立刻到达文件尾。对于get(char *, int)来说,另一种可能是输入了一个空行:
    char temp[80];
    while(cin.get(temp, 80))
      ...
  有意思的是,空行并不会导致getline()设置failbit。这是因为getline()仍将抽取换行符,虽然不会存储它。如果希望getline()在遇到空行时终止循环,则可以这样编写:
    char temp[80];
    while(cin.getline(temp, 80) && temp[0] != '\0')
  现在假设输入队列中的字符数等于或超过了输入方法指定的最大字符数。首先来看getline()和下面的代码:
    char temp[30];
    while(cin.getline(temp, 30))
  getline()方法将从输入队列中读取字符,将它们放到temp数组的元素中,直到(按测试顺序)到达文件尾、将要读取的字符是换行符或存储了29个字符为止。如果到达文件尾,则设置eofbit;如果将要读取的字符是换行符,则该字符被读取并丢弃;如果读取了29个字符,并且下一个字符不是换行符,则设置failbit。因此,包含30个或更多字符的输入行将终止输入。
  现在来看get(char *, int)方法。它首先测试字符数,然后测试是否为文件尾以及下一个字符是否是换行符。如果他读取了最大数目的字符,则不设置failbit标记。然而,由此可以知道终止读取是否是由于输入字符过多引起的。可以用peek()来查看下一个输入字符。如果它是换行符,则说明get()已读取了整行;如果不是换行符,则说明get()是在到达行尾前停止的。这种技术对getline()不适用,因为getline()读取并丢弃换行符,因此查看下一个字符无法知道任何情况。然而,如果使用的是get(),则可以知道是否读取了整个一行。下面的表总结了这些行为:
getline(char *, int)
如果没有读取任何字符(但换行符被视为读取了一个字符),则设置failbit;
如果读取了最大数目的字符,且行中还有其他字符,则设置failbit;
get(char *, int)
如果没有读取任何字符,则设置failbit;
&  3.4 其他istream方法
  除了前面讨论过的外,其他istream方法包括read()、peek()、gcount()和putback()。read()函数读取指定数目的字节,并将它们存储在指定的位置中。例如,下面的语句从标准输入中读取144个字符,并将他们存储在gross数组中:
    char gross[144];
    cin.read(gross, 144);
  与get()和getline()不同的是,read()不会在输入后加上空字符,因此不能将输入转换为字符串。read()方法不是专为键盘输入设计的,它最常与ostream::write()函数结合使用,来完成文件输入和输出。该函数的返回类型为istream &,因此可以像下面这样将它们拼接起来:
    char gross[144];
    char score[20];
    cin.read(gross, 144).read(score, 20);
  peek()函数返回输入中的下一个字符,但不抽取输入流中的字符。也就是说,它是的能够查看下一个字符。假设要读取输入,直到遇到换行符或句点,则可以使用peek()来查看输入流中的下一个字符,以此来判断是否继续读取:
      char great_input[80];
      
      int i = 0;
      while((ch = cin.peek()) != '.' && ch != '\n')
        cin.get(great_input[i++]);
      great_input[i] = '\0';
  gcount()方法返回最后一个非格式化抽取方法读取的字符数。这意味着字符是由get()、getline()、ignore()或read()方法读取的,不是由抽取运算符(&&)读取的,抽取运算符对输入进行格式化,使之与特定的数据类型匹配。例如,假设使用cin.get(myarray, 80)将一行读入myarray数组中,并想知道读取了多少个字符,则可以使用strlen()函数来计算数组中的字符数,这种方法比使用cin.gcount()计算从输入流中读取了多少字符的速度要快。
  putback()函数将一个字符插入到输入字符串中,被插入的字符将是下一条输入语句读取的第一个字符。putback()方法接受一个char参数&&要插入的字符,其返回类型为istream &,这使得可以将该函数调用与其他istream方法拼接起来。使用peek()的效果相当于先使用get()读取一个字符,然后使用putback()将该字符放回到输入流中。然而,putback()允许将字符放到不是刚才读取的位置。
四、文件输入和输出
  大多数计算机程序都使用文件。文件本身是存储在某种设备(磁带、光盘、软盘或硬盘)上的一系列字节。通常,操作系统管理文件,跟踪它们的位置、大小、创建时间等。除非在操作系统级别上编程,否则通常不必担心这些事情。需要的只是将程序与文件相连的途径、让程序读取文件内容的途径以及让程序创建和写入文件的途径。重定向可以提供一些文件支持,但它比显式程序中的文件I/O的局限性更大。 另外重定向来自操作系统,而非C++,因此并非所有系统都有这样的功能。
  C++I/O类软件包处理文件输入和输出的方式与处理标准输入和输出的方式非常相似。要写入文件,需要创建一个ofstream对象,并使用ostream方法,如&&插入运算符或 write()。要读取文件,需要创建一个ifstream对象,并使用istream方法,如&&抽取运算符或get()。
<span style="color: # 简单的文件I/O
&  要让程序写入文件,必须这样做:
    1)创建一个ofstream对象来管理输出流;
    2)将该对象与特定的文件关联起来;
    3)以使用cout的方式使用该对象,唯一的区别是输出将写入文件,而不是屏幕。
  要完成上述任务,首先应包含文件fstream文件。对于大多数实现来说,包含该文件便自动包括iostream文件,因此不必显式包含iostream文件。然后声明一个ofstream对象:
  对象名可以是任意有效的C++名称。
  接下来,必须将这个对象与特定的文件关联起来。为此,可以使用open()方法。例如,假设要打开jar.txt进行输出,则可以这样做:
    fout.open("jar.txt");
  可以使用另一个构造函数完成这两步(创建对象和关联到文件)合并成一条语句:
    ofstream fout("jar.txt");
  然后,以使用cout的方式使用ofstream对象。
  由于ostream是ofstream类的基类,因此可以使用所有的ostream方法,包括各种插入运算符定义、格式化方法和控制符。ofstream类使用被缓冲的输出,因此程序在创建ofstream对象时,将为输出缓冲区分配空间。如果创建两个ofstream对象,程序就创建两个缓冲区,每个对象一个。ofstream对象从程序那里逐字节地收集输出,当缓冲区填满后,它便将缓冲区内容一同传输给目标文件。由于磁盘驱动器被设计称以大块的方式传输数据,而不是逐字节地传输,因此通过缓冲区可以大大提高从程序到文件传输数据的速度。
  以这种方式打开文件来进行输出时,如果没有这样的文件,将创建一个新文件;如果有这样的文件,则打开文件将清空文件,输出将进入到一个空文件中。
  警告:以默认房事打开文件进行输出将自动把文件的长度截短为零,这相当于删除已有的内容。
  读取文件的要求与写入文件相似:
    1)创建一个ifstream对象来管理输入流;
    2)将该对象与特定的文件关联起来;
    3)以使用cin的方式使用该对象。
  上述读文件的步骤类似于写文件。首先,要包含fstream。然后声明一个ifstream对象,将它与文件名关联起来。
  可以像使用cin那样使用ifstream对象。
  输入和输出一样,也是被缓冲的;与输出一样,通过缓冲,传输数据的速度比逐字节传输要快的多。
  当输入和输出流对象过期(如程序终止)时,到文件的连接将自动关闭。另外,也可以使用close()方法来显式地关闭到文件的连接:
    fout.close();
  关闭这样的连接并不会删除流,而只是断开流到文件的连接。然而,流管理装置仍被保留。例如,ifstream的对象与它管理的输入缓冲区仍然存在。可以将流连接到同一个文件或另一个文件。
  下面的例子要求输入文件名,然后创建一个名称为输入名的文件,将一些信息写入到高文件中,然后关闭该文件。关闭文件将刷新缓冲区,从而确保文件被刷新。然后程序打开该文件,读取并显示其内容。注意,该程序以使用cin和cout的方式使用fin和fout。另外,该程序将文件名读取到一个string对象中,然后使用方法c_str()来给ofstream和ifstream的构造函数提供一个C- 风格字符串参数。
1 #include &iostream&
2 #include &fstream&
4 int main()
using namespace
cout && "Enter name for new file:\n";
filename = "/Users/xheart/Desktop/" +
ofstream fout(filename.c_str());
fout && "For your eyes only!\n";
cout && "Enter your secret number:\n";
fout && "Your secret number is " && secret &&
fout.close();
ifstream fin(filename.c_str());
cout && "Here are the contents of " && filename && ": \n";
while (fin.get(ch)) {
cout && "Done\n";
fin.close();
31 输出结果:
32 Enter name for new file:
33 myNewFile //键盘输入
34 Enter your secret number:
//键盘输入
36 Here are the contents of /Users/xheart/Desktop/myNewFile:
37 For your eyes only!
38 Your secret number is 1991
<span style="color: # 流状态检查和is_open()
  C++文件流类从ios_base类那里继承了一个流状态成员。正如前面指出的,该成员存储了指出流状态的信息:一切顺利、已到达文件、I/O操作失败等。如果一切顺利,则流状态为零(没有消息就是好消息)。其他状态都是通过将指定位设置为1来记录的。文件流还继承了ios_base类中报告流状态的方法。可以通过检查流状态来判断最后一个流操作是否成功。对于流文件,这包括检查试图打开文件时是否成功。
  C++实现提供了一种更好的检查文件是否被打开的方法&&is_open()方法:
    if(!fin.is_open()){
      //...
  警告:以前,检查文件是否成功打开的常见方式如下:
    if(fin.fail()) ... //failed to open
    if(!fin.good()) ... //failed to open
    if(!fin) ... // failed to open
  fin对象被用于测试条件中时,如果fin.good()为false,将被转换为false;否则将被转换为true。因此上面的三种方式等价。然而,这些测试无法检查到这样一种情形:试图以不合适的文件模式打开文件时失败。方法is_open()能够检测到这种错误以及good()能够检测到的错误。然而,老式C++实现没有is_open()。
<span style="color: # 打开多个文件
  程序可能需要打开多个文件,打开多个文件的策略取决于他们被如何使用。如果需要同时打开两个文件,则必须为每个文件创建一个流。可以同时打开的文件数取决于操作系统。
  然而,可能要依次处理一组文件。在这种情况下,可以打开一个流,并将它依次关联到各个文件。这在节省计算机资源方面,比为每个文件打开一个流的效率高。使用这种方法,需要声明一个ifstream对象(不必对他进行初始化),然后使用open()方法将这个流与文件关联起来。例如,下面依次读取两个文件的代码:
    fin.open("fat.txt");
    ...
    fin.close();
    fin.clear();
    fin.open("rat.txt");
    ...
    fin.close();
<span style="color: # &命令行处理技术
  文件处理程序通常使用命令行参数来指定文件。命令行参数是用户在输入命令时,在命令行中输入的参数。例如,在UNIX或Linux系统中计算文件包含的字数,可以在命令行提示符下输入下面的命令:
    wc report1 report2 report3
  其中,wc是程序名,report1,report2和report3是作为命令行参数传递给程序的文件名。
  C++有一种让在命令环境中运行的程序能够访问命令行参数的机制,方法是使用下面的main()函数:
    int main(int argc, char *argv[])
  argc为命令行中的参数个数,其中包括命令名本身。argv变量为一个指针,它指向一个指向char的指针。这过于抽象,但可以将argv看作一个指针数组,其中的指针指向命令行参数,argv[0]z是一个指针,指向存储第一个命令行参数的字符串的第一个字符,依此类推。也就是说,argv[0]是命令行中的第一个字符串,依此类推。例如,假设有下面的命令行:
    wc report1 report2 report3
  则argc为4,argv[0]为wc,argv[1]为report1,依此类推。下面的循环将把每个命令行参数分别打印在单独一行上:
    for(int i = 1; i & i++&)
      cout && argv[i] &&
  以i = 1开头将只打印命令行参数;以i=0开头同时打印命令名。
  当然,命令行参数与命令行操作系统(Windows命令提示模式、UNIX和Linux)紧密相关。其他程序也可能允许使用命令行参数。
    *很多Windows IDE(继承开发环境)都有一个提供命令行参数的选项。通常,必须选择一系列菜单,才能打开一个可以输入命令行参数的对话框。具体的步骤随厂商和升级版本而异,因此请查看文档。
    *很多Windows IDE都可以生成可执行文件,这些文件能够在Windows命令提示符模式下进行。
<span style="color: # &文件模式
  文件模式描述的是文件将被如何使用:读、写、追加等。将流与文件关联时(无论是使用文件名初始化文件流对象,还是使用open()方法),都可以提供指定文件模式的第二个参数:
    ifstream fin("banjo", mode1);
    ofstream fout();
    fout.open("harp", mode2);
  ios_base类定义了一个openmodel类型,用于表示模式;与fmtflags和iostate类型一样,它也是一种bitmask类型(以前,其类型为int)。可以选择ios_base类中定义的多个常量来指定模式,下面的表列出了这些常量及其含义。C++文件I/O做了一些改动,以便与ANSI C文件I/O兼容。
ios_base::in
打开文件,以便读取
ios_base::out
打开文件,以便写入
ios_base::ate
打开文件,并移到文件尾
ios_base::app
追加到文件尾
ios_base::trunc
如果文件存在,则截短文件
ios_base::binary
二进制文件
&  ifstream和ofstream的构造函数以及open()方法都接受两个参数,这些类成员函数的原型为第二个参数提供了默认值。ifstream::open()方法和构造函数用ios_base::in作为模式参数的默认值,而ofstream::open()方法和构造函数用ios_base::out|ios_base::trunc作为默认值。位运算符OR(|)用于将两个位值合并成一个可用于设置两个位的值。fstream不提供默认的模式值,因此在创建这种类的对象时,必须显式地提供模式。
  注意,ios_base::trunc标记意味着打开已有的文件,以接收程序输出时将被截短;也就是说,其以前内容被删除。
  老式C++时间之间可能有一些差异。例如,有些实现允许省略ios_base::out,有些则不允许。如果不是用默认模式,则最安全的方法是显式地提供所有的模式元素。有些编译器不支持上面表中的所有选项,有些则提供了表中没有列出的其他选项。但是C++标准提供了更高的统一性。
  标准C++根据ANSI C标准I/O定义了部分文件I/O。实现像下面这样的C++语句:    ifstream fin(filename, c++mode);
  就像它使用C的fopen()函数一样:
    fopen(filename, cmode);
  其中,c++mode是一个openmode值,如ios_base::in;而cmode是相应的C模式字符串,如&r&。下面的表列出了C++模式和C模式的对应关系。注意,ios_base::out本身将导致文件被截短,但与ios_base::in一起使用时,不会导致文件被截短。没有列出的组合,例如ios_base::in|ios_base::trunc,将禁止文件被打开。is_open()方法用于检查这种故障。
ios_base::in
打开以读取
ios_base::out
等价于ios_base::out|ios_base::trunc
ios_base::out | ios_base::trunc
打开以写入,如果已存在,则截短文件
ios_base::out | ios_base::app
打开以写入,只追加
ios_base::in | ios_base::out
打开以读写,在文件允许的位置写入
ios_base::in | ios_base::out | ios_base::trunc
打开以读写,如果已存在,则首先截短文件
c++mode | ios_base::binary
以C++mode(或相应的cmode)和二进制模式打开,例如:ios_base::in | ios_base::binary或&rb&
c++mode | ios_base::ate
以指定的模式打开,并移到文件尾。C使用一个独立的函数调用,而不是编码模式。例如:ios_base::in | ios_base::ate被转换为"r"模式和C函数调用fseek(file, 0, SEEK_END)
  注意,ios_base::ate和ios_base::app都将文件指针指向打开的文件尾。二者的区别在于,ios_base::app模式只允许将数据添加到文件尾,而ios_base::ate模式将指针放到文件尾。
  显然,各种模式的组合很多,我们将介绍几种有代表性的组合。
  (1)追加文件
  来看一个在文件尾追加数据的程序。该程序维护一个存储来客清单的文件。该程序显示文件当前的内容(如果有的话)。尝试打开文件后,它使用is_open()方法检查该文件是否存在。接下来,程序以ios_base::app模式代开文件,进行输入。然后,它请求用户从键盘输入,并将其添加到文件中。最后,程序显示修订后的文件内容。下面的程序清单演示了如何实现这些目标,请注意程序是如何使用is_open()方法检查文件是否被成功打开的。
1 #include &iostream&
2 #include &fstream&
3 #include &string&
4 #include &cstdlib&
6 const char * file = "/Users/xheart/Desktop/guests.txt";
7 int main(int argc, char * argv[])
using namespace
//show initial contents
fin.open(file);
if(fin.is_open()){
cout && "Here are the current contents of the " && file && " file: \n";
while (fin.get(ch)) {
fin.close();
//add new names
ofstream fout(file,ios_base::out | ios_base::app);
if (!fout.is_open()) {
cerr && "Can't open " && file && " file for output.\n";
exit(EXIT_FAILURE);
cout && "Enter guest names (enter a blank line to quit):\n";
while (getline(cin, name) && name.size() & 0) {
fout && name.c_str() &&
fout.close();
//show revised file
fin.clear();
fin.open(file);
if (fin.is_open()) {
cout && "Here are the new contents of the " && file && " file:\n";
while (fin.get(ch)) {
fin.close();
cout && "Done.\n";
50 输出结果:
51 Here are the current contents of the /Users/xheart/Desktop/guests.txt file:
56 Enter guest names (enter a blank line to quit):
60 Here are the new contents of the /Users/xheart/Desktop/guests.txt file:
  (2)二进制文件
  将数据存储在文件中时,可以将其存储为文本格式或二进制格式。文本格式指的是将所有内容(甚至数字)都存为文本。例如,以文本格式存储值-2.时,将存储该数字包含的13个字符。这需要将浮点数的计算机内部表示转换为字符格式,这正是&&插入运算符完成的工作。另一方面,二进制格式指的是存储值的计算机内部表示。也就是说,计算机不是存储字符,而是存储这个值的64位double表示。对于字符来说,二进制表示与文本表示是一样的,即字符的ASCII码的二进制表示。对于数字来说,二进制表示与文本表示有很大的差别。
  每种格式都有自己的优点。文本格式便于读取,可以使用编辑器或字处理器来读取和编辑文本文件,可以很方便地将文本文件从一个计算机系统传输到另一个计算机系统。二进制格式对于数字来说比较精确,因为他存储的是值的内部表示,因此不会有转换误差或舍入误差。以二进制格式保存数据的速度更快,因为不需要转换,并可以大块地存储数据。二进制格式通常占用的空间较小,这取决于数据的特征。然而,如果另一个系统使用另一种内部表示,则可能无法将数据传输给该系统。同一系统上不同的编译器也可能使用不同的内部结构布局表示。在这种情况下,必须编写一个将一种数据转换成另一种的程序。
  来看一个例子。考虑下面的结构定义和声明:
    const int LIM = 20;
    struct planet{
      char name[LIM]; &//name of planet
       &//its population
       &    //its acceleration of gravity
    };
    planet p1;
  要将结构p1的内容以文本格式保存,可以这样做:
    ofstream fout("Planets.dat",ios_base::out | ios_base::app);
    fout && p1.name && " " && p1.population && " " && p1.g && "\n";
  必须使用成员运算符显式地提供每个结构成员,还必须将相邻的数据分隔开,以便区分。如果结构有30个成员,则这项工作将很乏味。
  要使用二进制格式存储相同的信息,可以这样做:
    ofstream fout("Planets.dat", ios_base::out | ios_base::app | ios_base::binary);
    fout.write((char *) &p1, sizeof(p1));
  上述代码使用计算机的内部数据表示,将整个结构作为一个整体保存。不能将该文件作为文本读取,但与文本相比,信息的保存更为紧凑、精确。它确实便于键入代码,。这种方法做了两个修改:
    *使用二进制文件格式;
    *使用成员函数write().
  下面来详细的介绍这两项修改。
  有些系统(如Windows)支持两种文件格式:文本格式和二进制格式。如果要用二进制格式保存数据,应使用二进制文件格式。在C++中,可以将文件模式设置为ios_base::binary常量来完成。
补充:二进制文件和文本文件
   使用二进制文件模式时,程序将数据从内存传输给文件(反之亦然)时,将不会发生任何隐藏的转换,二默认的文本模式并非如此。例如,对于Windows文本文件,它们使用两个字符的组合(回车和换行)表示换行符;Macintosh文本文件使用回车来表示换行符;而UNIX和Linux文件使用换行(linefeed)来表示换行符。C++是从UNIX系统上发展而来的,因此也使用换行(linefeed)来表示换行符。为增加可移植性,Windows C++程序在写文本模式文件时,自动将C++换行符转换为回车和换行;Macintosh C++程序在写文件时,将换行符转换为回车。在读取文本文件时,这些程序将本地换行符转换为C++格式。对于二进制数据,文本格式会引起问题,因此double值中间的字节可能与换行符的ASCII码有相同的位模式。另外,在文件尾的检测方式也有区别。因此以二进制格式保存数据时,应使用二进制文件模式(UNIX系统只有一种文件模式,因此对于他来说,二进制模式和文本模式是一样的)。 
  要以二进制格式(而不是文本格式)存储数据,可以使用write()成员函数。这种方法将内存中指定书目的字节复制到文件中。但它只逐字节地复制数据,而不进行任何转换。例如,如果将一个long变量的地址传给它,并命令它复制4个字节,它将复制long值中的4个字节,而不会将他转换为文本。唯一不方便的地方是,必须将地址转换为指向char的指针。也可以使用同样的方式来复制整个planet结构。要获得字节数,可以使用sizeof运算符:
    fout.write((char *)&p1, sizeof(p1));
  这条语句导致程序前往p1的地址,并将开始的36个字节(sizeof(p1)表达式的值)复制到与fout关联的文件中。
  要使用文件恢复信息,请通过一个ifstream对象使用相应的read()方法:
    ifstream fin("planets.dat", ios_base::in | ios_base::binary);
    fin.read((char *)&p1, sizeof(p1));
  这将从文件复制sizeof(p1)个字节到p1结构中。同样的方法也适用于不使用虚函数的类。在这种情况下,只有数据成员被保存,而方法不会被保存。如果类有虚方法,则也将复制隐藏指针(该指针指向虚函数的指针表)。由于下一次运行程序时,虚函数表可能在不同的位置,因此将文件中的旧指针信息复制到对象中,将可能造成混乱。
  提示:read()和write()成员函数的功能是相反的。请使用read()来恢复用write()写入的数据。
  下面的程序清单使用这些方法来创建和读取二进制文件。
1 #include &iostream&
2 #include &fstream&
3 #include &string&
4 #include &cstdlib&
5 #include &iomanip&
7 inline void eatline(){while (std::cin.get() != '\n')continue;}
8 struct planet{
char name[20];
14 const char * file = "/Users/xheart/Desktop/planets.dat";
16 int main(int argc, char * argv[])
using namespace
planet p1;
cout && fixed &&
//show initial contents
fin.open(file,ios_base::in | ios_base::binary);
if (fin.is_open()) {
cout && "Here are the current contents of the " && file && " file:\n";
while (fin.read((char *)&p1, sizeof(p1))) {
cout && setw(20) && p1.name && ": " && setprecision(0) && setw(12) && p1.population && setprecision(2) && setw(6) && p1.g &&
fin.close();
//add new data
ofstream fout(file, ios_base::out | ios_base::app | ios_base::binary);
if (!fout.is_open()) {
cerr && "Can't open " && file && " file for output:\n";
exit(EXIT_FAILURE);
cout && "Enter planet name (enter a blank line to quit):\n";
cin.get(p1.name, 20);
while (p1.name[0] != '\0') {
eatline();
cout && "Enter planetary population:";
cin && p1.
cout && "Enter planet's acceleration of gravity:";
cin && p1.g;
eatline();
fout.write((char *)&p1, sizeof(p1));
cout && "Enter planet name (enter a blank line to quit):\n";
cin.get(p1.name, 20);
fout.close();
//show revised file
fin.clear();
fin.open(file, ios_base::in | ios_base::binary);
if (fin.is_open()) {
cout && "Here are the new contents of the " && file && " file:\n";
while (fin.read((char *)&p1, sizeof(p1))) {
cout && setw(20) && p1.name && ": " && setprecision(0) && setw(12) && p1.population
&& setprecision(2) && setw(6) && p1.g &&
fin.close();
cout && "Done.\n";
70 输出结果:
71 Here are the current contents of the /Users/xheart/Desktop/planets.dat file:
73 Enter planet name (enter a blank line to quit):
74 Jenny's World
75 Enter planetary population:
76 Enter planet's acceleration of gravity:8.93
77 Enter planet name (enter a blank line to quit):
79 Here are the new contents of the /Users/xheart/Desktop/planets.dat file:
Jenny's World:
<span style="color: # &随机存取
  随机存取指的是直接移动(不是依次移动)到文件的任何位置。随机存取常被用于数据库文件,程序维护一个独立的索引文件,该文件指出数据在主数据文件中的位置。这样,程序便可以直接跳到这个位置,读取(还可能修改)其中的数据。如果文件有长度形同的记录组成,这种方法实现起来最简单。
  要实现随机存取,需要创建一个fstream对象。fstream类是从iostream类派生而来的,而后者基于istream喝ostream两个类,因此它继承了它们的方法。它还继承了两个缓冲区,一个用于输入,一个用于输出,并能同步化这两个缓冲区的处理。也就是说,当程序读写文件时,它将协调地移动输入缓冲区的输入指针和输出缓冲区中的输出指针。
  要实现随机存取,首先要回答的是使用哪种文件模式。为读取文件,要使用ios_base::in模式。为执行二进制I/O,需要使用ios_base::binary模式(在某些非标准系统上,可以省略这种模式,事实上,可能必须省略这种模式)。为写入文件,需要ios_base::out或ios_base::app模式。然而,追加模式只允许程序将数据添加到文件尾,文件的其他部分是只读的;也就是说,可以读取原始数据,但不能修改它;要修改数据必须使用ios_base::out。同时使用in模式和out模式将得到读/写模式,因此只需要添加二进制元素即可。如前所述,要使用|运算符来组合模式。因此,需要使用下面的语句:
    finout.open(file, ios}

我要回帖

更多关于 字符串保留两位小数 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信