7. 输入与输出存有几种程序输出的显示方式, 数据能够输出为人可阅读的形式, 还能够被写入文件以供备用, 本章会探讨一些可以使用的方式。7.1. 更复杂的输出格式到此为止, 我们已经学习了两种写入值的方式: 表达式语句, 以及函数。第三种方式是运用文件对象的 write() 方法, 标准输出文件被称作 sys, 具体内容可查看标准库参考。对输出格式展开控制, 并非仅仅是打印以空格加以分隔的值而是还需要更多的方式, 格式化输出涵盖以下几种方法。要是并不要求那种花里胡哨的输出, 仅仅只是想着能够迅速地将变量给显示出来从而用以调试的话, 那么就能够采用, 或者是借助函数把取值转变为字符串。如果函数返回供人阅读的值, 那么就生成适于解释器读取的值有等效语法就按语法, 没有就强制执行。对于没有支持供人阅读展示结果的对象, 返回与之相同的值。一般来讲, 数字、列表或字典等结构的值, 使用这两个函数输出的表现形式是一样的。字符串存在两种不同的表现形式。示例如下 s Hello, world. str(s) Hello, world. repr(s) Hello, world. str(1/7) 0.14285714285714285 x 10 * 3.25 y 200 * 200 s The value of x is repr(x) , and y is repr(y) ... print(s) The value of x is 32.5, and y is 40000... # The repr() of a string adds string quotes and backslashes: ... hello hello, world\n hellos repr(hello) print(hellos) hello, world\n # The argument to repr() may be any Python object: ... repr((x, y, (spam, eggs))) (32.5, 40000, (spam, eggs))有种模块, 它包含类, 这个类, 提供了一种别样的方法, 是把值替换为字符串, 但它使用$x占位符, 并且是用字典的值来进行替换, 然而, 它对于格式控制的支持是比较有限的。7.1.1. 格式化字符串字面值在字符串前面加上前缀 f, 或者加上前缀 F, 简称为 f - 字符串, 借助 {} 这种表达式, 将一个表达式的值增添到字符串里面。格式说明符是可选择的, 它被写在表达式之后, 能够以此更好地把控格式化值的形式。对下例而言会把 pi 四舍五入到小数点后的三位: import math print(fThe value of pi is approximately {math.pi:.3f}.) The value of pi is approximately 3.142.冒号之后传递整数, 以此为该字段设定最小字符宽度, 常常被用于列对齐。 table {Sjoerd: 4127, Jack: 4098, Dcab: 7678} for name, phone in table.items(): ... print(f{name:10} {phone:10d}) ... Sjoerd 4127 Jack 4098 Dcab 7678还有一些用于在格式化之前对值进行转换的修饰符 , !a 会被应用 , !s 会被应用 , !r 会被应用: animals eels print(fMy hovercraft is full of {animals}.) My hovercraft is full of eels. print(fMy hovercraft is full of {animals!r}.) My hovercraft is full of eels.格式规范参考详见参考指南 。7.1.2. 字符串 () 方法方法的基本用法如下所示 print(We are the {} who say {}!.format(knights, Ni)) We are the knights who say Ni!带有花括号以及位于其之内出现着的字符, 此字符叫做格式字段, 格式字段会被运用替换来处理, 替换所用对象要传递给特定方法。花括号期间呈现的数字, 有着特别表示含义, 它表明传递给特定方法的对象, 具体所在的位置。 print({0} and {1}.format(spam, eggs)) spam and eggs print({1} and {0}.format(spam, eggs)) eggs and spam方法中使用关键字参数名引用值。 print(This {food} is {adjective}..format( ... foodspam, adjectiveabsolutely horrible)) This spam is absolutely horrible.位置参数和关键字参数可以任意组合 print(The story of {0}, {1}, and {other}..format(Bill, Manfred, otherGeorg)) The story of Bill, Manfred, and Georg.倘若不打算分拆较为长的格式字符串, 那么最好依据名称去引用变量来展开格式化, 而非按照位置。此项操作能够借助传递字典, 并且运用方括号 来访问键予以达成。 table {Sjoerd: 4127, Jack: 4098, Dcab: 8637678} print(Jack: {0[Jack]:d}; Sjoerd: {0[Sjoerd]:d}; ... Dcab: {0[Dcab]:d}.format(table)) Jack: 4098; Sjoerd: 4127; Dcab: 8637678也能够采用 ** 符号, 将 table 视作传递中的关键字参数。 table {Sjoerd: 4127, Jack: 4098, Dcab: 8637678} print(Jack: {Jack:d}; Sjoerd: {Sjoerd:d}; Dcab: {Dcab:d}.format(**table)) Jack: 4098; Sjoerd: 4127; Dcab: 8637678在同内置函数一块结合起来去使用之际, 此般方式是着实很实用的, 能够返回携带了所有局部变量的字典。例如, 下面的代码, 生成一组整齐的列, 这组列包含给定整数, 以及该整数的平方, 还有该整数的立方。 for x in range(1, 11): ... print({0:2d} {1:3d} {2:4d}.format(x, x*x, x*x*x)) ... 1 1 1 2 4 8 3 9 27 4 16 64 5 25 125 6 36 216 7 49 343 8 64 512 9 81 729 10 100 1000进行字符串格式化的完整概述详见 。7.1.3. 手动格式化字符串下面是使用手动格式化方式实现的同一个平方和立方的表 for x in range(1, 11): ... print(repr(x).rjust(2), repr(x*x).rjust(3), end ) ... # Note use of end on previous line ... print(repr(x*x*x).rjust(4)) ... 1 1 1 2 4 8 3 9 27 4 16 64 5 25 125 6 36 216 7 49 343 8 64 512 9 81 729 10 100 1000留意, 每列相互之间的空白是借助运用 添加上的: 它始终在其参量之间添加上空白字符串对象的方法, 借助在左侧填充空格, 于给定宽度字段当中, 对字符串实施右对齐操作。同类方法存在另外的和其他的。这些方法不会写入任什么内容, 仅仅返回一个全新字符串, 要是输入的字符串太过长, 它们不会去截断字符串, 而是按照原样返回虽说以这种方式会搞乱列布局, 可也比另外一种方法强, 后者在显示值的时候可能不准确要是真的想要截断字符串, 可以运用类似于x.ljust(n)这样的切片操作存在着这样一种方法, 此方法用于在数字字符串的左边部位填充零, 并且它能够对正负号进行识别。 12.zfill(5) 00012 -3.14.zfill(7) -003.14 3.14159265359.zfill(5) 3.141592653597.1.4. 旧式字符串格式化方法在字符串格式化里, % 运算法则也就是求余符号同样是能够被用以操作的。要是给定了 % , 那么 当中所涉的 % 示例, 就会借助零个或者是多个 元素来加以替换。这样的 一种操作, 它被赋予了字符串插值这样的名称。就好比是以下的示例: import math print(The value of pi is approximately %5.3f. % math.pi) The value of pi is approximately 3.142.小节介绍更多相关内容。7.2. 读写文件返回一个, 最为经常被使用的是两个处于特定位置的参数以及一个具备特定性质可当作关键词的参数: open(,, mode, None), 其中模式为None。 f open(workfile, w, encodingutf-8)第一个实参, 是那承载着文件名字的字符串。第二个实参, 竟然是内含描述文件使用方式字符这般的字符串。mode 的值, 有着其中一项为 r 的状况, 它所表达的是文件单单只能读取的性质还有 w 这一项, 表示的是仅能写入的情况, 而且现有同名文件将会被覆盖再有 a 这项, 表示打开文件并且追加内容, 任何写入的数据则会自动添加到文件那末尾之处。r 更是表示打开文件用以读写。mode 实参是属于可选的范畴, 省略的时候其默认值是 r。一般状况之下, 文件是以text mode予以打开的, 这也就意味着, 你从文件里面进行读写的那些字符串皆是以特定的某种编码呈现的。要是未曾指定, 其默认的是与平台相互关联的见某某内容。鉴于UTF - 8是现代实际意义上的标准, 除非您明确晓得自己需要运用一种别样的编码方式, 不然的话则建议使用 utf - 8。于模式的后面添加上一个 b , 能够以 mode去打开文件。二进制模式里面的数据是以某种对象的形态去进行读写的。在二进制模式下打开文件时你不能指定 。当于文本模式里读取文件之际, 默认情形下会将平台特定的行结束符于 Unix 上是 \n, 在 上是 \r\n变换成 \n。于文本模式下写入数据之时, 默认会把 \n 转换回归到平台特定结束符。这样的操作方式于后台进行文件数据修改, 对于文本文件而言不存在问题, 不过却会损害 JPEG 或者 EXE 等二进制文件当中的数据。要留心, 在读写这类文件的时候, 一定得采用二进制模式。于操作文件对象之时, 选用以关键字最优。其优势在于, 待子句体终结之后, 文件能够得以正确关闭, 哪怕触发异常亦可行。并且, 运用with相较等效的代码块简洁不少: with open(workfile, encodingutf-8) as f: ... read_data f.read() # We can check that the file has been automatically closed. f.closed True要是没运用关键字, 那就得调用 f.close() 去关闭文件, 如此便能释放在文件所占用的系统资源。警告调用f.write()之际, 没有用with关键字, 或者没有调用f.close(), 就算程序正常退出, 也存在一种可能性, 会致使f.write()的参数未能全部写入磁盘。倘若借助语句, 或者在调用 f.close() 把文件对象关闭之后, 再度运用该文件对象, 那么将会遭遇失败。 f.close() f.read() Traceback (most recent call last): File , line 1, in ValueError: I/O operation on closed file.7.2.1. 文件对象的方法本节下文中的例子假定已创建 f 文件对象。f.read(size)能够用来读取文件内容, 它会去读取一些数据, 进而返回字符串处于文本模式当中, 或者字节串对象在二进制模式的情况下。size属于可选的数值参数, 当初省略size或者size为负数的时候, 读取并返回的是整个文件的内容, 当文件大小是内存两倍的时候, 就会出现问题。当size取其他值的时候, 读取并返回的是最多size个字符文本模式之中或者size个字节二进制模式之下。如已到达文件末尾f.read() 返回空字符串。 f.read() This is the entire file.\n f.read() f.从文件那儿读取单行数据, 字符串最末尾保留换行符\n, 唯有在文件并非以换行符作为结尾的时候, 文件那最后的一行才会省略换行符。通过这种方式使得返回值清晰又明确, 只要 f.() 返回的是个空字符串那就表明已经抵达了文件末尾, 空行用\n来表示, 此字符串仅仅包含一个换行符。 f.readline() This is the first line of the file.\n f.readline() Second line of the file\n f.readline() 读多行那得从文件里读, 读多行的时候呢可以借助循环, 循环用来遍历整个文件对象。这般操作能够高效地利用内存, 速度较快快得很, 并且代码简单: for line in f: ... print(line, end) ... This is the first line of the file. Second line of the file欲要以列表形式去读取文件之中的所有行, 能够采用 list(f) 或者 f.()。f.write() 将 的相关内容, 书写记录到文件当中嗯哼, 然后返回所写入的字符数量哟。 f.write(This is a test\n) 15在将其写入别的种类的对象以前, 得先把它们转变为字符串, 此为文本模式, 或者转变为字节对象, 这是二进制模式: value (the answer, 42) s str(value) # convert the tuple to string f.write(s) 18f.tell()返还整数, 给出文件对象在此文件里的当前位置, 呈现为二进制模式下自文件起始的字节数, 以及文本模式下意涵难于明确解读的数据。f.seek(, )能够对文件对象的位置予以改变, 借助向参考点纳入计算位置 、此参考点经参数加以指定 值是0之际 表明是从文件起始处实施计算 1意味着运用当前文件所在位置 2表示以文件末尾当作参考点 省略的时候 它的默认值是0 也就是采用文件开头去做参考点。 f open(workfile, rb) f.write(b0123456789abcdef) 16 f.seek(5) # Go to the 6th byte in the file 5 f.read(1) b5 f.seek(-3, 2) # Go to the 3rd byte before the end 13 f.read(1) bd在文本文件, 当其以未使用b的模式字符串打开时, 只通行相对于文件开头来进行搜索, 不过呢, 使用seek(0, 2)这种方式把搜索指向文件末尾是属于例外情况的, 而唯一具备效能的值是可以从f.tell()那里返回的, 又或者是0。其他的值都会引发未定义的行为存在。对于文件对象而言, 其还支持诸如 () 以及 () 等方法, 然而这些方法并不常被使用关于文件对象的全面指南的话, 可详细查看库参考。7.2.2. 使用 保存结构化数据要是从文件那儿写入或者读取字符串, 这事儿是挺简单的, 可要是处理数字那就稍微麻烦那么一些, 为啥这么说, 是因为它read()这个方法仅仅只会返回字符串, 而这些字符串, 还得传递给像那样的函数, 那种函数是能去接受类似123这样的字符串, 并且还能返回数字值123的。当要保存像嵌套列表、字典这类复杂数据类型的时候, 手动去进行解析以及序列化这项操作可是相当复杂的。支持JSON这种广泛流行的数据交换格式, 用户不要绵绵不绝地编写、调试代码, 才能够把繁杂的数据类型保存至文件。标准模块采用数据层次结构, 且将之转变为字符串表示形式, 这个过程称作序列化从字符串表示里重建数据称作解序化。在序列化及解序化之间, 表示对象的字符串也许已经存储于文件或者数据里, 或者经过网络连接发送至远方的机器。注解常用于现代应用程序数据交换的格式是JSON, 程序员对它早就熟悉得不能再熟悉了, 它可是交互操作的最佳选择。只需要那么一行简简单单的代码, 就能够去查看某一个特定对象的JSON字符串呈现形式了: import json x [1, simple, list] json.dumps(x) [1, simple, list]函数存在另外一种变体, 它进行序列化操作时, 仅仅是把对象转化成特定形式, 所以, 要是 f 属于那种对象, 那么能够采用如下做法:json.dump(x, f)若是那种已处于打开状态、可供读取的对象, 要再次对其进行解码, 倘若 f 属于此对象范畴的话。x json.load(f)注解JSON文件需要采用UTF - 8编码, 在打开JSON文件将其作为一个用于读写的对象时, 要使用 utf-8。这种能够处理列表与字典的简单序列化技术, 在 JSON 里要序列化任意类的实例, 就得付出额外努力才行。模块的参考当中有关于此的解释。参见- 封存模块与别的情况不同, 其具备特有的一种性质, 这种性质乃是一种能准予为复杂对象开展序列化操作的协议性质。为此, 属于它单独所拥有的特性, 是不可以用以跟由其他不同语言编写出来的应用程序实现通信的。在默认情况下面它也是并非呈现安全态势的情形表现: 要是解序化之后所得到的数据系经由具备高明手段的攻击者用心打造及设计的, 那么这样一种来源于不受任何信任的途径的特定数据是能够执行任意代码指令的。