Pyhton学习笔记十:IO编程

文章目录

    • 1. 文件读写
      • 1.1 读文件
      • 1.2 file-like Object
      • 1.3 二进制文件
      • 1.4 字符编码
      • 1.5 写文件
    • 2. StringIO 和 BytesIO
      • 2.1 StringIO
      • 2.2 BytesIO
    • 3. 操作文件和目录
      • 3.1 环境变量
      • 3.2 操作文件和目录
    • 4. 序列化
      • 4.1 JSON
      • 4.2 JSON 进阶
      • 4.3 EX

类别:同步 IO异步 IO
区别:是否等待 IO 执行的结果,但异步的复杂度远高于同步。本章都是同步 IO

1. 文件读写

读写文件步骤:请求操作系统打开一个文件对象(文件描述符),然后通过操作系统提供的接口从这个文件对象中读取数据,或者把数据写入这个文件对象;

1.1读文件

>>> f = open('E:/4_Programe/1_Python/3_Code/1_LiaoDaDa/test.txt', 'r')   # 括号内部传入文件名和标示符,'r'表示读

>>> f = open('E:/4_Programe/1_Python/3_Code/1_LiaoDaDa/test.txt', 'r')
Traceback (most recent call last):
  File "E:\1_Install_Total\8_Anaconda\lib\site-packages\IPython\core\interactiveshell.py", line 2961, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "", line 1, in <module>
    f = open('E:/4_Programe/1_Python/3_Code/1_LiaoDaDa/test.txt', 'r')
FileNotFoundError: [Errno 2] No such file or directory: 'E:/4_Programe/1_Python/3_Code/1_LiaoDaDa/test.txt'   # 若文件不存在,open()函数会抛出一个 IOError 错误,并给出详细信息

>>> f.read()   # 若打开成功,调用read()可以一次性读取文件的全部内容,并用一个str对象表示; 
'Hello, World!'   
>>> f.close()   # 文件使用完毕后必须关闭
  • 由于文件读写时都有可能产生IOError,一旦出错,将无法调用f.close(),所以,为了保证无论是否出错都能正确关闭文件,可使用try...finally来实现:
try:
    f = open('/path/to/file', 'r')
    print(f.read())
finally:
    if f:
        f.close()
  • 但每次都这么写太麻烦,所以 Python 引入了with语句来自动帮我们调用close()方法:
with open('/path/to/file', 'r') as f:
	print(f.read())
  • 调用方式:
with open('E:/4_Programe/1_Python/3_Code/1_LiaoDaDa/test.txt', 'r') as f:
	print(f.read())   # 一次性读取文件全部内容,当文件过大时,内存就爆了
	print(f.read(size))   # 每次最多读取size个字节的内容
    
    print(f.readline())   # 每次读取一行内容,同时也可指定size

    print(f.readlines())   # 一次读取所有内容并按行返回list
    # 运行结果
    ['Hello,World!\n', '2\n', '3\n', '4']

    for line in f.readlines():
        print(line.strip())   # 把末尾的‘\n’删除
  • 如果文件很小,read()一次性读取最方便;如果不能确定文件大小,反复调用read(size)比较保险;如果是配置文件,调用readlines()最方便;

1.2 file-like Object

  • open()函数返回的这种有个read()方法的对象,在 Python 中统称为 file-like Object, 除了 file 外,还可以是内存的字节流,网络流,自定义流等等。file-like Object 不要求从特定类继承,只要写个 read() 方法就行。
  • StringIO就是在内存中创建的 file-like Object,常用作临时缓冲。

1.3 二进制文件

  • 前面讲的都是读取 UTF-8 编码的文本文件,'re'模式打开二进制文件
>>> f = open('E:/4_Programe/1_Python/3_Code/1_LiaoDaDa/test.png', 'rb')
>>> f.read()
b'\xff\xd8\xff\xe1\x00\x18Exif\x00\x00...' # 十六进制表示的字节

1.4 字符编码

# 读取非UTF-8编码的文本文件,给open()函数传入encoding()参数
>>> f = open('E:/4_Programe/1_Python/3_Code/1_LiaoDaDa/test1.txt', 'r', encoding='gbk')
>>> f.read()
'测试'

# 遇到些编码不规范的文件,可能会出现UnicodeDecodeError,此时,接受一个errors参数,表示如果遇到编码错误后如何处理,最简单的方式是直接忽略:
>>> f = open('E:/4_Programe/1_Python/3_Code/1_LiaoDaDa/test1.txt', 'r', encoding='gbk', errors='ignore')

1.5 写文件

>>> f = open('E:/4_Programe/1_Python/3_Code/1_LiaoDaDa/test1.txt', 'w')   # 'w'会覆盖掉文件中原先的内容,相当于删掉后建了个新文件;
>>> f.write('222')   
>>> 3
>>> f.close()

# 使用with语句保险:
with open('E:/4_Programe/1_Python/3_Code/1_LiaoDaDa/test1.txt', 'w') as f:
    f.write('Hello, world!')
  1. 'r':读
  2. 'w':写
  3. 'a':追加
  4. 'r+' == r+w(可读可写,文件若不存在就报错 (IOError))
  5. 'w+' == w+r(可读可写,文件若不存在就创建)
  6. 'a+' ==a+r(可追加可写,文件若不存在就创建)
  7. 对应的,如果是二进制文件,就都加一个 b 就好啦:
    'rb'  'wb'  'ab'  'rb+'  'wb+'  'ab+'

2. StringIO 和 BytesIO

2.1 StringIO

  • 顾名思义就是在内存中读写 str
# 把str写入StringIO,需要先创建一个StringIO,然后像写文件一样写入即可:
>>> from io import StringIO
>>> f = StringIO()
>>> f.write('hello')
5
>>> f.write(' ')
0
>>> f.write('world!')
6
>>> print(f.getvalue())   # getvalue()方法用于获得写入后的str
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!

2.2 BytesIO

  • StringIO 操作的只能是 str,如果要操作二进制数据,就需要用到 BytesIO
# BytesIO实现了在内存中读写bytes:
>>> from io import BytesIO
>>> f = BytesIO() # 创建一个BytesIO
>>> f.write('中文'.encode('utf-8'))  # 写入一些bytes(写入的不是str,而是经过UTF-8编码的bytes)
6
>>> print(f.getvalue())
b'\xe4\xb8\xad\xe6\x96\x87'

>>> from io import BytesIO
>>> f = BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
>>> f.read()
b'\xe4\xb8\xad\xe6\x96\x87'

3. 操作文件和目录

  • 在 python 中执行目录和文件的操作,可以使用内置的os模块直接调用操作系统的接口函数:
>>> import os
>>> os.name  # 操作系统类型
'nt' # Windows系统,若是'posix',则是Linux,Unix或Mac OS X

>>> os.uname()  # uname()函数在Windows上不提供
posix.uname_result(sysname='Darwin', nodename='MichaelMacPro.local', release='14.3.0', version='Darwin Kernel Version 14.3.0: Mon Mar 23 11:59:05 PDT 2015; root:xnu-2782.20.48~5/RELEASE_X86_64', machine='x86_64')

3.1 环境变量

# 在操作系统中定义的环境变量,全部保存在os.environ这个变量中,可以直接查看
>>> import os
>>> os.environ
environ({
     'ALLUSERSPROFILE': 'C:\\ProgramData', 'AMDRMSDKPATH': 'C:\\Program Files\\AMD\\RyzenMasterSDK\\', 'APPDATA': 'C:\\Users\\Jack\\AppData\\Roaming', 'COMMONPROGRAMFILES': 'C:\\Program Files\\Common Files', 'COMMONPROGRAMFILES(X86)': 'C:\\Program Files (x86)\\Common Files', 'COMMONPROGRAMW6432': 'C:\\Program Files\\Common Files', 'COMPUTERNAME': 'DESKTOP-3VRSQ0V', 'COMSPEC': 'C:\\Windows\\system32\\cmd.exe', 'CONDA_DEFAULT_ENV': 'base', 'CONDA_EXE': 'C:\\Anaconda\\Scripts\\conda.exe', 'CONDA_PREFIX': 'C:\\Anaconda', 'CONDA_PROMPT_MODIFIER': '(base) ', 'CONDA_PYTHON_EXE': 'C:\\Anaconda\\python.exe', 'CONDA_SHLVL': '1', 'DRIVERDATA': 'C:\\Windows\\System32\\Drivers\\DriverData', 'HOMEDRIVE': 'C:', 'HOMEPATH': '\\Users\\Jack', 'LOCALAPPDATA': 'C:\\Users\\Jack\\AppData\\Local', 'LOGONSERVER': '\\\\DESKTOP-3VRSQ0V', 'NUMBER_OF_PROCESSORS': '12', 'ONEDRIVE': 'C:\\Users\\Jack\\OneDrive', 'OS': 'Windows_NT', 'PATH': 'C:\\Anaconda;C:\\Anaconda\\Library\\mingw-w64\\bin;C:\\Anaconda\\Library\\usr\\bin;C:\\Anaconda\\Library\\bin;C:\\Anaconda\\Scripts;C:\\Anaconda\\bin;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Windows\\System32\\OpenSSH\\;C:\\Program Files\\Intel\\WiFi\\bin\\;C:\\Program Files\\Common Files\\Intel\\WirelessCommon\\;D:\\01 Installation path\\17 MATLAB\\runtime\\win64;D:\\01 Installation path\\17 MATLAB\\bin;C:\\Anaconda;C:\\Anaconda\\Library\\mingw-w64\\bin;C:\\Anaconda\\Library\\usr\\bin;C:\\Anaconda\\Library\\bin;C:\\Anaconda\\Scripts;C:\\Users\\Jack\\AppData\\Local\\Microsoft\\WindowsApps;D:\\01 Installation path\\07 Bandizip\\;C:\\PyCharm\\PyCharm 2018.3.3\\bin', 'PATHEXT': '.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC', 'PROCESSOR_ARCHITECTURE': 'AMD64', 'PROCESSOR_IDENTIFIER': 'AMD64 Family 23 Model 8 Stepping 2, AuthenticAMD', 'PROCESSOR_LEVEL': '23', 'PROCESSOR_REVISION': '0802', 'PROGRAMDATA': 'C:\\ProgramData', 'PROGRAMFILES': 'C:\\Program Files', 'PROGRAMFILES(X86)': 'C:\\Program Files (x86)', 'PROGRAMW6432': 'C:\\Program Files', 'PROMPT': '(base) $P$G', 'PSMODULEPATH': 'C:\\Program Files\\WindowsPowerShell\\Modules;C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules;C:\\Program Files\\Intel\\Wired Networking\\', 'PUBLIC': 'C:\\Users\\Public', 'PYCHARM': 'C:\\PyCharm\\PyCharm 2018.3.3\\bin;', 'PYTHONIOENCODING': '936', 'SYSTEMDRIVE': 'C:', 'SYSTEMROOT': 'C:\\Windows', 'TEMP': 'C:\\Users\\Jack\\AppData\\Local\\Temp', 'TMP': 'C:\\Users\\Jack\\AppData\\Local\\Temp', 'USERDOMAIN': 'DESKTOP-3VRSQ0V', 'USERDOMAIN_ROAMINGPROFILE': 'DESKTOP-3VRSQ0V', 'USERNAME': 'Jack', 'USERPROFILE': 'C:\\Users\\Jack', 'WINDIR': 'C:\\Windows'})

# 要想获取某个环境变量的值,可以调用os.environ.get('key'):
>>> os.environ.get('PATH')
'C:\\Anaconda;C:\\Anaconda\\Library\\mingw-w64\\bin;C:\\Anaconda\\Library\\usr\\bin;C:\\Anaconda\\Library\\bin;C:\\Anaconda\\Scripts;C:\\Anaconda\\bin;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Windows\\System32\\OpenSSH\\;C:\\Program Files\\Intel\\WiFi\\bin\\;C:\\Program Files\\Common Files\\Intel\\WirelessCommon\\;D:\\01 Installation path\\17 MATLAB\\runtime\\win64;D:\\01 Installation path\\17 MATLAB\\bin;C:\\Anaconda;C:\\Anaconda\\Library\\mingw-w64\\bin;C:\\Anaconda\\Library\\usr\\bin;C:\\Anaconda\\Library\\bin;C:\\Anaconda\\Scripts;C:\\Users\\Jack\\AppData\\Local\\Microsoft\\WindowsApps;D:\\01 Installation path\\07 Bandizip\\;C:\\PyCharm\\PyCharm 2018.3.3\\bin'
>>> os.environ.get('x', 'default')
'default'

3.2 操作文件和目录

  • 操作文件和目录的函数一部分放在os模块中,一部分放到os.path模块中,要注意一下这点
# 查看当前目录的绝对路径:
>>> os.path.abspath('.')
'C:\\Windows\\system32'
# 在某个目录下创建一个新目录,首先把新目录的完整路径表示出来:
>>> os.path.join('C:\\Windows\\system32', 'testdir')
'C:\\Windows\\system32\\testdir'
# 然后创建一个目录:
>>> os.mkdir('C:\\Windows\\system32\\testdir')
# 删除一个目录:
>>> os.rmdir('C:\\Windows\\system32\\testdir')
  • 通过os.path.join()函数来将两个路径合成一个:
part-1/part-2  # 在linux/unix/mac下,此函数返回这样的字符串;

part-1\part-2  # 在windows下返回这样的字符串;
  • 通过os.path.split()函数来拆分路径
>>> os.path.split('C:\\Windows\\system32\\testdir\\file.txt')
('C:\\Windows\\system32\\testdir', 'file.txt')   # 后一部分总是最后级别的目录或文件名

>>> os.path.splitext('/path/to/file.txt')   # 通过此函数直接得到文件扩展名
('/path/to/file', '.txt')
# 合并,拆分路径的函数不要求真实存在,只是对字符串进行操作
  • 文件操作使用以下的函数,假定当前目录下有一个test.txt文件:
# 对文件重命名:
>>> os.rename('test.txt', 'test.py')
# 删除文件:
>>> os.remove('test.py')
  • 复制文件的函数在os模块中不存在,可以通过shutil模块中的copyfile()函数来实现复制文件;

  • 利用 python 的特性来过滤文件:

# 列出当前目录下的所有目录:
>>> [x for x in os.listdir('.') if os.path.isdir(x)]
['0409', 'AdvancedInstallers', 'am-et', 'AppLocker', 'appraiser', 'AppV', 'ar-SA', 'bg-BG', 'Boot', 'Bthprops', 'CatRoot', 'catroot2', 'CodeIntegrity',...]
>>> [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']

4. 序列化

  • 在程序运行过程中,所有的变量都是在内存中,比如定义一个 dict,我们可以随时修改变量,比如把name修改成Bill,但是一旦程序结束后,变量所占用的内存就会被操作系统全部收回,若没有把修改后的Bill存储到磁盘上,下次重新运行程序,变量又被初始化为Bob
d = dict(name='Bob', age=20, score=88)
  • 把变量从内存中变成可存储或传输的过程叫做序列化,在 python 中叫pickling,序列化之后,就可以把序列化之后的内容写入磁盘或者通过网络传输到别的机器上,反过来,把变量内容从序列化的对象重新读到内存里叫做反序列化,即unpickling
  • python 提供了pickle模块来实现序列化:
# 把一个对象序列化:
>>> import pickle
>>> d = dict(name='Bob', age=20, score=88)
>>> pickle.dumps(d)  # 通过此方法把任意对象序列化成一个bytes
b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x03\x00\x00\x00Bobq\x02X\x03\x00\x00\x00ageq\x03K\x14X\x05\x00\x00\x00scoreq\x04KXu.'

# 将序列化后的对象写入一个file-like Object:
>>> f = open('dump.txt', 'wb')
>>> pickle.dump(d, f)
>>> f.close()

# 将对象从磁盘读到内存:
>>> import pickle
>>> f = open('dump.txt', 'rb')
>>> d = pickle.load(f)   # 用此方法从一个file-like Object中直接反序列化出对象
>>> f.close
<built-in method close of _io.BufferedReader object at 0x0000024248596E08>
>>> d
{
     'name': 'Bob', 'age': 20, 'score': 88}
# 这个变量和刚才哪个变量只是内容相同,两者完全是不相干的对象
  • pickle 的问题和其他编程语言特有的序列化问题一样,就是只能用于 python,并且不同版本之间的 python 是不兼容的。

4.1 JSON

  • 如果想要在不同的编程语言之间传递对象,就必须把对象序列化成标准格式,JSON 表示出来就是一个字符串,可被所有语言读取,同时可在 Web 页面中读取,非常方便;
  • JSON 表示的对象就是标准的 JavaScript 语言的对象,python 内置的json模块提供了非常完善的 python 对象到 JSON;
# dumps()方法返回一个str,内容就是标准的JSON,类似的dump()方法可以直接把JSON写入一个file-like Object
>>> import json
>>> d = dict(name='Bob', age='20', score=88)
>>> json.dumps(d)
'{"name": "Bob", "age": "20", "score": 88}'

# 同样,把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'}

4.2 JSON进阶

  • python 的dict对象可以直接序列化为 JSON 的{},那怎么把用class表示的对象序列化呢,比如定义的Student类:
import json

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

s = Student('Bob', 20, 88)
print(json.dumps(s))

# 运行此代码,会得到一个错误:
Traceback (most recent call last):
  ...
TypeError: Object of type Student is not JSON serializable
  • 错误的原因是Student对象不是一个可序列化的 JSON 对象,但这肯定不合理,仔细看dumps()方法的参数列表,除了第一个必须的obj参数外,dumps方法还提供了一大堆可选参数:https://docs.python.org/3/library/json.html#json.dumps 这些可选参数可以允许我们自己定制 JSON 序列化,前面的代码之所以无法把Student类实例序列化为 JSON,是因为默认情况下,dumps方法不知道如何将Sdudent实例变为一个 JSON 的{}对象;
  • 可选参数default就是把任意一个对象变成一个可序列化为 JSON 的对象,我们之需要为Sdudent专门写一个转换函数,再把函数传进去即可:
def student2dict(std):
	return {
     
		'name': std.name,
		'age': std.age,
		'score': std.score
	}
  • 这样Student实例首先被Student2dict()函数转换成dict,然后再被顺序序列化为 JSON:
>>> print(json. dumps(s, default=student2dict))
{
     "age": 20, "name": "Bob", "score": 88}
  • 不过,下次如果遇到一个Teacher类的实例,照样无法序列化为 JSON。我们可以偷个懒,把任意class的实例变为dict
print(json. dumps(s, default=lambda obj: obj.__dict__))
  • 因为通常class的实例都有一个__dict__属性,他就是一个dict,用来储存实例变量,也有少数例外,比如定义了__slots__的 class;
  • 同样的道理,如果我们要把 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>
  • 打印出的是反序列化的Student实例对象。

4.3 EX

对中文进行 JSON 序列化时,json.dumps()提供了一个ensure_ascii参数,观察该参数对结果的影响:

import json

obj = dict(name = '小明', age = 20)
s = json.dumps(obj, ensure_ascii = True)
print(s)

# 运行结果
{
     "name": "\u5c0f\u660e", "age": 20}

你可能感兴趣的:(Python入门笔记)