【Think Python】Python笔记(十四)文件

(一)持久化

  • 之前的程序都是临时的(transient):只运行一段时间并输出结果,当程序运行结束以后,数据就消失了;再次运行程序,将以全新的状态开始;
  • 还有持久的(persistent),可以长时间运行(或者一直运行),程序重新启动之后吗,将从上次中断的地方开始;
  • 程序保存数据的一个简单的办法是读写文件;
  • 另外一个办法是使用数据库;

(二)读取和写入

  • 要写入文件,将open函数的第二个参数写为w:
>>> fout = open('output')

如果该文件已经存在,那么用写入模式打开它将会清空原来的数据并从新开始,所以要小心! 如果文件不存在,那么将创建一个新的文件;

  • open会返回一个文件对象,这个对象提供了操作我呢间的方法;write方法将数据写入文件:
>>> line = "This here's the wattle,\n"
>>> fout.write(line1)
24

返回值是被写入字符的格式;

  • 文件对象将跟踪自身的位置,所以下次调用write的时候,会在文件末尾添加新的数据:
>>> line2 = "the emblem of our land.\n"
>>> fout.write(line2)
24
  • 完成文件的写入之后,需要关闭这个文件,否则会在程序结束的时候关闭:
>>> fout.close()

(三)格式化运算符

  • write的参数必须是字符串,所以可以使用str将值转化为字符串格式

  • 另一种方法是格式化运算符(format operator),即%:

    • 当作用于整数的时候,%是取模运算符;
    • 当第一个运算数是字符串的时候,%则是格式化运算符;
  • 第一个运算数是格式化字符串(format string),包含一个或者多个格式化序列(format sequence);格式化序列指定了第二个运算数是如何格式化的,运算的结果是一个字符串:

# 格式化序列 '%d' 意味着第二个运算数应该被格式化为一个十进制整数
>>> camels = 42
>>> '%d' % camels
'42'
  • 一个格式化序列可以出现在字符串中的任何位置,所以可以将一个值嵌入到一个语句中:
>>> 'I have spotted %d camels.' % camels
'I have spotted 42 camels.'
  • 如果字符串中有多个格式化序列,则第二个参数必须是一个元组;每个格式化序列按照顺序与元组中的元素进行对应:
# 使用 '%d' 来格式化一个整数, '%g' 来格式化一个浮点数,以及 '%s' 来格式化一个字符串:

>>> 'In %d years I have spotted %g %s.' % (3, 0.1, 'camels')
'In 3 years I have spotted 0.1 camels.'
  • 元组中元素的个数必须等于字符串中格式化序列的个数。 同时,元素的类型也必须符合对应的格式化序列:
>>> '%d %d %d' % (1, 2)
TypeError: not enough arguments for format string
>>> '%d' % 'dollars'
TypeError: %d format: a number is required, not str
  • 格式化输出的另一种办法是使用**str.format()**方法:

    • 这个方法使用占位符,占位符中是序号:
    >>> "The sum of 1 + 2 is {0}".format(1+2)
    "The sum of 1 + 2 is 3"
    

(四)文件名和路径

文件是以**目录(directory)的形式组起来的,每个正在运行的程序都有一个当前目录(current directory)**作为大多数操作的默认目录;

  • os模块提供了操作文件和目录的函数;os.getcwd返回当前目录的名称:
>>> import os
>>> cwd = os.getcwd()
>>> cwd
'/home/dinsdale'
  • 一个简单的文件名memo.txt同样是一个路径,只不过是相对路径
  • 一个以/开头的路径和当前目录无关,叫做绝对路径(absolute Path);可以使用下面的方法获取一个文件的绝对路径:
>>> os.path.abspath('memo.txt')
'/home/dinsdale/memo.txt'
  • 使用os.path.exists检查一个文件或者路径是不是存在:
>>> os.path.exists('memo.txt')
True
  • 使用os.path.isdir检查它是不是一个目录:
>>> os.path.isdir('memo.txt')
False
>>> os.path.isdir('/home/dinsdale')
True
  • 使用os.path.isfile检查是不是一个文件:

  • 使用os.listdir返回给定目录下的文件列表(以及其他目录):

>>> os.listdir(cwd)
['music', 'photos', 'memo.txt']
  • 示例:
def walk(dirname):
    for name in os.listdir(dirname):
        path = os.path.join(dirname, name)

        if os.path.isfile(path):
            print(path)
        else:
            walk(path)
  • os.path.join接受一个目录和一个文件名,并将它们合并成一个完成的路径

(五)异常捕获

  • 当试图读写文件的时候,会很容易发生错误;如访问不存在的文件、没有权限的文件等;
  • 为了避免这样的错误,当然可以使用类似于os.path.existsos.path.isfile的函数进行检查,但是这样会耗费大量的时间和代码检查所有的可能性;
  • 更加好的办法是在问题出现的时候才进行处理,这个是try语句做的事情:
try:
    fin = open('bad_file')
except:
    print('Something went wrong.')
  • 一般来说,捕获异常之后,可以选择是不是解决这个问题,或者继续常识运行,或者结束程序;

(六)数据库

  • 数据库是用来存储信息的文件;
  • 大多数的数据库采用类似字典的形式,即将键映射到值;但是数据库和字典的最大的区别是:数据库是存储在硬盘上的,所以当程序结束运行之后,依然是存在的;
  • dbm模块提供了创建和更新数据库文件的接口:
>>> import dbm
>>> db = dbm.open('captions', 'c')
# 'c'代表如果数据库不存在则创建该数据库;
#这个操作返回一个数据库对象,可以像使用字典一样使用它(对于多数的操作)
  • 当创建一个新的项目时,dbm将会更新数据库:
>>> db['cleese.png'] = 'Photo of John Cleese'
  • 当访问某个项时,dbm将会读取文件:
>>> db['cleese.png']
b'Photo of John Cleese.'

返回的结果是一个字节对象(bytes object),所以是以b开头;一个字节对象在很多方面和一个字符串很像,在目前的阶段可以忽略他们之间的不同,但是他们之间的不同是非常重要的;

  • 当对已经存在的值进行重新赋值,将会替换原有的值:
>>> db['cleese.png'] = 'Photo of John Cleese doing a silly walk.'
>>> db['cleese.png']
b'Photo of John Cleese doing a silly walk.'
  • keysitems等字典方法并不适合于数据库对象,但是for循环是可以的:
for key in db:
    print(key, db[key])
  • 当操作完毕之后需要关闭文件:
>>> db.close()

(七)序列化

  • dbm模块的键和值必须是字符串或者字节;
  • pickle可以将几乎所有类型的对象转化为适合在数据库中存储的字符串;也可以将字符串还原为原来的对象
  • pickle.dumps读取一个对象作为参数,并返回一个字符串:
>>> import pickle
>>> t = [1, 2, 3]
>>> pickle.dumps(t)
b'\x80\x03]q\x00(K\x01K\x02K\x03e.'
  • pickle.loads可以重建对象:
>>> t1 = [1, 2, 3]
>>> s = pickle.dumps(t1)
>>> t2 = pickle.loads(s)
>>> t2
[1, 2, 3]
  • 虽然新的对象和旧的对象是有相同的值,但是并不是相同的对象:
>>> t1 == t2
True
>>> t1 is t2
False

序列化,之后反序列化,等效于复制一个对象;

(八)管道

  • 大多数操作系统提供了命令行接口,称之为shell
  • 任何在shell中可以启动的程序,都可以在python中通过**管道对象(pipe object)**启动;
  • 一个管道代表一个正在运行的程序;
# Unix命令'ls -l'以详细格式显示当前目录下的内容
>>> cmd = 'ls -l'
>>> fp = os.popen(cmd)
# 返回值是一个行为类似已打开文件的对象
>>> res = fp.read()
# 关闭管道
>>> stat = fp.close()
>>> print(stat)
None
  • md5sum可以读取一个文件的内容,计算一个校验和:
>>> filename = 'book.tex'
>>> cmd = 'md5sum ' + filename
>>> fp = os.popen(cmd)
>>> res = fp.read()
>>> stat = fp.close()
>>> print(res)
1e0033f0ed0656636de0d75144ba32e0  book.tex
>>> print(stat)
None

(九)编写模块

任何包含python代码的文件,都可以作为模块导入

  • 有一个wc.py文件:
def linecount(filename):
    count = 0
    for line in open(filename):
        count += 1
    return count

print(linecount('wc.py'))
  • 现在有一个模块wc
>>> wc
<module 'wc' from 'wc.py'>
  • 这个模块对象提供了linecount函数:
>>> wc.linecount('wc.py')
7
  • 现在这个模块的问题在于,当导入这个模块之后,将自动运行到后面的测试代码;通常当导入一个模块的时候,将定义一些新的函数,但是并不运行它们;
  • 作为模块的程序通常是:
if __name__ == '__main__':
	print(linecount('wc.py'))
  • __name__是一个在程序开始时设置到内建变量;
    • 如果程序以脚本的形式运行,__name__的值为__main__,这时候其中的代码被执行;
    • 否则,当作为模块导入的时候,其中的代码将会被跳过;
  • 如果你导入一个已经被导入了的模块,Python 将不会做任何事情。它并不会重新读取文件,即使文件的内容已经发生了改变;
    • 如果你要重载一个模块,可以使用内建函数 reload ,但它可能会出错。因此最安全的方法是重启解释器,然后重新导入模块。

(十)调试

读写文件的时候,可能会遇到空格带来的问题;这个问题难以解决是因为这些空格、制表符、换行符是不可见的;

  • 使用内建函数repr可以解决这个问题:
>>> s = '1 2\t 3\n 4'
>>> print(s)
1 2  3
 4
    
>>> print(repr(s))
'1 2\t 3\n 4'
发布了73 篇原创文章 · 获赞 2 · 访问量 7153

猜你喜欢

转载自blog.csdn.net/forever_008/article/details/104649120