python文件操作
python文件操作
- 三元运算
- 字符编码转换
- 文件处理
三元运算
三元运算又称为三目运算,是对简单的条件语句的简写。
@requires_authorization # 简单条件语句 a = 2 b = 5 if a < b: # if判断大小 val = a else: val = b print(val) # val = 2 # 改写成三元运算 val = a if a < b else b print('val:',val) # val: 2
字符编码转换
编码回顾
这哥们的这段话说的太对了,搞Python不把编码彻底搞明白,总有一天它会猝不及防坑你一把。
回顾一下编码种类情况
ASCII 占1个字节 只支持英文 GB2312 占2个字节 支持6700+汉字 GBK GB2312的升级版 支持21000+汉字 Shift_JIS 日本编码 ks_c_5601_1987 韩国编码 TIS-620 泰国编码
由于每个国家都有自己的字符,所以其对应关系也涵盖了自己国家的字符,但是以上编码都存在局限性,即:仅局限本国字符,无其他国家字符的对应关系。应运而生了万国码,它涵盖了全球所有的文字和二进制对应关系
- Unicode 2-4 个字节,已经收录了136690个字符,并还在不断扩张中
Unicode起到了2个作用:
- 直接支持全球所有语言,每个国家都可以不再使用自己之前的旧编码,用Unicode就可以了
- Unicode包含了跟全球所有国家编码的映射关系
Unicode解决了字符和二进制的对应关系,但是使用Unicode表示一个字符,太浪费空间,比如:利用Unicode表示"python"需要12个字节才能解决,比原来ASCII表示增加了1倍。
由于计算机的内存比较大,并且字符串再内容中表示时也不会特别大,所以内容用Unicode来处理,但是在存储和网络传输中一般数据会比较多,那么增加1倍是无法容忍的!
为了解决存储和网络传输的问题,出现了UTF,即:对Unicode中的进行转换,以便于在存储和网络传输时可以节省空间。
- UTF-8:使用1-4个字节来表示所有字符;优先使用1个字符,无法满足则使增加1个字节,最多4个字节。英文占1个字节 ,欧洲语系占2个、东亚占3个,其它及特殊字符占4个。
- UTF-16:使用2-4个字节表示所有字符;优先使用2个字节,否则使用4个字节表示。
- UTF-32:使用4个字节来表示所有字符。
总结:UTF是为了Unicode编码设计的一种在存储和传输时节省空间的编码方案。
文件处理
引子
问题1:给你一个文件 "学生名单.txt" ,如何查看内容?
答:
1.安装文本编辑器软件
2.选中右键,利用文本编辑器软件打开
3.查看 or 写入
4.保存,关闭
问题2:文件在硬盘上是如何存储的?
答:
以某种编码格式的"01010101010101"保存在硬盘
python处理文件
python文件分为读、写、修改
读文件
首先在pycharm定义 "联系方式.txt" 文本文件
姓名 年龄 联系电话 老王 23 13003123113 小李 20 18947123319 张三 25 13888889888 李四 18 18492134132
那么我们想读取到文件中的内容,需要怎么办呢?
示例1:
f = open('联系方式.txt',mode='r',encoding='UTF-8') data = f.read() print(data) f.close() '联系方式.txt' 表示路径,因为就在当前目录下,所以不用写路径也可以 mode = 'r' 表示只读模式 encoding = 'UTF-8' 表示将硬盘上的01010101按照UTF-8的规则去断句,再将“断句”后的每一段0101010转换成unicode的 01010101,unicode对照表中有01010101和字符的对应关系。 f.read() 表示读取所有内容,内容是已经转换完毕的字符串 f.close() 表示关闭文件 # 在这里需要注意的是,用什么编码存的文件,就要用什么编码去打开 # 还有一种打开的方式 f = open(file = 'D:\py_study\day06-文件处理\联系方式.txt',mode = 'r',encoding='utf-8') data = f.read() print(data) f.close() 解释一下区别: 如果要打开的这个文件存放在当前目录,那么就可以不用写绝对路径,只需要写相对路径就可以(或者可以不用写) 如果打开的文件不存放在当前目录,那么就必须要写上绝对路径
示例2:
f = open('联系方式.txt','rb') data = f.read() print(data) # 打印的是16进制 f.close() '联系方式.txt' # 代表的是文件路径,因为在当前目录下,所以就不用写路径了 mode = 'rb' # 表示只读 f.read() # 表示读取所有内容,内容是硬盘上原来以某种编码保存的 010101010,即:某种编码格式的字节类型 f.close() # 表示关闭文件
运行结果如下:
姓名 年龄 联系电话 老王 23 13003123113 小李 20 18947123319 张三 25 13888889888 李四 18 18492134132
那么示例1和示例2有什么区别呢?
在于示例2并没有指定encoding,这是为何?是因为直接以rb的模式打开了文件,rb是指二进制模式,数据读到内存李直接是bytes格式,如果想内容,还需要手动decode,因此在文件打开阶段,不需要指定编码。
那么,我们不知道要处理的文件编码怎么办呢?
import chardet # 导入工具包 f = open('联系方式.txt',mode='rb') # 以二进制模式打开 data = f.read() f.close() result = chardet.detect(open('联系方式.txt',mode = 'rb').read()) # 将读取的文件交给detect函数去处理 print(result)
运行结果为:
{'encoding': 'utf-8', 'confidence': 0.99, 'language': ''} # 即99%几率是utf-8
注意:
- 文件操作时,以r或者rb模式打开,则只能读,无法写入;
- 硬盘上保存的文件都是某种编码的0101010,打开时需要注意:
- rb,直接读取文件保存时原生的01010101,在python中用字节类型表示
- r和encoding,读取硬盘上的01001010,并按照encoding指定的编码格式进行断句,再将断句后的每一段0101001转换成unicode的01010101101,在python中用字符串类型表示
循环文件
f = open('联系方式.txt','r',encoding='utf-8') for line in f: print(line) # 会有空格 f.close()
运行结果如下:
小明 北京 10402384103 老王 上海 10403145103 校长 深圳 13888888888 王寒 珠海 12389874589 黎明 北京 10123451313
写文件
f = open('写文件.txt','w',encoding='utf-8') f.write('北大本科美国留学一次50,微信号:xxxxx') f.close()
上述操作语法解释:
'写文件.txt' # 表示文件路径 mode = 'w' # 表示只写 encoding='utf-8' # 将要写入的unicode字符编码成utf-8编码 f.write() # 将写入文件内容,写入的内容是unicode字符串类型,内部会根据encoding转换为制定编码的 01101010101,即:字节类型 f.close() # 关闭文件
二进制写
# 二进制写 f = open('写文件.txt',mode='wb') f.write(' hello python'.encode('utf-8')) f.close()
注意:
1、文件操作时,以w或者wb模式打开,则只能写,并且在打开的同时会先将内容清空。
2、写入到硬盘上时,必须是某种编码的0101010,打开时需要注意
- wb,写入时需要直接传入以某种编码的01010100,即:字节类型
- w和encoding,写入时需要传入unicode字符串,内部会根据encoding指定的编码将unicode字符串转换为该编码的01010101
追加
将内容增加到文件尾部
f = open('联系方式.txt','a',encoding='utf-8') f.write('\n杜珊珊 北京 13004562134') f.close()
运行结果:
小明 北京 10402384103 老王 上海 10403145103 校长 深圳 13888888888 王寒 珠海 12389874589 黎明 北京 10123451313 杜珊珊 北京 13004562134 # 这行是新添加的
注意:
1、文件操作时,以a或ab模式打开,则只能追加,即:在原有的内容的尾部追加内容
2、写入到硬盘上时,必须是某种编码的01010101,打开时需要注意:
- ab,写入时要直接传入以某种编码的01010101,即:字节类型
- a和encoding,写入时需要传入unicode字符串,内部会根据encoding制定的编码将unicode字符串转换为该编码的 010101010
读写模式
读写模式
读写模式,将内容追加到文件尾部
f = open('联系方式.txt','r+',encoding='utf-8') data = f.read() print(data) f.write('\n 肖亚飞 河南 12345678123') f.close()
运行结果如下:
肖亚飞 河南 12345678123 肖亚飞 河南 12345678123 肖亚飞 河南 12345678123 肖亚飞 河南 12345678123 肖亚飞 河南 12345678123 肖亚飞 河南 12345678123
写读模式
写读模式,会将源文件中内容全部清空掉
f = open('写文件.txt',mode='w+',encoding='utf-8') f.write(' 肖亚飞 河南 12345678123') f.close()
运行结果如下:
肖亚飞 河南 12345678123
文件操作的其他功能
def fileno(self, *args, **kwargs): # real signature unknown 返回文件句柄在内核中的索引值,以后做IO多路复用时可以用到 def flush(self, *args, **kwargs): # real signature unknown 把文件从内存buffer里强制刷新到硬盘 def readable(self, *args, **kwargs): # real signature unknown 判断是否可读 def readline(self, *args, **kwargs): # real signature unknown 只读一行,遇到\r or \n为止 def seek(self, *args, **kwargs): # real signature unknown 把操作文件的光标移到指定位置 *注意seek的长度是按字节算的, 字符编码存每个字符所占的字节长度不一样。 如“肖亚飞” 用gbk存是2个字节一个字,用utf-8就是3个字节,因此以gbk打开时,seek(4) 就把光标切换到了“亚”和“飞”两个字中间。 但如果是utf8,seek(4)会导致,拿到了亚这个字的一部分字节,打印的话会报错,因为处理剩下的文本时发现用utf8处理不了了,因为编码对不上了。少了一个字节 def seekable(self, *args, **kwargs): # real signature unknown 判断文件是否可进行seek操作 def tell(self, *args, **kwargs): # real signature unknown 返回当前文件操作光标位置 def truncate(self, *args, **kwargs): # real signature unknown 按指定长度截断文件 *指定长度的话,就从文件开头开始截断指定长度,不指定长度的话,就从当前位置到文件尾部的内容全去掉。 def writable(self, *args, **kwargs): # real signature unknown 判断文件是否可写
flush()把文件从内存buffer里强制刷新到硬盘
T>>> f = open('flush.txt','w',encoding='utf-8') >>> f.write('xiaoyafei') # 因为此时写完后,文件还存放在内存里,并没有写进硬盘里 9 >>> f.flush() # 把文件从内存里强制刷新到硬盘 >>> f.close()
readable()判断是否可读,可以则返回True
f = open('1.txt','r',encoding='utf-8') print(f.readable()) # True f.close()
readline()只读一行,遇到\r or \n为止
# 联系方式.txt 小明-中国-北京 1 老王-日本-东京 1
代码:
f = open('联系方式.txt','r',encoding='utf-8') data = f.readline() print(data) # 一直会打印 小明-中国-北京 1 f.close()
seek()把操作文件的光标移到指定位置
tell()返回光标所在位置
# admin.txt utf-8 admin-admin-admin-1 admin-admin-admin-2 admin-admin-admin-3 admin-admin-admin-4
代码:
f = open('admin.txt','r',encoding='utf-8') print(f.tell()) # 0 print(f.readline()) # admin-admin-admin-1 print(f.tell()) # 21 f.seek(2) # 光标为2 print(f.readline()) # min-admin-admin-1
注意
1.f.read()找的是字符
2.f.tell()和f.seek()找的是字节
seekable() 判断文件是否可以进行seek操作
# 刚刚的admin.txt 文件不修改 f = open('admin.txt','r',encoding='utf-8') print(f.seekable()) # True f.close()
writeable() 判断文件是否能写操作
f = open('admin.txt','r',encoding='utf-8') # mode = 'r' 表示只读 print(f.writable()) # False f.close() f = open('admin.txt','w',encoding='utf-8') # mode = 'w' 表示写 print(f.writable()) # True f.close()
truncate() 按指定长度截断文件,从当前位置往后截断
# f_flush.txt文件内容: 肖亚飞
代码:
f = open('f_flush.txt','r+',encoding='utf-8') res = f.read() print(res) # 肖亚飞 print(f.truncate(6)) # 6 f.flush() # 肖亚 f.close()
占硬盘方式的文件修改代码示例:
# 联系方式.txt文件内容 小明 深圳 173 50 13744234523 康康 广州 172 52 15823423525 玛利 北京 175 49 18623423421 刘诺 北京 170 48 18623423765 老张 广州 172 52 15823423514 罗梦 北京 175 49 18623423311 刘诺 北京 170 48 18623423315
代码:
import os # 导入工具包 f_name = '联系方式.txt' # 旧的文件名 f_new_name = '%s.new' % f_name # 新的文件名 old_str = '阿富汗' # 将要替换的字符串 new_str = '中国' # 替换成... f = open(f_name,mode='r',encoding='utf-8') # 打开文件进行读操作 f_new = open(f_new_name,mode='w',encoding='utf-8') # 打开文件进行写操作 for line in f: # 循环文件 if old_str in line: # 如果old_str在line中 line = line.replace(old_str,new_str) # 替换old_str为new_str f_new.write(line) # 写入文件内容 f.close() f_new.close()
补充1:
我们已经学习了seek,那么现在告诉你,之所以文件会追加到追后面,是因为,文件一打开,要写的时候,光标会默认移动到文件尾部,再开始写,那么,我现在想修改中间部分怎么办?
小明 深圳 173 50 13744234523 康康 广州 172 52 15823423525 玛利 北京 175 49 18623423421 刘诺 北京 170 48 18623423765 老张 广州 172 52 15823423514 罗梦 北京 175 49 18623423311 刘诺 北京 170 48 18623423315
运行完一下代码会是什么样的?:
f = open('联系方式.txt','r+',encoding='utf-8') f.seek(6) f.write('xiaoyafeibaba') f.close()
运行结果:
小明xiaoyafeibaba 173 50 13744234523 康康 广州 172 52 15823423525 玛利 北京 175 49 18623423421 刘诺 北京 170 48 18623423765 老张 广州 172 52 15823423514 罗梦 北京 175 49 18623423311 刘诺 北京 170 48 18623423315
f.write()确实是从第6个字节开始改的,但是把后面文件的内容覆盖了,这是怎么回事?
这是硬盘的存储原理导致的,当你把文件存到硬盘上,就在硬盘上划了一块空间,存数据,等你下次打开这个文件 ,seek到一个位置,每改一个字,就是把原来的覆盖掉,如果要插入,是不可能的,因为后面的数据在硬盘上不会整体向后移。所以就出现 当前这个情况 ,你想插入,却变成了会把旧内容覆盖掉。
但为什么word和vim可以修改文件呢?
我并没说就不能修改了,你想修改当然可以,就是不要在硬盘上修改,把内容全部读到内存里,数据在内存里可以随便增删改查,修改之后,把内容再全部写回硬盘,把原来的数据全部覆盖掉。vim word等各种文本编辑器都是这么干的。
说的好像有道理,但你又没看过word软件的源码,你凭什么这么笃定?
哈哈,我不需要看源码,硬盘 的存储原理决定了word必须这么干 ,不信的话,还有个简单的办法来确认我说的,就是用word or vim读一个编辑一个大文件 ,至少几百MB的,你 会发现,加载过程会花个数十秒,这段时间干嘛了? cpu 去玩了?去上厕所啦? 当然不是,是在努力把数据 从硬盘上读到内存里。
但是文件如果特别大,比如5个GB,读到内存,就一下子吃掉了5GB内存,好费资源呀,有没有更好的办法呢?
如果不想占内存,只能用另外一种办法啦,就是边读边改, 什么意思? 不是不能改么?是不能改原文件 ,但你可以打开旧文件 的同时,生成一个新文件呀,边从旧的里面一行行的读,边往新的一行行写,遇到需要修改就改了再写到新文件 ,这样,在内存里一直只存一行内容。就不占内存了。 但这样也有一个缺点,就是虽然不占内存 ,但是占硬盘,每次修改,都要生成一份新文件,虽然改完后,可以把旧的覆盖掉,但在改的过程中,还是有2份数据 的。