05-03 文件处理part2

一、x模式(了解)

  • x模式:文件不存在则创建,存在则报错。只写模式,不可读
# 1、示例一:提前创建a.txt文件,文件存在则报错
# with open('a.tx', mode='xt', encoding='utf-8') as f:  # 报错:FileExistsError: [Errno 17] File exists: 'a.txt'
#     pass  

# 2、示例二:当前目录下文件b.tx不存在并创建 + 不可读。
with open(r'b.txt', mode='xt', encoding='utf-8') as f:
    # f.read()  # x模式只写模式,不可读。执行了f.read以后就。报错:io.UnsupportedOperation: not readable
    
# 3、示例三:当前目录下c.txt不存在 + 只写
with open(r'c.txt', mode='xt', encoding='utf-8') as f:
    f.write('哈哈哈\n')
    
"""
拓展:换行符的概念由来
    linux换行符:\n
    windows换行符:\r\n   
        \r\n由来:\r表示回到行首 \n表示换行(举例:打印机)
        优化:\r\n符号过多,过多占用空间。直至到了现在python3中使用:\n会在windows中默认指定的就是\r\n,而到了linux中默认指定的说就是\n。
"""

二、b模式

1、演示b模式读写都是以二进制为单位、可以争对所有文件、不能指定字符编码

# 引入:t模式只能读文本文件,不能读取其他文件需要用到b模式。
# with open(r'爱nmlgb的爱情.mp4', mode='rt', encoding='utf-8') as f:
    # f.read()  # 报错:UnicodeDecodeError

# 演示一:b模式下读取图片文件:不能指定encoding  
# with open(r'test.jpg', mode='rb', encoding='utf-8') as f:
#     res = f.read()  
    # 注意:二进制模式一定不能指定encoding,指定会报错(ValueError: binary mode doesn't take an encoding argument)。
    # print(res, type(res))
    
# 演示二:b模式下读取文本文件:打印字符使用decode指定的编码,要与原来存入硬盘的编码格式相同。
with open(r'd.txt', mode='rb') as f:
    date_bytes = f.read()
    # f.read由b模式的加持下,直接将硬盘的二进制读入内容,b模式下,不做任何转换,直接读入内存。(提示:物理层面其实读出来的内容就是二进制,但是python进行处理模式会转成bytes类型,以后我们看到bytes类型,就把它直接当作二进制去看。)

    print(date_bytes)   # b'\xe5\x93\x88\xe5\x93\x88\xe5\x93\x88a'
    print(date_bytes.decode('utf-8'))  # 哈哈哈a
    # 文本模式下,需要解码才能打印显示字符,不然打印出来的像上面一样是硬盘中躺着的是按照utf-8编码的二进制的格式。上面显示的是16进制的格式,每一个斜杠划分了每一个bytes类型。我们看到有三个哈哈哈字符,它们的utf-8对应中文字符采用的是3个字节,因此我们可以看到三个相同的‘xe5\x93\x88’。但是b模式下争对英文,会显示英文字符本身。本身bytes类型就是以硬盘默认存的时候的编码格式并以16进制的方式显示,也是转换的过程,像英文为什么这样,这些都只是给你的显示的结果。
    
# 演示三:b模式下对文本文件执行写操作:使用encode指定一种编码格式往文件中写,读出文件时就需要使用decode指定同样的编码方式读。
with open(r'e.txt', mode='wb') as f:
    f.write('你好hello'.encode("gbk"))  # 以gbk编码格式往e.txt文件中写,读出文件需要使用gbk编码方式读。

    
# 演示四:b模式下对文本文件执行写操作:使用encode将多种编码格式写入文件,将引发文件读取时困难。
with open('f.txt', mode='wb') as f:
     # 冒号前使用utf-8格式写,冒号后使用gbk编码格式写。这样读取文件的时候,就需要判断如果是冒号前,那么就使用utf-8解码。如果冒号后,那么就需要使用gbk解码。
    f.write("嘿嘿嘿你好!".encode('utf-8'))  # 使用utf-8打开看到:嘿嘿嘿你好!:��������ã�
    f.write(":".encode('utf-8'))
    f.write("哈哈哈你好!".encode('gbk'))  # 使用gbk打开看到:鍢垮樋鍢夸綘濂斤紒:哈哈哈你好!

2、区分b模式与t模式在读写单位、争对文件类型、是否指定字符编码

"""
t模式:
    1、读写都是以字符串为单位(字符串在python中就是unicode)
    2、只能争对文本文件
    3、必须指定字符编码。
b模式:
    1、读写都是以二进制为单位
    2、可以争对所有文件
    3、不能指定字符编码,只要制定就报错。
    拓展:b模式下争对英文,会显示英文字符本身。本身bytes类型就是以硬盘默认存的时候的编码格式并以16进制的方式显示,也是转换的过程,像英文为什么这样,这些都只是给你的显示的结果。
"""

3、文件拷贝功能优化:用户不仅可以copy文本文件,还能copy任何的文件。

soruce_file_path = input('源文件路径>>:').strip()
target_file_path = input('目标文件路径>>:').strip()
with open(fr'{soruce_file_path}', mode='rb') as f1, \
    open(fr'{target_file_path}', mode='wb') as f2:
    for line in f1:
        f2.write(line)

4、循环读取文件:当行数据过大是使用while循环读取,for循环将不再适用

"""
前提:了解区分t模式、b模式怎么区分每一行。
	1、t模式下,文本文件以换行符为区分。
	2、b模式下,任意文件也是以换行符为区分。
以下for、while例子再次强调:for循环可以做的事情,while循环也能做到   
"""
# for循环读取:
"""
结束条件:在循环结束时,文件读取结束。
#空行是判断循环时是否有数据的关键条件。
"""
with open(r'g.txt', mode='rt') as f:
    for line in f:
        print(len(line), line)  # 如果当前行时空行,长度为1
with open(r'g.txt', mode='rb') as f:
    for line in f:
        print(len(line), line)  # 如果当前行是空行,b'\n' 长度为1
with open(r'test.jpg', mode='rb') as f:
    for line in f:
        print(line)  # b模式下,line都是bytes类型
        
# while循环读取:
"""
结束条件:
	在使用f.read读取文件内容的长度为0时,文件读取结束。
使用while循环时,for循环没有的优点:
	while循环可以自己控制每次读取的数据量(使用时段:当文件一行内容很很长的情况下,使用while循环)
"""
with open(r'test.jpg', mode='rb') as f:
    while True:
        res = f.read(1024)   # f.read()操作,可以指定每次读取数据的字符个数。这里1024指的是,当前次读取1024个字符。
        
        print(len(res), res)  # 如果文件内容读取完毕以后,继续执行f.read(1024)操作,这个时候读取的内容长度就是0,所以我们使用这个条件来作为文件循环取值结束的条件。
        
        # if not len(res):  # 或者使用下面的写法
        if len(res) == 0:
        if not len(res):
            break

5、补充:得到bytes类型的三种方式

"""
得到bytes类型的三种方式:
	1、字符串编码之后的结果
		'上'.encode("utf-8")
		bytes('上', encoding='utf-8)
	2、b'必须是纯英文字符'
	3、b模式下打开文件,f.read()读出的内容。
"""

三、文件的操作的其他方法

1、f.readline、f.readlines

with open(r'g.txt', mode='rt', encoding='utf-8') as f:
    # 1、f.readline基本使用:一次读一行。注意:以换行符进行区分每一行
    # print(f.readline())  # 111
    # print(f.readline())  # 222
    # print(f.readline())  # 333

    # 2、模仿f.read操作:使用while循环 + f.readline用来。
    while True:
        line = f.readline()
        print([line], len(line))
        if not len(line):
            break
            
# 3、f.readlines详细介绍:把所有内容读出来,以每一行为区分,一个一个的放入列表中。
with open(r'g.txt',mode='rt',encoding='utf-8') as f:
    res = f.readlines()
    print(res)  # ['111\n', '222\n', '333\n', '444\n', '5555\n', '\n', '\n']
    
# 4、f.readlines与f.read使用注意:read和readlines都是将数据一次性读入内存,在数据内容过大时会导致内存溢出,需谨慎使用

2、f.writelines

# 1、f.writelines基本使用:把列表中的多个值取出来,写入文件中。(注意:列表中的每个元素都必须指定字符串类型。 )
# 验证:列表中的每个元素都必须指定字符串类
with open('h.txt',mode='wt',encoding='utf-8') as f:
    # f.writelines(['111', '222', '333', 444])  # 注意:列表中的每个元素都必须指定字符串类型,否则报错(TypeError: write() argument must be str, not int)

# 验证:换行符的使用
with open('j.txt',mode='wt',encoding='utf-8') as f:    
    f.writelines(['111', '222', '333', '444'])  # 这里的内容会全部在一行显示:111222333444
with open('h.txt',mode='wt',encoding='utf-8') as f:    
    f.writelines(['111\n', '222\n', '333\n', '444\n'])  # 其中每个元素都就加上了换行符,111等与其他元素值,都将独立一行

# 2、f.writelines与for循环的关系:它底层相当于调用了for循环,把列表中的每个元素for循环写入文件。)    
with open('k.txt',mode='wt',encoding='utf-8') as f:   
    # f.writelines相当于调用了for循环,把列表中的每个元素for循环写入文件.
    li = ['111\n', '222\n', '333\n', '444\n']
    for item in li:
        f.write(item)
        
# 3、b模式下的写操作
with open('l.txt', mode='wb') as f:
    li = [
        '111aaa\n'.encode('utf-8'),
        '222bbb\n'.encode('utf-8'),
        '333ccc\n'.encode('utf-8'),
    ]
    # 3.1 b模式下使用writelines,列表中的每个字符串类型的元素,都需要进行编码成bytes类型
    f.writelines(li)

with open('m.txt', mode='wb') as f:
    li = [
        b'aaaaaa\n',
        b'bbbbbb\n',
        '333ccc\n'.encode('utf-8'),
    ]
    # 3.2 b模式下使用writelines,当列表中的字符串元素中的值全为字母时,可以不用特地解码。可以直接在要写入的字符串内容前加前缀小b,得到bytes类型
    f.writelines(li)

with open('n.txt', mode='wb') as f:
    li = [
        bytes('111aaa\n', encoding='utf-8'),
        bytes('222bbb\n', encoding='utf-8'),
        bytes('333ccc\n', encoding='utf-8'),
    ]
    # 3.3 '上'.encode('utf-8')等同于bytes('上', encoding='utf-8')
    f.writelines(li)
    
'''
以上三步查看文件中的结果都是:
111aaa
222bbb
333ccc
''' 

3、flush

# flush使用场景:主要争对写操作,告诉操作系统,把数据直接从内存写入硬盘中。通常flush主要用于测试行为。
with open('o.txt', mode='wt', encoding='utf-8') as f:
    f.write('哈哈哈')
    f.flush()  # 立刻将文件内容从内存刷到硬盘

4、其他了解操作:f.readable()、f.writeable()、f.encoding、f.name、f.colsed

with open('p.txt', mode='wt', encoding='utf-8') as f:
    print(f.readable())  # 判断f句柄是否可读,False
    print(f.writable())  # 判断f句柄是否可写,True
    
    print(f.encoding)    # 查看f句柄采用的字符编码,utf-8(如果文件打开模式为b,则没有该属性)
    # print(f.encoding)  # 如果没有指定encoding,默认使用操作系统的编码,在windsow中显示:CP936(CP936其实就是GBK)
    
    print(f.name)   # 查看f句柄的文件名,p.txt
print(f.closed)     # 判断文件是否关闭

四、控制文件指针操作

1、f.seek、f.tell

"""
一、指针移动的单位:
    指针移动的单位都是以字节为单位,每次都是移动移动几个字节。(只有一种情况特殊,t模式下的read(n), 这个n代表的是字符个数,而b模式下read(n),这个n代表的是字节数)
    
二、f.seek(n,模式):n指的是移动的字节数。模式有三种:
    0:参照物是文件开头。举例:f.seek(9,0),文件从开头位置移动8个bytes。
    1:参照物是当前指针所在位置。举例:f.seek(9,1),如果文件是从第2个字节位置开始,执行该操作以后当前文件中指针所在位置在11字节的位置。
    2:参照物是文件末尾,文件指针倒着移动。举例:f.seek(-3,2),如果文件中总字节数为9,那么当前所在的字节数是6.

补充:
	1、seek默认就是0模式
	2、指针使用默认不移动:0、1、2三种模式只要没有进行移动操作t、b模式中都可以使用。
	3、指针移动时:0模式只能用在t模式中使用。0、1、2模式都可以用来b模式中
	4、seek推荐用的模式:seek推荐使用在b模式中,一般基于seek的操作,都是用于b模式下的操作。
	5、注意:a模式下,无论你的文件指针在哪个位置。一旦你使用write操作,写入的内容必然要追加到文件的末尾。
	6、f.seek三种模式的正反向移动:正向移动文件指针不受内容的限制。反向移动文件指针不能超出内容部分,超出报错。


三、f.tell():获取文件指针当前位置。
""" 

'''
创建aaa.txt文件,写入以下内容作为测试需要:本次使用utf-8作为存储单位。一共9个字节。
abc你好
'''
# 验证:t模式下的read(n), 这个n代表的是字符个数
with open('aaa.txt', mode='rt', encoding='utf-8') as f:
    res = f.read(4)
    print(res)  # abc你

    
# 验证:b模式下read(n),这个n代表的是字节数
with open('aaa.txt', 'rb') as f:
    print(f.read(3))  # b'abc'
    print(f.read(3).decode('utf-8'))  # 你
    print(f.read(3).decode('utf-8'))  # 好


# 验证:只有0模式可以在t模式下使用
with open('aaa.txt', mode='rt', encoding='utf-8') as f:
    f.seek(3, 0)
    # f.seek(3, 1)  # 报错(不支持运算):io.UnsupportedOperation: can't do nonzero cur-relative seeks
    # f.seek(3, 2)    # 报错(不支持运算):io.UnsupportedOperation: can't do nonzero end-relative seeks
    
# 示范一:0模式下指针的移动以文件开头为参照
with open('aaa.txt', mode='rt', encoding='utf-8') as f:
    f.seek(3, 0)  # 1、以文件开头为参照:移动3个字节,文件指针在'c'后面'你'前面.
    # print(f.read())  # 你好
    print(f.tell())  # 3

    f.seek(1, 0)  # 2、以文件开头为参照:移动0个字节,文件指针在'a'后面'b'前面.
    print(f.tell())  # 1
    print(f.read())   # bc你好
    print(f.tell())   # 9(这里因为上面的f.read()操作,导致文件指针在末尾)

    f.seek(0, 0)
    f.seek(4, 0)
    # print(f.read().decode('utf-8'))  # 3、注意:如果文件指针移动到的位置不符合解码条件,报错(Unicode解码错误):UnicodeDecodeError: 'utf-8' codec can't decode byte 0xbd in position 0: invalid start byte

# 示范二:1模式下指针的移动以当前位置为参照物(文件指针并不会因为文件内容做停留)
with open('aaa.txt', mode='rb') as f:
    f.seek(9, 1)  # 1、以当前位置为参考:r模式,前面没有任何操作,此时默认在起始位置。执行f.seek操作后,此时文件指针在末尾。
    print(f.tell())  # 9
    print(f.read())  # b''(文件指针在末尾,f.read读不出任何内容)

    f.seek(7, 1)  # 以当前位置为参考:之前文件指针在9字节位置,现在继续向后移动3字节.
    print(f.tell())  # 16(文件已经到末尾了,文件指针并不会因为文件内容做停留)
    print(f.read())  # b''

# 示范三:2模式下指针的移动以末尾为参照物,文件指针移动数值指定负数。(注意: 如果指针往前移动的指定范围超出,报错)
with open('aaa.txt',mode='rb') as f:
    f.seek(-9, 2)    # 以末尾为参照物:文件指针往前移动9个字节。目前在‘a’前面,也就是开头起始位置。
    print(f.tell())  # 0
    print(f.read())  # b'abc\xe4\xbd\xa0\xe5\xa5\xbd'(文件指针在开头,读出了所有数据)

    f.seek(-3, 2)    # 以末尾为参照物:文件指针往前移动3个字节。目前在'你'后面,'好'前面。
    print(f.tell())  # 6
    print(f.read())  # 好
    
    # f.seek(-10, 2)  # 注意: 如果指针往前移动的指定范围超出,报错(操作系统错误):OSError: [Errno 22] Invalid argument

2、什么是往文件末尾追加一行内容?是用编辑器打开文件,还是a操作?

"""
注意:我们双击打开文件,往文件的末尾写一行内容,其实这并不叫往文件末尾追加一行内容。我们双击打开文件,看到文件中的内容,这个时候这个文件是在内存中,我们在内存中的这样的修改,只是把文件内容覆盖回硬盘了,你看起来好像是修改了,其实是把硬盘原来的内容,完整的给覆盖了一遍。也就是说你如果自己用编辑器打开文件,在原有的基础之上,往后面写内容,那其实不叫追加操作。
    
模拟追加操作:
	以a模式打开文件,运行一次,那就是往文件末尾追加内容。
	
linux命令拓展:
	tail -f access.log:捕捉access.log这个文件末尾的内容。
"""
posted @ 2020-03-16 19:25  给你加马桶唱疏通  阅读(146)  评论(0编辑  收藏  举报