wk_06.md

IO与文件操作

文件内建函数open

内建函数open提供了初始化输入/输出(I/O)操作的通用接口。open()内建函数成功打开文件后会返回一个文件对象。open函数的语法如下:

open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

In [5]: f = open('file.txt')

In [6]: f
Out[6]: <_io.TextIOWrapper name='file.txt' mode='r' encoding='UTF-8'>

mode参数

文件对象的访问模式

文件模式操作
r、rt 以只读方式打开、文件指针-0、不会创建文件
w、wt 以只写方式打开、文件指针-0、创建文件、清空打开的文件
a、at 以追加方式打开、文件指针-end、创建文件、不清空文件
x、xt 创建文件、文件只写、文件指针-0、文件存在-抛出FileExistsError异常
r+ 以读写方式打开、文件指针-0、不会创建文件
w+ 以读写方式打开、文件指针-0、创建文件、清空打开的文件
a+ 以读写方式打开、文件指针-end、创建文件、不清空文件
x+ 创建文件、文件可读写、文件指针-0、文件存在-抛出FileExistsError异常
rb 以二进制只读方式打开(参照r)
wb 以二进制只写方式打开(参照w)
ab 以二进制追加方式打开(参照a)
xb 以二进制只写方式打开(参照x)
rb+ 以二进制读写方式打开(参照r+)
wb+ 以二进制读写方式打开(参照w+)
ab+ 以二进制读写方式打开(参照a+)
xb+ 以二进制读写方式打开(参照x+)
  • open()函数打开文件默认是以只读模式打开,默认打开文件的格式是文本模式。所以r和rt等价。
  • mode决定了open的返回值。
  • 当mode为b的时候,返回的文件对象是以字节为单位操作文件的。
  • 当mode为t的时候,返回的文件对象是以字符为单位操作文件的, 这是默认行为。
  • mode为w、w+、wb+时总是清空文件
  • mode为r时, 打开的文件可读, 不可写, 这个默认行为。
  • mode为w时,打开的文件可写, 不可读。
  • 读写追加只能使用其中一直方式打开文件。
  • 凡是有r的,都不会创建文件,当文件不存在时, 报FileNotFoundError

注意:关于r+和a+使用的说明。

In [176]: cat file1.txt
123abc
In [177]: f= open('file1.txt','r+')

In [178]: f.write('456')
Out[178]: 3

In [180]: f.close()

In [181]: cat file1.txt
456abc

In [182]: f= open('file1.txt','a+')

In [183]: f.tell()
Out[183]: 6

In [184]: f.seek(0,0)
Out[184]: 0

In [185]: f.write('def')
Out[185]: 3

In [186]: f.close()

In [187]: cat file1.txt
456abcdef
  • 在使用r+的时候若指针在0,则写入的数据会覆盖原先的数据,是按位置进行覆盖。
  • 在使用a+的时候若指针在0,则写入的数据出现的文件的最后,并不会出现覆盖。

buffering

在Linux系统中常常看到buffer和cache的使用,在Python中其实也是有它们的应用的。其中buffer就是写文件的时候,先写缓冲区,当缓冲区满的时候, 操作系统会把缓冲区flush到硬盘。通常缓冲区越大, 写入性能越好。但是当操作系统掉电、或者进程意外退出,缓冲区的数据可能丢失。注意:缓冲区的数据,对其他进程是不可见的。
由于在Python3中文件写入有文本和二进制模式,所以在buffer上两者的设置使用还是有区别的。

文本模式

以text模式打开的时候并且buffering=1则其使用的line buffering模式, 遇到换行符就会刷新到硬盘:

In [192]: f=open('file.txt','wt',buffering=1)

In [193]: cat file.txt

In [194]: f.write('123')
Out[194]: 3

In [195]: cat file.txt

In [196]: f.write('abc
')
Out[196]: 4

In [197]: cat file.txt
123abc

在以text模式打开文件时,buffering不能设置为0,不然会抛出ValueError: can't have unbuffered text I/O的异常。若设置成除了0和1的数字则不是使用的line buffering模式,而是使用当写入缓存区的字符大于等于8193个才会被写入到文件中。

二进制模式

在二进制模式中buffering是可以设置成为0的,所设置成为0则写入的二进制数据会立即刷新到磁盘文件中。

In [73]: f=open('file.txt','wb',buffering=0)

In [74]: f.write(b'A')
Out[74]: 1

In [75]: cat file.txt
A

当buffering设置成别的数字的时候则写入的数据直到大于该值则会写入的文件中。

In [77]: f=open('file.txt','wb',buffering=10)

In [78]: f.write(b'A'*10)
Out[78]: 10

In [79]: cat file.txt

In [80]: f.write(b'B')
Out[80]: 1

In [81]: cat file.txt
AAAAAAAAAA

在实际使用中一般buffering是使用的默认值-1,并不会进行设置除非有特殊需求。而python中默认的buffer可以通过io模块进行查看:

In [1]: import io

In [2]: io.DEFAULT_BUFFER_SIZE
Out[2]: 8192

总结:

  • 文本模式下,不设置buffering参数,遇到换行,刷新buffer。
  • 二进制模式下,不设置buffering参数,根据操作系统自动判断buffer大小。
  • 二进制模式下,设置buffering参数为0,则关闭buffer。

encoding和errors

open函数中encoding和errors用在打开文本模式,其中encoding用于指定打开文件的编码,默认打开的编码是utf-8,若遇到gbk的文件则可以使用此参数。而我们在文本模式打开文件时若打开的编码格式不正确则会抛出异常,若不想让解释器抛出异常则指定errors参数为ignore,该参数默认是设置成严格模式strict。

文件对象--读

readable

  • 判断文件对象是否可读
In [5]: f=open('file.txt')

In [6]: f.readable()
Out[6]: True

read

  • read方法默认size为-1即读入全部内容,当不指定读入的size时则一次性读入全部内容。
  • read方法中若指定读入的size则在文本模式下读入指定size个字符,在二进制模式下读入指定size个字节。
  • read类的操作, 总是向后移动文件指针。
In [7]: f.read()
Out[7]:'Defense of the Ancients
DOTA
刀塔
'

In [13]: f.seek(0,0)
Out[13]: 0

In [14]: f.read(4)
Out[14]: 'Defe'

In [15]: f.read()
Out[15]: 'nse of the Ancients
DOTA
刀塔
'

readline

  • readline一次读入一行, 包括换行符。
  • readline 和strip一起使用,去除换行符。
  • readline 和rstrip(' ') 一起使用,避免移除首尾的空白。
In [10]: f.readline()
Out[10]: 'Defense of the Ancients
'

In [11]: f.readline().strip()
Out[11]: 'DOTA'

In [12]: f.readline().rstrip('
')
Out[12]: '刀塔'

readlines

  • 一次读入所有行 返回一个list。
In [14]: f.readlines()
Out[14]: ['Defense of the Ancients
', 'DOTA
', '刀塔
']
  • read、readline、readlines都能以二进制模式读取文件。
  • 当读到文件末尾,read和readline,返回空字符串/空bytes, readlines 返回空list。
  • 这个可以用于判断是否读到文件末尾。

文件对象--写

writeable

  • 判断文件是否可写。

write

  • write向文件写入字符串/bytes。
  • write写入后会返回写入的字符数/字节数。
  • 写操作的时候, 换行符始终需要显式传入。
  • write 操作总是向后移动文件指针。
  • 当模式以w打开文件的时候写文件会根据文件指针的位置进行写入。若文件指针所在位置有数据则写入的数据会覆盖之前的数据。
  • 当模式以a打开文件的时候不管文件指针在何处,写入的数据都会写入到最后。
In [10]: f=open('test.txt','w+')

In [11]: f.write('Defense of the Ancients')
Out[11]: 23

In [13]: f.tell()
Out[13]: 23

In [14]: f.seek(0)
Out[14]: 0

In [15]: f.read()
Out[15]: 'Defense of the Ancients'

In [16]: f.seek(0)
Out[16]: 0

In [17]: f.write('DOTA')
Out[17]: 4

In [18]: f.seek(0)
Out[18]: 0

In [19]: f.read()
Out[19]: 'DOTAnse of the Ancients'

In [20]: f.close()

In [21]: f=open('test.txt','a+')

In [22]: f.write('Defense of the Ancients')
Out[22]: 23

In [23]: f.seek(0)
Out[23]: 0

In [24]: f.write('DOTA')
Out[24]: 4

In [25]: f.seek(0)
Out[25]: 0

In [26]: f.read()
Out[26]: 'DOTAnse of the AncientsDefense of the AncientsDOTA'

writelines

  • writelines 写入的数据是一个可迭代对象,其元素为字符串/bytes。
  • writelines 会遍历参数,然后写入。
  • writelines 写入后返回None。
  • 写操作的时候, 换行符始终需要显式传入
In [28]: f=open('test.txt','w+')

In [29]: f.writelines(['Defense of the Aencients','DOTA'])

In [33]: f.close()

In [34]: cat test.txt
Defense of the AencientsDOTA
In [35]: f=open('test.txt','w+')

In [37]: f.writelines(['Defense of the Aencients
','DOTA
'])

In [39]: f.close()

In [40]: cat test.txt
Defense of the Aencients
DOTA

truncate

  • 当truncate指定了参数,则清除从指定的文件指针到文件最后指针位置的数据。
  • 如果不指定了参数,则从当前文件指针处开始清空。
  • 当打开模式是'r+'的时候,文件指针在最开始,若使用truncate没有指定参数则会清除所有的数据。
  • 若打开模式是'w',此模式默认就会清除文件中的内容。
  • 当打开模式是'a+'的时候,文件指针会在最后,若使用truncate没有指定参数则不会清除数据。
In [48]: f=open('test.txt','r+')

In [50]: f.tell()
Out[50]: 0

In [51]: f.read()
Out[51]: 'Defense of the Aencients
DOTA
'

In [52]: f.seek(0)
Out[52]: 0

In [53]: f.truncate()
Out[53]: 0

In [55]: f.close()

In [56]: cat test.txt

In [93]: f=open('test.txt','a+')

In [107]: f.write('DOTA')
Out[107]: 4

In [108]: f.tell()
Out[108]: 4

In [109]: f.truncate(2)
Out[109]: 2

In [110]: f.close()

In [111]: cat test.txt
DO

文件对象--其他

close

我们在打开文件的时候一定要记住:打开的文件在使用完成后,一定要关闭!这是因CentOS 系统默认打开的文件句柄数是1024,如果出现打开的文件超过系统的上限则会报:[Errno 24] Too many open files: 'test.txt'的异常。有些时候我们无法及时close文件则可以使用下面的方法自动的关闭文件。

In [1]: with open('test.txt') as f:
   ...:     f.read()
   ...:     

In [2]: f.closed
Out[2]: True

flush

flush 强制刷新缓冲区

In [3]: f=open('test.txt','w+')

In [4]: f.write('Defense of the Ancients')
Out[4]: 23

In [5]: cat test.txt

In [6]: f.flush()

In [7]: cat test.txt
Defense of the Ancients

seek方法

file.seek(off,whence=0)
  • seek用于移动文件指针
  • whence 0 从开始处移动 1从当前位置移动 2 从文件末尾移动
  • offset 为正数,向后移动,为负数向前移动
  • seek移动的是字节数
  • 当文本模式打开,whence只能是0
  • seek不可以移动到文件头之前
  • seek可以移动到文件尾之后
  • 当seek到文件尾之后, 操作是,从文件尾开始

文件属性

name

查看打开的文件名

closed

表示文件是否已经关闭,关闭为True未关闭则为False。

encoding

文件使用的编码。

errors

error的模式

扩展学习

StringIO

  • StringIO把一个字符串封装成一个文件对象
  • StringIO 相当于以文本模式打开的文件对象
In [74]: from io import StringIO,BytesIO

In [75]: fs=StringIO('Defense of the Aencients')

BytesIO

  • BytesIO把一个bytes封装成一个文件对象
  • BytesIO 相当于以二进制模式打开的文件对象
In [76]: fb=BytesIO(b'Defense of the Aencients')

实际使用:

In [77]: import json

In [84]: fp = StringIO('{"a": 1}')

In [85]: json.load(fp)
Out[85]: {'a': 1}

目录操作

创建

  • os.mkdir 是用来创建空目录的,但是不能递归的创建多级目录。
  • os.makedirs 可以创建一级目录,也可以递归的创建多级空目录。
  • 当目录已经存在时, mkdir 和makedirs都会抛出异常。
  • os.makedirs 在创建目录的时候可以指定参数exist_ok:是否抛出异常,mode:目录的权限。
  • 在os.makedirs创建目录指定权限时最好使用八进制进行表示,这样符合Linux系统使用习惯。
In [8]: os.makedirs('/tmp/test/test')

In [9]: ls -ld /tmp/test/
drwxr-xr-x 3 root root 4096 10月  1 22:18 /tmp/test//

In [11]: os.makedirs('/tmp/test/test',exist_ok=True)

In [16]: os.makedirs('/tmp/test1/test',mode=0o700)

In [18]: ls -ld /tmp/test1
drwx------ 3 root root 4096 10月  1 22:21 /tmp/test1/

In [19]: ls -ld /tmp/test1/test/
drwx------ 2 root root 4096 10月  1 22:21 /tmp/test1/test//

删除

  • os.rmdir 只能删除空目录,不能删除非空目录。
  • os.removedirs 递归的删除空目录。
  • shutil.rmtree(path[, ignore_errors[, onerror]]) 递归删除目录。
    • path必须指向一个目录(不可以是指向目录的软链接)。
    • 如果ignore_errors为真,将忽略删除失败产生的错误;如果为假或被省略,这些错误将通过调用onerror指示的处理程序处理,或者如果省略onerror,它们将会引发异常。
    • 如果提供onerror,它必须是一个接受三个参数的可调用对象:function、path和excinfo。第一个参数function是引发异常的函数;第二个参数path是传递给function的路径名称。第三个参数excinfo是由sys.exc_info()返回的异常信息。onerror抛出的异常不会被捕获。
In [25]: shutil.rmtree('/usr/local/src/test/',onerror=lambda fn,path,exc_info:print('{} => {}'.format(path,exc_info[1])))
/usr/local/src/test/ => [Errno 2] No such file or directory: '/usr/local/src/test/'

移动

  • shutil.move(src, dst)以递归方式移动文件或目录(src)到另一个位置(dst)。如果目标是一个目录或一个目录的符号链接,那么src被移动到该目录中。
In [27]: shutil.move('/tmp/test','/tmp/abc')
Out[27]: '/tmp/abc'

复制

  • shutil.copyfileobj(fsrc, fdst[, length]) 将类文件对象fsrc 的内容复制到类文件对象fdst。
  • shutil.copyfile(src, dst) 复制文件src的内容(不包含元数据)到文件dst。dst 必须是完整的目标文件的名称;
  • shutil.copymode(src, dst) 将权限位从src复制到dst。文件内容、 所有者和组不会受到影响。src和dst是作为字符串给出的路径名称。
  • shutil.copystat(src, dst) 将权限位、 最后存取时间、 最后修改时间和标志从src复制到dst。文件内容、 所有者和组不会受到影响。src和dst是作为字符串给出的路径名称。
  • shutil.copy(src, dst) 将文件src复制到文件或目录dst。如果dst是一个目录,将在该指定的目录中创建(或覆盖)一个具有和src相同名称的文件。权限位也被复制。src和dst是作为字符串给出的路径名称。
  • shutil.copy2(src, dst) 类似于shutil.copy(),但元数据也复制 — 事实上,它只是shutil.copy()加上copystat()。这是类似于Unix 的命令cp -p。
  • shutil.copytree(src, dst, symlinks=False, ignore=None) 以递归方式复制以src为根的整个目录树。目标目录dst必须不存在;它和父目录将一起创建。复制权限和目录使用copystat()的,单个文件的复制使用shutil.copy2()。
    • 如果syslinks为真,源树中的软链接表示为新树中的软链接,但不复制原始链接的元数据;如果为假或被省略,链接的文件内容和元数据将复制到新树。
    • 如果给定ignore,则它必须是可调用将接收作为其参数由os.listdir()返回后造访了copytree()和其内容的列表的目录。由于copytree()以递归方式调用,ignore可调用对象将对每个复制的目录调用。可调用必须返回一个序列的目录和文件的名称,相对于当前目录 (即第二个参数中的项的子集) ;这些名称将在复制过程中被忽略。
shutil.copytree('/tmp/a', '/tmp/b') == # cp -rp 

shutil.copytree('/tmp/a', '/tmp/d', symlinks=True) == # cp -rpP

遍历

  • os.listdir(path) 返回一个列表,包含给定path 目录下所有条目的名字。列表是无序的。列表并不包含 '.' 和 '..' 目录,即使存在也不包含。
  • os.walk(top, topdown=True, onerror=None, followlinks=False) 通过遍历目录树,自顶向下或自底向上生成目录树下的文件名。对于以top 为根的目录树下的每个子目录(包括top自己),它生成一个三元组(dirpath, dirnames, filenames)。
    • dirpath 为目录路径的字符串。dirnames 为dirpath 下子目录的名称列表(不包括'.' 和'..')。filenames 为dirpath 下非目录文件的名称列表。注意,列表中的名词不包含路径部分。要获取dirpath 下目录或文件的完整路径(以top 开始),需要用os.path.join(dirpath, name)。
    • 如果可选的参数topdown 为True 或没有指定,那么目录的三元组在其子目录之前生成(自顶向下)。如果topdown 为False,那么目录的三元组在其所有的子目录之后生成(自底向上)。
    • 当topdown 为True 时,调用则可以修改dirnames 列表(使用del 或切片赋值),walk() 将只遍历dirnames 中剩下的子目录;这可以用于优化搜索、定义访问的顺序、或者甚至在可以重新walk() 之前通知walk() 调用者创建或者重命名的目录。当topdown 为False 时,修改dirnames 是无效的,因为在自底向上的模式下dirnames 下的目录在 dirpath 之前生成。
    • 默认情况下,listdir() 调用产生的错误将被忽略。如果可选的参数onerror 有指定,它应该是一个函数;它将用一个OSError 实例作为参数进行调用。它可以报告错误以继续遍历还是引发一个异常以停止遍历。注意,文件名可以通过异常对象的filename 属性访问。
    • 默认情况下,walk() 不会遍历进入目录的符号链接中。在支持符号链接的系统上,可以设置followlinks 为True 来访问符号链接指向的目录。
In [38]: for dirname,dirs,files in os.walk('/tmp/test1/',onerror=lambda e: print(e)):
              print(dirname)
              print(dirs)
              print(files)
              print('-------------')
             ....:     
/tmp/test1/
['test']
['abc.txt']
-------------
/tmp/test1/test
[]
['def.txt']
-------------

[root@hpf-linux ~]# tree -F /tmp/test1/
/tmp/test1/
├── abc.txt
└── test/
    └── def.txt

1 directory, 2 files

路径相关操作

  • os.path.abspath(path) 返回路径名path的规范化的绝对路径。
  • os.path.basename(path) 返回路径名path的主文档名。在path上调用split()函数,返回的二元组中的第二个元素就是主文档名。这和Unix的basename不同;对于'/foo/bar/',basename返回'bar',而basename()函数返回空字符串('')。
  • os.path.dirname(path) 返回路径名path的目录名。在path上调用函数split(),返回的二元组中的第一个元素就是目录名。
  • os.path.join(path1[, path2[, ...]]) 将一个或多个路径正确地连接起来。如果任何一个参数是绝对路径,那之前的参数就会被丢弃,然后连接继续(如果在Windows上,如果有盘符,盘符也会被丢弃)。返回的值是path1、path2等等的连接,每个非空部分除了最后一个后面只跟随一个目录分隔符(os.sep)。(这意味着最后一部分为空将导致路径以分隔符结尾。)注意,在windows上, 由于每个盘符都是一个目录,因此,os.path.join("c:", "foo") 将返回一个C盘的相对路径,即基于C盘C:的绝对路径 (c:foo), 而非一个相对路径c:foo.
  • os.path.split(path) 将路径名path 分割成(head, tail),其中tail路径名的最后一部分,head 是其之前的所有部分。tail部分永远不会包含斜线;如果path以斜线结束,tail 将为空。如果path中没有斜线,head将为空。如果path为空,head 和 tail两个都将为空。尾部的斜线会从head中去除掉,除非它是根目录(只包含一个或多个斜线的目录)。对于所有情况,join(head, tail)都将返回一个和path相同的位置(但是表示的字符串可能不一样)。
  • os.path.isabs(path) 如果path是绝对路径名,返回True。
  • os.path.isfile(path) 如果path是一个存在的普通文件,返回True。
  • os.path.isdir(path) 如果path是一个存在的目录,返回True。
  • os.path.islink(path) 如果path引用的目录条目是个符号链接,返回True。
  • os.path.ismount(path) 如果路径名path是一个mount point(挂载点),返回True。

获取脚本的绝对路径:os.path.abspath(os.path.dirname(sys.argv[0]))

序列化与反序列化

  • 序列化 对象 -> str/bytes
  • 反序列化 str/bytes -> 对象
原文地址:https://www.cnblogs.com/cuchadanfan/p/6718423.html