# 【【IO】】
# 【文件读写】
'''
读写文件是最常见的IO操作。Python内置了读写文件的函数,用法和C是兼容的。
读写文件前,我们先必须了解一下,在磁盘上读写文件的功能都是由操作系统提供的,现代操作系统不允许普通的程序直接操作磁盘,所以,读写文件就是请求操作系统打开一个文件对象(通常称为文件描述符),然后,通过操作系统提供的接口从这个文件对象中读取数据(读文件),或者把数据写入这个文件对象(写文件)
'''
# 读文件
f = open('1.py','r')
print (f.read())
f.close()
# 由于文件读写会产生IOError ,所以采用以下方法
try:
f = open('1.py','r')
print (f.read())
except IOError:
raise
finally:
if f:
f.close()
print ('*****************')
#但是每次这么写太繁琐,python引入了with语句来自动帮我们调用close()方法
with open('1.py','r') as f:
for line in f.readlines():
print (line)
'''
myslice = slice(3)
print(myslice)
arr = list(range(10))
print(arr)
print(arr[myslice])
'''
with open('1.py','r') as f:
for line in f.readlines():
print (line.split())
'''
['myslice', '=', 'slice(3)']
['print(myslice)']
['arr', '=', 'list(range(10))']
['print(arr)']
['print(arr[myslice])']
'''
'''
【备注】
调用read,会一次性读取文件的全部内容,如果文件有10G,内存就爆了,所以为了保险起见,可以反复调用 read(size),每次最多读取size 个字节。
另外, readline()可每次读取一行内容,调用readlines()一次性读取所有内容并按行返回list.
如果文件很小,read()。如果不能确定文件大小,反复调用read(size). 如果是配置文件,readlines()
'''
# file-like object
'''
像open()函数返回的这种有个read()方法的对象,在python中统称为file-like object 。除了file 外,还可以是内存的字节流,网络流,自定义流等。
file-like Object 不要求从特定类继承,只要写个read()就行。
StringIO 就是在内存中创建的file-like Object.常用作临时缓冲。
'''
#二进制文件 比如图片、视频等 用'rb'模式打开文件即可
f = open('flower.png','rb')
print (f.read()) #b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\xcd\x00\x00\x00`\x08\x06\x00\x00\x00\xbe>\xf6Y\x00\x00\x18\'iCCPICC Profile\x00\x00X\x85\x95y\x07T\x14K\xd4f\xf5\xf4$\xc2\x90sFr\xceYr\xce9\x8a\xca\x9038$AA\x04D\x82\x8aH\x10\x05D\x01\x11D\xc1H\x12\x01\t\xa2\x88$\t\x8a\x82( \xa0\xa8\x18\x00I\xb2M\xd0\xf7\xfe\xb7{v\xcf\xd69\xd5\xfd\xcd\xed[\xb7\xbe[u\xab\xaao\x0f\x00\xec\x0c\xc4\xd0\xd0@\x14\r\x00A\xc1\xe1$\x1bC\x1d\x1e\'g\x17\x1e\xdc{\x80\x03\x10 \x03R\x80\x85\xe8\x11\x16\xaamee\x06\x90\xf2\xe7\xfe?\xcb\xd20\xa2\x8b\x94\x17\x12[\xb6\xfe\xf7\xe7\xff\xd7B\xeb\xe9\x15\xe6\x01\x00d\x85`w\xcf0\x8f \x04\xdf\x03\x00\xcd\xe2\x11J\n\x07\x00\xd3\x8d\xc8\xf9\xa2\xc2C\xb7\xf0\x02\x82\x19H\x08A\x00\xb0\xe8-\xec\xb3\x83Y\xb6\xb0\xfb\x0e\x16\xdf\xd6\xb1\xb3\xd1E\xb0\x16\x00xJ"\x91\xe4\x03\x00\xd5\x16o\x9eH\x0f\x1f\xc4\x0e\x15\xc2\x11K\x17\xec\xe9\x17\x8c\xa8\xc6#X\xc3\xc3\x97\xe8\t\x00[\x0b\xa2#\x1e\x14\x14\xb2\x85\xe7\x11,\xec\xfe/;>\xff\xc3\xa6\xfb_\x9bD\xa2\xcf_\xbc\xe3\xcbv\xc1\xeb\xf9\x85\x85\x06\x12\xa3\xff?\x87\xe3\xff]\x82\x02#\xfe\xf4\xb1\x07\xa9\x94\xbe$#\x9b-\x9f\x91q+\x0f\x081\xdd\xc2\x94\x08n\x0cv\xb7\xb0D0\x1d\x82\x9f\xf8yn\xebo\xe1Q\xdf\x08#\xfb]\xfdy\x8f0]d\xcc\x00\x13\x00(\xe0I\xd43E0\x07\x82\x99"\x02\xec\xb5w\xb1,\x91\xb4\xdd\x16\xd1GY\xf8\x85\x1b\xdb\xedbwR\x88\xcd\xae}Tdp\xa0\x85\xd9\xae\x9d\x14_/\xe3?\xf8\x92W\x98\xbe\xed\x1f\x1do?\x03c\x04#\x91\x86\xba\x17\xe3k\xe7\xb8\xc3\x13\xd5\x1e\xe9\xe7`\x81`*\x04\xf7\x85\x05\xd8\x9a\xee\xb6\x1d\x8f\xf1\xd5\xb5\xf8\xa3C\x8a\xb0\xd9\xe2\xcc\x8f\xe0\x9f\xde$\x03\x9b\x1d\x1d\x98%(\xec\x8f_\xb0\xa4\x07q\xbb/$\x16`\xadp_;\xa3\x9d\xb6\xb0\x93W\x98\x93\xd9\x1f\x0e\x9e^z\xfa;\x1c`O\xaf`\xfb]n0\x12]:6\xbbm\x93C\x03\xadv\xf5\xe1K^\x81\x866;\xe3\x0c\xdf\n\x8b\xb4\xfd\xd3v \x1c\t\xb0\x9dq\x80\xdf\xfb\x13M\xacv\xfbZ\n\r\xb7\xb2\xdb\xe1\x86F\x013\xa0\x0b\xf4\x00\x0f\x88@\xaa;\x08\x01\xfe\xc0\xafg\xbev\x1e\xf9\xb5\xf3\xc4\x00\x10\x01\t\xf8\x00/ \xb1+\xf9\xd3\xc2q\xfbI0r\xb5\x051\xe03\x82\xbc@\xd8\xdfv:\xdbO\xbd@$"\xdf\xf8+\xdd\xb9J\x00\xef\xed\xa7\x91\xdb-\x02\xc0\x07\x04\x07\xa1\xd9\xd0\x1ah5\xb4\x19r\xd5B\xaa,Z\x19\xad\xf2\xa7\x1d\x0f\xf5\x9f^\xb1\xfaX=\xac\x11\xd6\x00+\xf2\x97\x87\x07\xc2:\x10\xa9$\xe0\xf7\x7f\x90\x99"w/\xc4\xbb-.\xc1\x7f|\xf8\xc7\x1e\xe6\x03\xa6\x1f\xf3\x1e3\x84\x99\xc0\xbc\x02\x0e`j\xdb\xca\xae\xd6A\xbf\x04\xd2\x7f\x98\xf3\x00s0\x81X3\xd8\xf5\xce\xfd\xdf\xde\xa1\x05\x11\xd6\nh\x1d\xb4:\xc2\x1f\xe1\x8efB\xb3\x01\t\xb4<\xe2\x896Z\x13\xf1M\x01\x91\xfe\x9ba\xc4_n\xff\x8c\xe5\x7f\xfb\xdbb\xfdo\x7fv\xe5T\xa2T\n\xbb,\xdc\xff\xce\x8c\xee_\xad\xffZ\xd1\xfd\xd7\x18y"w\xj\xc1\xdb\xe33\x04\xa1u\xe9\x81>W\x07m\x0b7Z\'\xf2unh\\{\xc5}\x0f\xb4\xc4\xe3\x99*\x12\rbj^\xed[\\%\x86\\8P\xc6\x8cv#\xb3\xf5\xff\x01(\xb3\xc1\x91\x80B\x14\x17\x00\x00\x00\x00IEND\xaeB`\x82'
# 写文件
f = open('1.py','w')
f.write('Hello world!')
f.close()
'''
可以反复调用write()来写入文件,但是务必要调用close()来关闭文件。当我们写文件时,操作系统往往不会立即把数据写入磁盘,而是放到内存缓存起来,
空闲的时候再慢慢写入。只有调用close()时候,操作系统才保证把没写入的数据全部写入磁盘。忘记调用close()的后果是数据可能是数据只写了一部分到
磁盘,剩下的丢失了。所以,还是用with语句来的保险
'''
with open('1.py','w') as f:
f.write('Hello world!!!')
#写入特定编码 的文本文件,给open()函数传入encoding参数,将字符串自动转成指定编码
#以'w'模式写入,如果文件以ing存在,会覆盖。可以用'a',追加
with open('1.py','a') as f:
f.write('wxue')
#【StingIO BytesIO】很多时候,数据读写不一定是文件,也可以在内存中du读写。
#StringIO 是在内存中读写str
#例子 把str写入StringIO
from io import StringIO
f = StringIO()
print (f.write('hello')) #5
print (f.write(' ')) #1
print (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!
'''
# BytesIO 二进制
from io import BytesIO
f = BytesIO()
f.write('中文'.encode('utf-8'))
print (f.getvalue()) #b'\xe4\xb8\xad\xe6\x96\x87'
#读
from io import BytesIO
f = BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
print (f.read()) #b'\xe4\xb8\xad\xe6\x96\x87'
# 【操作文件和目录】
import os
print (os.name) #posix 【备注】如果是posix ,说明是Linux Unix 或Mac OS X ,如果是nt,说明是Windows
#获取详细的系统信息
print (os.uname()) #posix.uname_result(sysname='Darwin', nodename='surendeMacBook-Air.local', release='16.7.0', version='Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64', machine='x86_64')
#环境变量
print (os.environ) #environ({'PATH': ......ruby-2.4.0@global/bin:/Users/suren/.rvm/rubies/ruby-2.4.0/bin:.:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/MacGPG2/bin:/Library/Java/JavaVirtualMachines/jdk1.8.0_77.jdk/Contents/Home//bin:/Users/suren/ST/anaconda/anaconda/bin:/Users/suren/android-sdk-macosx/platform-tools:/Users/suren/android-sdk-macosx/tools:/Library/Frameworks/Python.framework/Versions/3.6/bin/python3.6/bin:/usr/local/mysql/bin:/Users/suren/.rvm/bin', 'IRBRC': '/Users/suren/.r..........', '_system_arch': 'x86_64', 'rvm_bin_path': '/Users/suren/.rvm/bin', 'rvm_prefix': '/Users/suren', 'PS1': '\\h:\\w \\u$', 'VERSIONER_PYTHON_VERSION': '2.7', 'LOGNAME': 'suren', 'rvm_version': '1.29.3 (latest)', 'XPC_SERVICE_NAME': 'com.jetbrains.pycharm.1732', 'PWD': '/Users/suren/PycharmProjects/untitled1', 'PYCHARM_HOSTED': '1', 'PYTHONPATH': '/Users/suren/PycharmProjects/untitled1', 'SHELL': '/bin/bash', '_system_type': 'Darwin', 'PYTHONIOENCODING': 'UTF-8', 'MY_RUBY_HOME': '/Users/suren/.rvm/rubies/ruby-2.4.0', '_system_version': '10.12', 'VERSIONER_PYTHON_PREFER_32_BIT': 'yes', 'USER': 'suren', 'GEM_HOME': '/.........', 'rvm_path': '/Users/suren/.rvm', 'TMPDIR': '/var/folders/7b/v65b2_394_jdz_24k80htg840000gn/T/', 'SSH_AUTH_SOCK': '/private/tmp/com.apple.launchd.zVVVhXl5p7/Listeners', 'RUBY_VERSION': 'ruby-2.4.0', 'XPC_FLAGS': '0x0', 'GEM_PATH': '/Users/suren/.rvm/gems/ruby-2.4.0:/Users/suren/.rvm/gems/ruby-2.4.0@global', 'PYTHONUNBUFFERED': '1', '_system_name': 'OSX', '__CF_USER_TEXT_ENCODING': '0x1F5:0x19:0x34', 'Apple_PubSub_Socket_Render': '/private/tmp/com.apple.launchd.VgL0SUrWb9/Render', 'LC_CTYPE': 'zh_CN.UTF-8', 'HOME': '/Users/suren', '__PYVENV_LAUNCHER__': '/Library/Frameworks/Python.framework/Versions/3.6/bin/python3.6'})
print (os.environ.get('PYCHARM_HOSTED')) #1
print (os.environ.get('key','default')) #default
# 操作文件和目录 相关函数一部分放在os 一部分放在os.path
#查看当前目录的绝对路径
print (os.path.abspath('.')) #/Users/suren/PycharmProjects/untitled1
#在某个目录下创建新的目录,首先把新目录的完整路径表示出来
print (os.path.join(os.path.abspath('.'),'testdir')) #/Users/suren/PycharmProjects/untitled1/testdir
#创建目录
#os.mkdir('/Users/suren/PycharmProjects/untitled1/testdir')
#删除目录
#os.rmdir('/Users/suren/PycharmProjects/untitled1/testdir')
#拆分路径
print (os.path.split('/Users/suren/PycharmProjects/untitled1/testdir')) #('/Users/suren/PycharmProjects/untitled1', 'testdir')
print (os.path.split('/Users/suren/PycharmProjects/untitled1/1.py')) #('/Users/suren/PycharmProjects/untitled1', '1.py')
# 得到文件扩展名
print (os.path.splitext('/Users/suren/PycharmProjects/untitled1/1.py')) #('/Users/suren/PycharmProjects/untitled1/1', '.py')
#【备注】这些合并、拆分函数并不要求目录和文件要真是存在,只是对字符串进行操作
# 文件操作使用下面的。
#重命名
#os.rename('1.py','1.txt')
#删除文件
#os.remove('1.txt')
'''
但是复制文件的函数居然在os模块中不存在!原因是复制文件并非由操作系统提供的系统调用。理论上讲,我们通过上一节的读写文件可以完成文件复制,只不过要多写很多代码。
幸运的是shutil模块提供了copyfile()的函数,你还可以在shutil模块中找到很多实用函数,它们可以看做是os模块的补充
'''
# 利用Python的特性来过滤文件。比如要列出当前目录下的所有目录
print ([x for x in os.listdir('.') if os.path.isdir(x)]) #['.idea', '__pycache__']
#列出所有py 文件
print ([x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1] == '.py' ]) #['1.py', 'def.py', 'dict&set.py', 'hello.py', 'io_test.py', 'list&tuple.py', 'senior_pro.py']
# 练习题1 利用os 模块 实现dir -l
import os
import time
import re
def listFile(path):
print ( '权限\t文件数\t用户名\t群组名\t大小\t月份\t日期\t时间\t文件名')
for x in os.listdir(path):
dir = os.path.join(path,x)
st = os.stat(dir)
print (oct(st.st_mode)[-3:],end='\t')
print (numOfFiles(dir),end='\t')
print (st.st_uid,end='\t')
print (st.st_gid,end='\t')
print (st.st_size,end='\t')
lc_time = time.localtime(st.st_mtime)
print (time.strftime('%b',lc_time),end='\t')
print (lc_time.tm_mday,end='\t')
print (time.strftime('%H:%M',lc_time),end='\t')
print (x)
#计算文件夹数量,最小1
def numOfFiles(path,num=1):
try:
for x in os.listdir(path):
dir = os.path.join(path,x)
if os.path.isdir(dir):
num += 1
num = numOfFiles(dir,num)
except BaseException as e:
pass
finally:
return num
dirpath = os.path.abspath('.')
listFile(dirpath)
'''
权限 文件数 用户名 群组名 大小 月份 日期 时间 文件名
755 1 501 20 204 Jul 21 17:29 .idea
644 1 501 20 18 Jul 21 17:31 1.py
755 1 501 20 102 Jul 19 07:51 __pycache__
644 1 501 20 7420 Jun 25 17:54 def.py
644 1 501 20 2080 Jun 23 10:26 dict&set.py
644 1 501 20 45467 Jul 21 09:59 flower.png
644 1 501 20 93 Jul 19 07:49 hello.py
644 1 501 20 139629 Jul 21 17:31 io_test.py
644 1 501 20 1267 Jun 23 09:33 list&tuple.py
644 1 501 20 43567 Jul 21 08:00 senior_pro.py
'''
'''
# 练习题2 能在当前目录以及当前目录所有子目录下查找文件名包含指定字符串的文件,并答应出相对路径。
import os
keyword = input('Input Keyword:')
up = '.'
result = []
def check(up,keyword):
all = os.listdir(up)
for x in all:
try:
abs_x = os.path.join(up,x)
if os.path.isdir(abs_x):
up = os.path.join(up,x)
check(up,keyword)
#up = os.path.split(up)[0]
if os.path.isfile(abs_x):
if keyword in x:
result.append(abs_x)
except:
continue
check(up,keyword)
print ('\n============Find %d files===========\n' % len(result))
num = 0
for r in result:
num += 1
print ('%d %s' % (num,r))
print ('\n=============End====================\n')
os.system('pause')
'''
# 【序列化】
'''
在程序运行的过程中,所有的变量都是在内存中,比如,定义一个dict:
d = dict(name='Bob', age=20, score=88)
可以随时修改变量,比如把name改成'Bill',但是一旦程序结束,变量所占用的内存就被操作系统全部回收。如果没有把修改后的'Bill'存储到磁盘上,下次重新运行程序,变量又被初始化为'Bob'。
我们把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,在其他语言中也被称之为serialization,marshalling,flattening等等,都是一个意思。
序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。
反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling。
Python提供了pickle模块来实现序列化。
'''
# 把一个对象序列化并写入文件
import pickle
d = dict(name='Bob',age=20,score=88)
#pickle.dumps()可以把任意对象序列化成一个bytes,就可以把这个bytes写入文件。
print (pickle.dumps(d)) #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.'
#或者用pickle.dump(d,f)直接把对象序列化后写入一个file-like object
f = open('dump.txt','wb')
pickle.dump(d,f)
f.close()
#看看写入的dump.txt文件,一堆乱七八糟的内容,这些都是Python保存的对象内部信息\
#当我们要把对象从磁盘读到内存,可以先把内容读到一个bytes,然后用pickle.loads()反序列化出对象。也可以用pickle.load() 从一个file-like object 中直接反序列化出对象,
f = open('dump.txt','rb')
d = pickle.load(f)
f.close()
print (d) #{'name': 'Bob', 'age': 20, 'score': 88} #变得内容又回来了。【注意】这个变量和原来的变量是完全不想干的对象,它们只是内容相同而已。
'''
Pickle的问题和所有其他编程语言特有的序列化问题一样,就是它只能用于Python,并且可能不同版本的Python彼此都不兼容,因此,只能用Pickle保存那些不重要的数据,不能成功地反序列化也没关系。
'''
# JSON
'''
如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,比如XML,但更好的方法是序列化为JSON,因为JSON表示出来就是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。JSON不仅是标准格式,并且比XML更快,而且可以直接在Web页面中读取,非常方便。
JSON表示的对象就是标准的JavaScript语言的对象,JSON和Python内置的数据类型对应如下:
JSON类型 Python类型
{} dict
[] list
"string" str
1234.56 int或float
true/false True/False
null None
Python内置的json模块提供了非常完善的Python对象到JSON格式的转换
'''
#把python对象变成一个JSON
import json
d = dict(name='Bob',age=20,score=88)
print (json.dumps(d)) # {"name": "Bob", "age": 20, "score": 88}
'''dumps()方法返回一个str,内容就是标准的JSON.
类似的,dump()可以直接把一个JSON 写入一个file-like object
反序列化,loads() 是把JSON的字符串反序列化,后者是从file-like object 中读取字符串并反序列化。
'''
json_str = '{"name": "Bob", "age": 20, "score": 88}'
print (json.loads(json_str)) #{'name': 'Bob', 'age': 20, 'score': 88}
# JSON 进阶
# Python的dict对象可以直接序列化为JSON的{},不过更多时候,我们更喜欢用class表示对象,然后序列化
import json
class Student(object):
def __init__(self,name,age,score):
self.name = name
self.age = age
self.score = score
d = Student('Bob',20,88)
#print (json.dumps(d)) #TypeError: Object of type 'Student' is not JSON serializable
'''
错误的原因是Student对象不是一个可序列化为JSON的对象。
如果连class的实例对象都无法序列化为JSON,这肯定不合理!
别急,我们仔细看看dumps()方法的参数列表,可以发现,除了第一个必须的obj参数外,dumps()方法还提供了一大堆的可选参数:
https://docs.python.org/3/library/json.html#json.dumps
这些可选参数就是让我们来定制JSON序列化。前面的代码之所以无法把Student类实例序列化为JSON,是因为默认情况下,dumps()方法不知道如何将Student实例变为一个JSON的{}对象。
可选参数default就是把任意一个对象变成一个可序列为JSON的对象,我们只需要为Student专门写一个转换函数,再把函数传进去即可
'''
def student2dict(std):
return {
'name' : std.name,
'age': std.age,
'score':std.score
}
# 这样,Student实例首先被student2dict()转化成dict,然后再被顺利序列化为JSON。
print (json.dumps(d,default=student2dict)) #{"name": "Bob", "age": 20, "score": 88}
# 不过,下次如果遇到一个Teacher类的实例,照样无法序列化为JSON。我们可以用class实例的__dict__属性,它就是一个dict(用来存储实例变量的)
print (json.dumps(d,default=lambda obj:obj.__dict__)) #{"name": "Bob", "age": 20, "score": 88}
# 把JSON反序列化为一个Student对象实例,loads()方法首先转换出一个dict对象,然后,传入的object_hook函数负责把dict转成Student实例
def dict2student(d):
return Student(d['name'],d['age'],d['score'])
json_str = '{"name":"wxue","score":98,"age":23}'
print (json.loads(json_str,object_hook=dict2student)) #<__main__.Student object at 0x1022c2e10>
# 练习题 对中文进行JJSON序列化时,json_dumps()提供了一个ensure_ascii参数,观察该参数对结果的影响
import json
obj = dict(name='小明',age=20)
s = json.dumps(obj)
print (s) #{"name": "\u5c0f\u660e", "age": 20}
'''
[分析]json.dumps 序列化时对中文默认使用的ascii编码.想输出真正的中文需要指定ensure_ascii=False:
'''
s = json.dumps(obj,ensure_ascii=False)
print (s) #{"name": "小明", "age": 20}
'''
小结
Python语言特定的序列化模块是pickle,但如果要把序列化搞得更通用、更符合Web标准,就可以使用json模块。
json模块的dumps()和loads()函数是定义得非常好的接口的典范。当我们使用时,只需要传入一个必须的参数。但是,当默认的序列化或反序列机制不满足我们的要求时,我们又可以传入更多的参数来定制序列化或反序列化的规则,既做到了接口简单易用,又做到了充分的扩展性和灵活性。
'''