09Python学习文件处理

一、概念

文件保存于硬盘,存储的数据都是101010二进制数据。

二、字符编码

2.1 概念

从明文到编码文本的转换称为“编码”,从编码文本又转回成明文则为“解码”。

字符串----->encode-------->utf-8

utf-8-------->decode---------->字符串

2.2 乱码的两种情况:

乱码一:存文件时就已经乱码
存文件时,由于文件内有各个国家的文字,我们单以shiftjis去存,
本质上其他国家的文字由于在shiftjis中没有找到对应关系而导致存储失败
但当我们硬要存的时候,编辑并不会报错(难道你的编码错误,编辑器这个软件就跟着崩溃了吗???),但毫无疑问,不能存而硬存,肯定是乱存了,即存文件阶段就已经发生乱码
而当我们用shiftjis打开文件时,日文可以正常显示,而中文则乱码了

乱码二:存文件时不乱码而读文件时乱码
存文件时用utf-8编码,保证兼容万国,不会乱码,而读文件时选择了错误的解码方式,比如gbk,则在读阶段发生乱码,读阶段发生乱码是可以解决的,选对正确的解码方式就ok了,

2.3 不通编码特性

①ascii 只支持英文字符,且占用一个字节,一个字节为8位。1byte = 8bit

>>> ord('a')
97
>>> bin(ord('a'))
'0b1100001'

②gbk 只支持中文字符,且一个字符占用两个字节,2byte= 16bit

>>> '我'.encode("gbk")
b'\xce\xd2'

>>> int.from_bytes('我'.encode('gbk'),byteorder='big',signed=False)
52946

③unicode 俗称万国码,不管英文还是中文都占3字节。3byte = 24bit

>>> '我'.encode()
b'\xe6\x88\x91'


>>> int.from_bytes('我'.encode(),byteorder='big',signed=False)
15108241

④utf-8 为了解决磁盘空间占用问题,它可以使用1~4个字节表示一个符号,出现了utf-8,中文utf-8仍占三字节,但是英文只用一个字节

>>> '我'.encode("utf-8")
b'\xe6\x88\x91'

>>> int.from_bytes('我'.encode('utf-8'),byteorder='big',signed=False)
15108241

三、文件操作

3.1 操作文件的流程

①. 打开文件,得到文件句柄并赋值给一个变量
②. 通过句柄对文件进行操作
③. 关闭文件

#获取文件句柄,其中参数1是文件的路径,	参数2是读写模式,		参数3是字符编码
>>> f = open('1.txt','w',encoding='utf-8')	
#写字符操作
>>> f.write('hello world')
11
#关闭资源
>>> f.close()


>>> rf = open('1.txt','r',encoding='utf-8')
>>> rf.read()
'hello world'
>>> rf.close()

3.2 注意事项

3.2.1 回收资源

在操作完毕一个文件时,必须把与该文件的这两部分资源回收

回收方法为:f.close() 然后 del f 一般 f.close()后,系统会自动del变量名。

3.2.2 字符编码->仅限文本模式

f=open(…)是由操作系统打开文件,那么如果我们没有为open指定编码,那么打开文件的默认编码很明显是操作系统说了算了,操作系统会用自己的默认编码去打开文件,在windows下是gbk,在linux下是utf-8
若要保证不乱码,文件以什么方式存的,就要以什么方式打开。

3.3 文件操作的各种模式

3.3.1 打开文件的模式有(默认为文本模式):

r ,只读模式【默认模式,文件必须存在,不存在则抛出异常

w,只写模式【不可读;不存在则创建;存在则清空内容

a, 追加写模式【不可读;不存在则创建;存在则只追加内容

x, 只写模式【不可读;不存在则创建,存在则报错

3.3.2 对于非文本文件,我们只能使用b模式,

"b"表示以字节的方式操作(而所有文件也都是以字节的形式存储的,使用这种模式无需考虑文本文件的字符编码、图片文件的jgp格式、视频文件的avi格式)

rb ,只读模式

wb,只写模式

ab,追加写模式

xb,只写模式

注:以b方式打开时,读取到的内容是字节类型,写入时也需要提供字节类型,不能指定编码

3.3.3 "+"形式

“+” 表示可以同时读写某个文件

r+, 读写【可读,可写】

w+,写读【可读,可写】

a+, 写读【可读,可写】

x+ ,写读【可读,可写】

3.4 文件推荐操作

3.4.1 with open操作

使用with open…可一个或多个文件使用逗号隔开 as 变量名称:

with open操作句柄,无需手工关闭资源。系统操作完毕后将自动回收。文件句柄是个可迭代对象,如果文件内容较多时,推荐使用迭代器来操作文本。

>>> with open('1.txt','r') as f:
...     pass
...

>>> with open('1.txt','r') as f1 , open('2.txt','w') as f2:
...     pass
...

3.4.2 eval()函数

可以把字符串里的字符转换为可执行代码,但只支持一行字符

>>> s = "1+1+1"
>>> type(s)
<class 'str'>
>>> a = eval(s)	# 把字符串中表达式转成int类型 值为3
>>> type(a)
<class 'int'>

>>> d = "{'a':1,'b':2}"
>>> d
"{'a':1,'b':2}"
>>> d_e = eval(d)	# 把字符串中表达式转成dict类型
>>> d_e
{'a': 1, 'b': 2}

>>> l = "[1,2,3,4,5]"
>>> l
'[1,2,3,4,5]'
>>> l_e = eval(l)	# 把字符串中表达式转成list类型
>>> l_e
[1, 2, 3, 4, 5]

>>> s_s = "(1,2,3)"
>>> type(eval(s_s))	# 把字符串中表达式转成tuple类型
<class 'tuple'>

>>> s_s = "{1,2,}"
>>> type(eval(s_s))	# 把字符串中表达式转成set类型
<class 'set'>

>>> eval("sfsfasf")	# 把字符串表达式无法执行,报错
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
NameError: name 'sfsfasf' is not defined

3.5 文件的读写操作

3.5.1 文本模式读取

注:Widnows 系统下的换行代表2个字符大小为 \r\t

一次性读取文本内容,缺点:效率低,一次性加载内存。

>>> with open("1.txt",'r',encoding='utf-8') as rf:
...     print("一次性读取所有内容:: ", rf.read())
...
一次性读取所有内容::  hello world1
Hello world2
Hello world3

一次性读取文本内容,并组成列表。效率同样低下

>>> with open("1.txt",'r',encoding='utf-8') as rf:
...     l = rf.readlines()
...     print('l:',l)
...
l: ['hello world1\n', 'Hello world2\n', 'Hello world3']

通过迭代器模式,效率较高

>>> with open("1.txt",'r',encoding='utf-8') as rf:
...     for x in rf:
...             print(x)
...
hello world1

Hello world2

Hello world3

读取不存在的文件

>>> with open('adfasf.txt','r') as f:
...     print(f)
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'adfasf.txt'

写入一个文件

>>> with open('asdf.txt','w') as f:		# 没有源文件,创建源文件,有文件则覆盖源文件
...     f.write("123\n")
...
4

>>> f = open('asdf.txt','w')	# 没有源文件,创建源文件,有文件则覆盖源文件
>>> f.writes(["1","2"])
>>> f.writelines(["1","2"])	# 通过列表写入数据
>>> f.close()

3.5.2 字节模式读写

>>> with open('1.txt','rb+') as f:		# 通过b模式读写文件,无需编码参数
...     print(f.read().decode())			# 读取字节进行解码, 没有指定编码格式,默认使用系统编码
...
hello world1
Hello world2
Hello world3


>>> with open('1.txt','ab') as f:				# 通过b模式追加文件,无需编码参数
...     f.write("您好".encode("utf-8"))	# 写入字节进行编码, 指定utf-8
...
6

3.5.3 seek方法,文件内光标移动

fileObject.seek(offset[, whence])

  • offset: 开始的偏移量,也就是代表需要移动偏移的字节数,如果是负数表示从倒数第几位开始。

  • **whence:**可选,默认值为 0。给 offset 定义一个参数,表示要从哪个位置开始偏移;0 代表从文件开头开始算起,1 代表从当前位置开始算起,2 代表从文件末尾算起。当有换行时,会被换行截断。seek()无返回值,故值为None。

    seek有三种移动方式0,1,2,其中1和2必须在b模式下进行,但无论哪种模式,都是以bytes为单位移动的

    有如下文本

    ➜  python cat test.txt
    123456789
    中国
    
    # 系统默认utf-8编码,所以总共字节数 = 9个数字*1字节+1个换行符*1字节+2个中文字符*3字节 = 16字节
    
    Python 3.7.2 (default, Feb 12 2019, 08:15:36)
    [Clang 10.0.0 (clang-1000.11.45.5)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> f = open("test.txt",'r')
    >>> f.tell()			#获取当前光标位置
    0
    >>> f.seek(4)			#光标从开始初,偏移4字节
    4
    >>> f.read()			#read方法读取光标剩余部分文本
    '56789\n中国\n'
    >>> f.close()
    
    >>> f = open("test.txt",'rb')
    >>> f.seek(-1)													#直接偏移倒数第一字节,报错
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    OSError: [Errno 22] Invalid argument
    >>> f.seek(-12,2)												#直接偏移倒数第12个字节,报错
    5
    >>> f.seek(2,1)													#直接偏移倒数第一字节,报错
    7
    >>> f.seek(-1,2)												#直接偏移倒数第一字节,报错
    16
    >>> f.seek(-10)													#直接从开始位置偏移倒数第10个字节,报错
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    OSError: [Errno 22] Invalid argument
    >>> f.seek(-10,2)
    7
    >>> print(f.read().decode())
    89
    中国
    
    >>> f.close()
    
    

3.5.4 truncate方法,截断文本

truncate(n): 从文件的首行首字符开始截断,截断文件为n个字符;无n表示从当前位置起截断;截断之后n后面的所有字符被删除。其中win下的换行代表2个字符大小。

但是不能用w或w+等方式打开,因为那样直接清空文件了,所以truncate要在r+或a或a+等模式下测试效果

Last login: Thu May  9 19:05:37 on ttys000
➜  python cat test.txt
123456789
中国

>>> f = open("test.txt",'rb+')
>>> f.truncate(5)
5
>>> print(f.read().decode())
12345
>>> f.close()

3.6 例子:查看日志最后一行记录,使用高效方式

优势,日志文件如果很大的话。不会一次性加载到内存中,会根据偏移量慢慢读取

# 日志最后一行记录
➜  python tail -1 api.debug.log
2019-05-09 15:14:04.184 |-DEBUG [http-nio-8081-exec-9] com.xxx.course.config.AuthenticationInterceptor [74] -| url:http://test-apicourse.hello.cn/ method:GET

>>> def read_last_line(x):
...     with open(x,'rb') as f:							#通过b模式读取,拿到句柄
...             init_num = -100							#初始偏移值 100字节
...             while 1:
...                     f.seek(init_num,2)	#光标从文件末尾偏移
...                     l = f.readlines()		#读取光标后面内容,转成列表
...                     if len(l)>=2:				#如果列表元素>=2 说明读取行数超过2行
...                             print("最后一行是: %s" % l[-1].decode())
...                             break
...                     else:								#否则增加偏移量,继续循环
...                             init_num*=2
...
>>> read_last_line("api.debug.log")
最后一行是: 2019-05-09 15:14:04.184 |-DEBUG [http-nio-8081-exec-9] com.xxx.course.config.AuthenticationInterceptor [74] -| url:http://test-apicourse.hello.cn/ method:GET
            

3.7 例子:有如下数据文件,求男女比例各占多少

➜  python cat person_data.txt
{'name':'人员1','性别':'男'}
{'name':'人员2','性别':'男'}
{'name':'人员3','性别':'男'}
{'name':'人员4','性别':'男'}
{'name':'人员5','性别':'男'}
{'name':'人员6','性别':'女'}
{'name':'人员7','性别':'女'}
{'name':'人员8','性别':'女'}
{'name':'人员9','性别':'女'}%
# 通过r来进行特殊字符处理,使得路径合法
res_dic = {'男': 0, '女': 0}  # 初始男女
with open(r'/Users/qinjie/doc/code/note/python/person_data.txt', 'rb') as read_f:
    for i in read_f:  # 直接for循环操作句柄,不会一次性加载到内存中
        dic_line = eval(i.decode())  # 使用b模式需要解码操作,并转成字典
        if dic_line['性别'] == '男':
            res_dic['男'] += 1
        else:
            res_dic['女'] += 1

print('男性人数:%s' % res_dic['男'])
print('男性占比:{:.2%}'.format(res_dic['男']/sum(res_dic.values())))
print('女性人数:%s' % res_dic['女'])
print('女性占比:{:.2%}'.format(res_dic['女']/sum(res_dic.values())))

# 打印
# 男性人数:5
# 男性占比:55.56%
# 女性人数:4
# 女性占比:44.44%
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值