Python学习笔记(IO编程)

1、文件读写
     读文件
     使用内置open(name[, mode[, buffering]])函数,name是 包含了文件名的路径字符串,mode是打开方式。
模式描述
t文本模式 (默认)。
x写模式,新建一个文件,如果该文件已存在则会报错。
b二进制模式。
+打开一个文件进行更新(可读可写)。
U通用换行模式(不推荐)。
r以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
rb以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。一般用于非文本文件如图片等。
r+打开一个文件用于读写。文件指针将会放在文件的开头。
rb+以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。一般用于非文本文件如图片等。
w打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
wb以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
w+打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
wb+以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
a打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
ab以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
a+打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
ab+以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。
       例子,以只读方式打开文件:
>>> f = open('/Users/michael/test.txt''r')
       调用read()方法可以一次读取文件的全部内容,Python把内容读到内存,用一个str对象表示:
>>> f.read()
'Hello, world!'
       最后需要调用close()方法关闭文件,否则会占用系统的资源。
>>> f.close()
       Python引入了with语句来自动帮我们调用close()方法,
with open('/path/to/file''r'as f:
    print(f.read())
       不必写close()方法,能自动关闭文件。
       调用read()会一次性读取文件的全部内容,如果文件太大,内存就爆了,所以,要保险起见,可以反复调用read(size)方法,每次最多读取size个字节的内容。另外,调用readline()可以每次读取一行内容,调用readlines()一次读取所有内容并按行返回list。

       二进制文件
       要读取二进制文件,比如图片、视频等等,用'rb'模式打开文件即可:
>>> f = open('/Users/michael/test.jpg''rb')
>>> f.read()
b'\xff\xd8\xff\xe1\x00\x18Exif\x00\x00...' # 十六进制表示的字节
     
      字符编码
      要读取非UTF-8编码的文本文件,需要给open()函数传入encoding参数,例如,读取GBK编码的文件
>>> f = open('/Users/michael/gbk.txt''r'encoding='gbk')
>>> f.read()
'测试'
      open()函数还接收一个errors参数,表示如果遇到编码错误后如何处理。最简单的方式是直接忽略:
>>> f = open('/Users/michael/gbk.txt''r'encoding='gbk'errors='ignore')

      写文件
      调用open()函数时,传入标识符'w'或者'wb'表示写文本文件或写二进制文件:
with open('/Users/michael/test.txt''w'as f:
    f.write('Hello, world!')
      如果要写入特定编码的文本,也需要和open()一样,传入encoding参数。

2、StringIO和BytesIO
     StringIO
     在内存中读写str,需要先创建一个StringIO,然后,像文件一样写入,getvalue()方法用于获得写入后的str。
>>> from io import StringIO
>>> f = StringIO()
>>> f.write('hello')
5
>>> f.write(' ')
1
>>> f.write('world!')
6
>>> print(f.getvalue())
hello world!
     要读取StringIO,可以用一个str初始化StringIO,然后,像读文件一样读取:
>>> from io import StringIO
>>> f = StringIO('Hello!\nHi!\nGoodbye!')
>>> while True:
...     s = f.readline()
...     if s == '':
...         break
...     print(s.strip())
...
Hello!
Hi!
Goodbye!
      文件指针
      当使用StringIO()去初始化的时候,其指针是指向0的位置;而如果是用write的方法的时候,其指针则是会移动到后面的,使用readline指针会移到第一行的后面。
      文件指针涉及两个方法:
      tell()方法返回当前指针位置。
      seek()用于移动文件读写指针到指定位置,有两个参数,第一个offset: 偏移量,需要向前或向后的字节数,正为向后,负为向前;第二个whence: 可选值,默认为0,表示文件开头,1表示相对于当前的位置,2表示文件末尾。
     
      BytesIO
      如果要操作二进制数据,就需要使用BytesIO。
>>> from io import BytesIO
>>> f = BytesIO()
>>> f.write('中文'.encode('utf-8'))
6
>>> print(f.getvalue())
b'\xe4\xb8\xad\xe6\x96\x87'
     写入的是经过UTF-8编码的bytes。

3、操作文件和目录
     Python内置的os模块可以直接调用操作系统提供的接口函数。
>>> import os
>>> os.name # 操作系统类型
'posix'
     如果是posix,说明系统是Linux、Unix或Mac OS X,如果是nt,就是Windows系统。
     环境变量
     在操作系统中定义的环境变量,全部保存在os.environ这个变量中,可以直接查看。要获取某个环境变量的值,可以调用os.environ.get('key')。
>>> os.environ.get('PATH')
'/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin:/usr/local/mysql/bin'
>>> os.environ.get('x''default')
'default'
      操作文件和目录
      操作文件和目录的函数一部分放在os模块中,一部分放在os.path模块中。
# 查看目录的绝对路径:
>>> os.path.abspath('.')
'/Users/michael'
# 在某个目录下创建一个新目录,首先把新目录的完整路径表示出来:
>>> os.path.join('/Users/michael''testdir')
'/Users/michael/testdir'
# 然后创建一个目录:
>>> os.mkdir('/Users/michael/testdir')
# 删掉一个目录:
>>> os.rmdir('/Users/michael/testdir')
# 对文件重命名:
>>> os.rename('test.txt''test.py')
# 删掉文件:
>>> os.remove('test.py')
        os.path 模块主要用于获取文件的属性。常用的方法如下:
方法说明
os.path.abspath(path)返回绝对路径
os.path.basename(path)返回文件名
os.path.commonprefix(list)返回list(多个路径)中,所有path共有的最长的路径
os.path.dirname(path)返回文件路径
os.path.exists(path)路径存在则返回True,路径损坏返回False
os.path.lexists路径存在则返回True,路径损坏也返回True
os.path.expanduser(path)把path中包含的"~"和"~user"转换成用户目录
os.path.expandvars(path)根据环境变量的值替换path中包含的"$name"和"${name}"
os.path.getatime(path)返回最近访问时间(浮点型秒数)
os.path.getmtime(path)返回最近文件修改时间
os.path.getctime(path)返回文件 path 创建时间
os.path.getsize(path)返回文件大小,如果文件不存在就返回错误
os.path.isabs(path)判断是否为绝对路径
os.path.isfile(path)判断路径是否为文件
os.path.isdir(path)判断路径是否为目录
os.path.islink(path)判断路径是否为链接
os.path.ismount(path)判断路径是否为挂载点
os.path.join(path1[, path2[, ...]])把目录和文件名合成一个路径
os.path.normcase(path)转换path的大小写和斜杠
os.path.normpath(path)规范path字符串形式
os.path.realpath(path)返回path的真实路径
os.path.relpath(path[, start])从start开始计算相对路径
os.path.samefile(path1, path2)判断目录或文件是否相同
os.path.sameopenfile(fp1, fp2)判断fp1和fp2是否指向同一文件
os.path.samestat(stat1, stat2)判断stat tuple stat1和stat2是否指向同一个文件
os.path.split(path)把路径分割成 dirname 和 basename,返回一个元组
os.path.splitdrive(path)一般用在 windows 下,返回驱动器名和路径组成的元组
os.path.splitext(path)分割路径,返回路径名和文件扩展名的元组
os.path.splitunc(path)把路径分割为加载点与文件
os.path.walk(path, visit, arg)遍历path,进入每个目录都调用visit函数,visit函数必须有3个参数(arg, dirname, names),dirname表示当前目录的目录名,names代表当前目录下的所有文件名,args则为walk的第三个参数
os.path.supports_unicode_filenames设置是否支持unicode路径名
         例子:
         拆分路径,获得最后级别的目录或文件名
>>> os.path.split('/Users/michael/testdir/file.txt')
('/Users/michael/testdir''file.txt')
         得到文件扩展名
>>> os.path.splitext('/path/to/file.txt')
('/path/to/file''.txt')
         列出当前目录下的所有目录
>>> [x for x in os.listdir('.'if os.path.isdir(x)]
['.lein''.local''.m2', ...]
         列出所有的.py文件
>>> [x for x in os.listdir('.'if os.path.isfile(x) and os.path.splitext(x)[1]=='.py']
['apis.py''config.py''models.py''pymonitor.py''test_db.py''urls.py''wsgiapp.py']
         OS模块没有复制文件的函数,shutil模块提供了copyfile()的函数。

4、序列化
     把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling。反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling。
     Python提供了pickle模块来实现序列化。
>>> import pickle
>>> d = dict(name='Bob'age=20score=88)
>>> pickle.dumps(d)
b'\x80\x03}q\x00(X\x03\x00\x00\x00ageq\x01K\x14X\x05\.'
     pickle.dumps()方法把任意对象序列化成一个bytes,然后,就可以把这个bytes写入文件。或者用另一个方法pickle.dump()直接把对象序列化后写入一个file-like Object:
>>> f = open('dump.txt''wb')
>>> pickle.dump(d, f)
>>> f.close()
     当我们要把对象从磁盘读到内存时,可以先把内容读到一个bytes,然后用pickle.loads()方法反序列化出对象,也可以直接用pickle.load()方法从一个file-like Object中直接反序列化出对象。
>>> f = open('dump.txt''rb')
>>> d = pickle.load(f)
>>> f.close()
>>> d
{'age'20'score'88'name''Bob'}
   
      JSON
      如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,比如XML,但更好的方法是序列化为JSON,因为JSON表示出来就是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。
    JSON表示的对象就是标准的JavaScript语言的对象,JSON和Python内置的数据类型对应如下:
JSON类型Python类型
{}dict
[]list
"string"str
1234.56int或float
true/falseTrue/False
nullNone
      使用Python内置的json模块可以完成Python对象到JSON格式的转换。
>>> import json
>>> d = dict(name='Bob'age=20score=88)
>>> json.dumps(d)
'{"age": 20, "score": 88, "name": "Bob"}'
      dumps()方法返回一个str,内容就是标准的JSON。类似的,dump()方法可以直接把JSON写入一个file-like Object。
      要把JSON反序列化为Python对象,用loads()或者对应的load()方法,前者把JSON的字符串反序列化,后者从file-like Object中读取字符串并反序列化。
>>> json_str = '{"age": 20, "score": 88, "name": "Bob"}'
>>> json.loads(json_str)
{'age'20'score'88'name''Bob'}

      JSON进阶
      将类的实例序列化为JSON时,dumps()方法默认不支持,需要增加default可选参数,我们可以自己为类专门写一个转换函数,再把函数传给default参数,就能实现类到JSON的转换。
import json

class Student(object):
    def __init__(selfnameagescore):
        self.name = name
        self.age = age
        self.score = score

def student2dict(std):
    return {
        'name': std.name,
        'age': std.age,
        'score': std.score
    }
>>>s = Student('Bob'2088)
>>> print(json.dumps(s, default=student2dict))
{"age"20"name""Bob""score"88}
      Student实例首先被student2dict()函数转换成dict,然后再被顺利序列化为JSON。
      如果要把任意class的实例变为dict:
print(json.dumps(s, default=lambda obj: obj.__dict__))
      有的python对象存在__dict__属性,主要是自定义的class和对应生成的object,用__dict__记录了的属性字典。
>>> class testdict():
...     cls = 0
...     def __init__(selfvalue):
...         self.value = value
...
>>> testdict.__dict__
mappingproxy({'__module__''__main__''cls'0'__init__': , '__dict__': , 
            '__weakref__': , '__doc__'None})
>>> t = testdict(123)
>>> t.__dict__
{'value'123}
       如果我们要把JSON反序列化为一个Student对象实例,loads()方法首先转换出一个dict对象,然后,我们传入的object_hook函数负责把dict转换为Student实例。
def dict2student(d):
    return Student(d['name'], d['age'], d['score'])

>>> json_str = '{"age": 20, "score": 88, "name": "Bob"}'
>>> print(json.loads(json_str, object_hook=dict2student))
<__main__.Student object at 0x10cd3c190>
       json.dumps()提供了一个ensure_ascii参数,如果ensure_ascii为True(默认值),则输出保证将所有输入的非ASCII字符转义。如果确保ensure_ascii为False,这些字符将原样输出。
>>> import json
>>> obj = dict(name='小明'age=20)
>>> s = json.dumps(obj, ensure_ascii=True)
>>> print(s)
{"name""\u5c0f\u660e""age"20}
>>> s = json.dumps(obj, ensure_ascii=False)
>>> print(s)
{"name""小明""age"20}
>>> 

评论