open()函数详解

要点概论:

1. file 与 mode 参数

2. buffering,encoding,errors,newlines参数

3. stdin,stdout,stderr

 

1. file 与 mode 参数

  file 可以是个字符串,指定读写文件的路径,可以指定相对路径或者绝对路径;

  mode 是使用字符串来指定文件打开的模式。

  先看一个基本款的文件打开模式表格:

字符串 说明
r 读取模式(默认)
w 写入模式,会先清空文件内容
x 只在文件不存在时才创建新文件并打开为写入模式,若文件已存在会引发 FileExistsError
a 追加模式,若文件已存在,写入的内容会附加到文件尾端
b 二进制模式
t 文本模式(默认)
+ 更新模式(读取与写入)

 

 

 

 

 

 

 

 

 

  open() 的 mode 默认是 r ,在只指定 r , w ,x ,a 的情况下就相当于以文本模式打开,也就等同于 rt , wt , xt , at ,

  如果要以二进制模式打开,就要指定 rb , wb ,xb ,ab。

  如果想以更新模式打开,对于文本模式,可以使用 r+ , w+ , a+ ,对于二进制模式,可以使用 r+b , w+b , a+b。

  

  1)read(),write(),close()

    首先来看一个通用的 upper 程序,可使用命令行参数指定源与目标,将源文本文件的内容全部转成大写后写入目标文件:

with open('小写字母表') as low,open('大写字母表','w') as upper:
    content = low.read()
    upper.write(content.upper())
basicio upper

    read() 方法在未指定自变量的情况下会读取文件全部的内容。并返回 str 实例(二进制文件返回 bytes 实例),因此例子中可以使用 upper() 转换为大写。

    write() 方法可将指定的数据写入文件,write() 接受 str 实例并返回写入的字符数(二进制文件接受 bytes 实例并返回写入的字节数)。

    PS:read() 方法在指定整数自变量的情况下会读取指定的字符数(二进制为字节数)

 

    文件打开模式如果与后续进行的操作不符合,会引发 Unsupported Operation 异常,可以使用 readable() 方法测试是否可读取,writable() 方法测试是否可写入:

f = open('测试表')
f.write('是否能写入')       # io.UnsupportedOperation: not writable

print(f.readable())          # True
print(f.writable())          #False
验证文件模式

 

  2)readline(),readlines(),writelines()

    文本模式和二进制模式通用。

    对于文本模式来说,默认读取到 'n','\r 或 '\r\n' 时都可以被判定为一行,而readline() 或 readlines() 读到的每一行换行符都一律换为 '\n' 。

    对于二进制模式来说,行的判断标准默认时遇到 'b\n' 这个 bytes 类型。

    文本模式在写入的情况下,任何 '\n' 都会被置换为 os.linesep 的值(Window 就是 '\r\n')。

    下面看一个'rb'模式打开文件,readlines() 的结果:

f = open('测试表','rb')

print(f.readlines())       #[b'111\r\n', b'222\r\n', b'333\r\n', b'\r\n', b'\r\n', b'666']

f.close()
readlines() 读取

    可以看到,readlines()返回一个 列表(list),列表里每个元素时文件中被判定为一行的内容。

    

    下面是使用 readline() 搭配 while 循环的例子,因为 readline() 在都不到下一行时会返回空字符串,所以这样写:

with open('测试表') as f:
    while True:
        line = f.readline()
        if not line:
            break
        print(line,end='')
while 实现读取按行读取

    

    因为 open() 返回的文件对象都实现了 __iter__() 方法,可以返回一个迭代器,因此可以直接是哟个 for in 来进行迭代,每次迭代相当于执行 readline():

with open('测试表') as f:
    for line in f:
        print(line,end='')
for 循环实现按行读取

    PS:python的文件读取风格时:读取一个文件最好的方式就是不要去 read 。

 

  3)tell(),seek(),flush()

    在进行文件读取时, tell() 方法可以告知文件指针当前阿紫文件中的位置,单位时字节数,文件开头的位移量时 0 , seek() 方法可以指定跳到哪个位移量,如下例所示:

with open('test','rb') as f:

    print(f.tell())    # 0
    
    print(f.read(1))   # b'1'

    print(f.read(1))   # b'2'

    print(f.tell())    # 2

    print(f.seek(1))   # 1

    print(f.read(1))   # b'2'
test 模拟 12345 并存盘

    

    使用 seek() 来实现文件的随机存取,例如将第三个字节改为 b'0' :

with open('test','r+b') as f:
    f.seek(2)
    print(f.write(b'0'))    # 1

    f.flush()
    f.seek(0)

    print(f.read())         # b'12045'
seek() 实现文件随机存取

    主要不要写成 'w+b', 这样会清空文件内容,也不要写成 'a+b' ,这样会将写入的数据放到文件尾端。

    由于文件对象默认会缓冲处理,不一定会马上看到文件中写入了数据,这时可以执行 flush() 方法,将缓冲内容清空并写入文件。

 

  

  4)readto()

    在二进制模式中, read() 方法返回的 bytes 是不可变动的,如果想将读取到的字节数据收集到一个列表(list)中,基本上可以使用 list() ,例如:

info = None
with open('test2','rb') as f:
    info = f.read()

print(list(info))        #[49, 50, 51, 52, 53]


#test2 中文件内容为 12345
test2

    除此之外,在二进制模式中的文件对象拥有一个 readinto() 方法接受 bytearray 实例,可以直接将读取到的数据传入,这样就可以不用中介的变量,例如:

import os.path

b_arr = bytearray(os.path.getsize('test2'))
with open('test2','rb') as f:
    f.readinto(b_arr)

print(b_arr[0])     # 49

print(b_arr[1])     # 50

print(b_arr)        # bytearray(b'12345')
readinto()

    PS:两者通过索引获取的每个元素都是 int 类型,可以通过 bytes([value]) 将整数值转为 bytes ,例如 bytes([49]) 结果会是 b'1'

 

2. buffering,encoding,errors,newlines 参数

  open() 函数一共有8个参数,除了常用的 file 与 mode 参数,这一部分介绍 buffering,encoding,errors 与 newlines 参数(closed,opener参数待补充)。

  

  1)buffering

    这个参数用来设置缓冲策略,默认的缓冲策略会试着自行决定缓冲区大小(通常为 4096 或 8192 字节),或者对互动文本文件( isatty() 为 True 时,例如 Windows的命令提示符)采取缓冲(line buffering)。

    buffering 设置为 0 表示关闭缓冲,设置为大于 0 的整数值表示指定缓冲区的字节大小。

    在前面的一个例子中如果采用 with open('test','r+b',buffering=0) as f ,在 f.write 更新文件之后不必使用 f.flush() ,马上就可以看到文件内容的变化:

with open('test','r+b',buffering=0) as f:
    f.seek(2)
    print(f.write(b'0'))    # 1

    # f.flush()
    f.seek(0)

    print(f.read())         # b'12045'
test

 

 

  2)encoding 与 errors

    encoding 是指定文件读取编码的参数。

    在 Windows 简体中文版中默认编码是 'cp936' 或 'gbk' (在 python 执行环境中,将 MS936 ,CP936,gbk 视为同一套编码)。

    

    errors 参数可指定发生编码错误时该如何处理。

    在不设置的情况下,发生编码错误时会引发 ValueError 的子类异常。

    例如 test3 文件中有 ‘方法已弃用’ 这几个汉字,并采用 utf-8 编码,如下方法读取,将会引发 UnicodeDecodeError 错误:

with open('test3') as f:
    print(f.read()) 

#UnicodeDecodeError: 'gbk' codec can't decode byte 0xa8 in position 14: incomplete multibyte sequence
View Code

    如果设置 errors 为 ‘ignore’ ,那么会忽略错误,继续读取的操作,如下例所示:

with open('test3',errors='ignore') as f:
    print(f.read())

# 鏂规硶宸插純鐢
View Code

     errors 的其他可设置选项请参考 open() 函数的说明

 

  3)newlines

    对于文本模式来说,默认读取到 '\n' ,'\r' 或 '\r\n 都可以被判定为一行,

    而 readline() 或 readlines() 读到的每一行一律保留来源的换行字符,如果设置为其他 '\n' ,'\r' ,'\r\n' ,那么读取后的换行字符就会时指定的字符。

    文本模式在写入的默认情况下,任何 '\n' 都会被置换为 os.linesep 的值。

    如果 newlines 设为 ‘’ 就保留原有的换行字符,如果指定为其他值,就以指定的字符进行置换。

 

3. stdin ,stdout ,stderr

  目前获取用户的输入都是使用 input() 函数,若想显示指定的值,则使用 print() 函数,它们各自会使用预先连接的设备进行输入或输出。

  预先连接的输入,输出设备被称为标准输入(Standard input)与标准输出(Standard output),以个人计算机而言,通常对应到终端的输入与输出。

 

  在 sys 模块中有个 stdin 代表标准输入,stdout 代表标准输出,它们的操作就像 open() 函数打开的文本模式的文件对象。

  下面的例子模仿了 input() 函数的实现:

import sys
def console_input(prompt):
    sys.stdout.write(prompt)      # 使用标准输出
    sys.stdout.flush()            # 缓存清出数据到标准输出
    return sys.stdin.readline()   #使用标准输入读取一行
name = console_input('请输入名称:')
print('hello',name)
stdin_demo

  在上面这个例子中, sys.stdout 的 write() 方法 “写出” 信息,为了能马上看到指定的信息显示,必须使用 flush() 方法清出数据(即将缓冲区中的数据清出到标准输出),

  接着可以使用 sys.stdin.readline() 读入一行输入的信息。

 

  对于标准输入或输出,若想要以二进制模式读取或写入,可以使用 sys.stdin.buffer 或 sys.stdout.buffer ,它们的操作或行为就像以 open() 函数打开的二进制模式的文件对象

 

  实际上可以改变标准输入或输出的来源,例如,将一个以 open() 函数打开的文件对象赋值给 sys.stdin 就可以利用 input() 来读取:

import sys

sys.stdin = open('stdin_demo.py',encoding='utf-8')

print(input())      # import sys
print(input())      # def console_input(prompt):
print(input())      #     sys.stdout.write(prompt)      # 使用标准输出
print(input())      #     sys.stdout.flush()            # 缓存清出数据到标准输出
print(input())      #     return sys.stdin.readline()   #使用标准输入读取一行
print(input())      # name = console_input('请输入名称:')
print(input())      # print('hello',name)
print(input())      # EOFError: EOF when reading a line
读取stdin_demo中的内容

  类似地,将一个打开的文件对象赋值给 sys.stdout 就可以利用 print() 把数据写到文件中。

  不过内建的 print() 函数本身就有一个 file 参数可以满足这样的需求:

with open('data.txt','w') as f:
    print('hello',file=f)
View Code

  上面的程序执行过后,当前工作目录下就会创建一个 data.txt 文件(已有则覆盖),内容就是执行 print() 函数时输出的 'hello' 信息。

 

 

 

 

 

 

  

 

     

    

 

posted @ 2018-02-24 11:51  皇昭仪  阅读(3550)  评论(0编辑  收藏  举报