关于读取文件的一些小细节的总结
python中文件的读取写入比较方便,当时难免在使用的时候回出现错误,接下来就对该内容做一个总结
- 获取系统默认编码
1
2
3import sys print(sys.getdefaultencoding())
输出结果:
1
2
3utf-8 #type : str
在读取文本的时候,会使用系统默认的编码但是呢,我们也可以自己更改编码格式。
1
2open('XXX', encoding='utf-8')
python支持非常多的文本编码,几个常见的编码是ascii,latin-1,utf-8等,latin-1是字节0-255到U+0000到U+00FF范围内Unicode字符串直接映射,使用该编码永远不会得到出现解码错误。使用latin-1编码读取一个文件的时候 也许不能产生完全正确的文本解码数据, 但是它也能从中提取出足够多的有用数据。同 时,如果你之后将数据回写回去,原先的数据还是会保留的。
默认情况下,Python会以统一模式处理换行符。 这种模式下,在读取文本的时候, Python可以识别所有的普通换行符并将其转换为单个 n 字符。 类似的,在输出时会将 换行符 n 转换为系统默认的换行符。 如果你不希望这种默认的处理方式,可以给 open() 函数传入参数 newline=’’ ,就像下面这样:
出现编码错误
可以使用如下这种方式:
1
2
3
4
5f = open('XXX.txt', 'r', encoding='ascill', errors='replace) f.read() ##### g = open('XXX.txt', 'w', encoding='ascill', errors='ignore')
errors参数一般处理编码错误会使用默认的编码通常是(‘utf-8’)
打印输出至文件中
使用print函数的输出重定向到一个文件中去,使用print中的file关键字采纳数
1
2
3with open('', 'w') as fp: print('hello world', file=fp)
这样就可以将文本写入文件了。
print()函数参数
- sep 控制字符间的字符
- end控制字符结尾字符
- join函数,和sep使用方法一样,不过str.join()join的参数只能是字符串
1
2
3#使用该函数需要经过特殊的处理 print(','.join(str(x) for x in row))
字节文本的使用
1
2
3
4b = b'Hello World' for c in b: print(c)
输出结果为:
72
101
108
108
111
32
87
111
114
108
100
如果想要从二进制文本中读取或写入文本,必须确保要进行解码
1
2
3
4
5
6
7with open('somefile.bin', 'rb') as f: data = f.read(16) text = data.decode('utf-8') with open('somefile.bin', 'wb') as f: text = 'Hello World' f.write(text.encode('utf-8'))
文件不存在才能写入
如果文件存在就不写入,如果存在呢,不做操作。
1
2
3
4
5
6
7import os if not os.path.exits('somefile') with open('somefile', 'w') as f: f.write('whllo world') else: print('File')
字符串的I/O操作
你想要使用操作类文件对象的程序来操作文本或二进制字符串
使用 io.StringIO() 和 io.BytesIO() 类来创建类文件对象操作字符串数据。
1
2
3
4
5
6import io s = io.StringIO() s.write('hello world') print('This is a test') s.getvalue()
1
2
3
4
5
6>>> s = io.StringIO('HellonWorldn') >>>> s.read(4) 'Hell' >>>> s.read() >'onWorldn'
如果要操作二进制数据要使用io.BytesIO()类来代替
1
2
3
4
5>>> s = io.BytesIO() >>> s.write(b'binary data') >>> s.getvalue() b'binary data
读取压缩文件
想要读取一个gzip或保证格式的压缩文件
1
2
3
4
5
6
7
8import gzip with gzip.open('somefile.gz', 'rt') as f: text = f.read() # bz2 compression import bz2 with bz2.open('somefile.bz2', 'rt') as f: text = f.read()
当写入压缩数据时,可以使用compresslevel这个可选的关键字参数来指定一个压缩等级。
1
2
3with gzip.open('somefile.gz', 'w', compresslevel=5) as f: f.write(text)
固定大小记录的文件迭代
想要在一个固定的长度上记录或者数据块的集合上迭代,而不是文件中一行一行的迭代。
方法:通过使用iter和functools.partial()函数:
1
2
3
4
5
6
7from functools import partial RECORD_SIZE = 32 with open('somefile.data', 'rb') as f: records = iter(partial(f.read, RECORD_SIZE), b'') for r in records:
这个例子中的records对象是一个可迭代对象,他会不断的产生固定大小的数据块,直到文件末尾
如果记录大小不是块大小的整数倍的话最后一个返回字节数会比期望值少。
- iter() 函数有一个鲜为人知的特性就是,如果你给它传递一个可调用对象和一个标记 值,它会创建一个迭代器。 这个迭代器会一直调用传入的可调用对象直到它返回标记值 为止,这时候迭代终止
- functools.partial 用来创建一个每次被调用时从文件中读取固定数目字节的 可调用对象。 标记值 b’’ 就是当到达文件结尾时的返回值。
读取二进制文件数据到可变缓冲区中
想要直接读取二进制文件数据到一个可变缓冲区,而不需要做任何的中间复制操作或者是想药原地修改数据并将它的值写入文件。
为了读取数据到一个可变数组中,使用文件对象的readinto()方法 , 比如。
1
2
3
4
5
6
7
8import os.path def read_into_buffer(filename):#读取文件到缓冲区 buf = bytearray(os.path.getsize(filename)) with open(filename, 'rb') ad fp: fp.readinto(buf) return buf
文件对象的readinto()方法能被用来预先分配内存的数组填充数据,和普通的read()方法不同的是,readinto()填充已存在的缓冲区
而不是为新对象重新分配内存再返回它们,
1
2
3
4
5
6
7record_size = 32 buf = bytearray(record_size) with open('somefile', 'rb') as fp: n= f.readinto(buf) if n < record_size: break
另一个有趣的特性就是memoryview,他可以同过零赋值的方式对已存在的缓冲区执行切片操作,甚至还能修改它的内容。
1
2
3
4
5
6
7
8
9>>> buf bytearray(b'Hello World') >>> m1 = memoryview(buf) >>> m2 = m1[-5:] >>> m2 <memory at 0x100681390> >>> m2[:] = b'WORLD' >>> buf bytearray(b'Hello WORLD') >>>
使用f.readinto()时需要注意,检查它的返回值,也就是实际读取的字节数,如果字节数小于缓冲区大小,表明数据被截断或则是被破坏掉
内存映射的二进制文件
通过内存映射一个二进制文件到一个可变的字节数组中去,目的是获取它的内存或者是原地的做些修改。
使用mmap模块来内存映射文件:
1
2
3
4
5
6
7
8import os import mmap def memory_map(filename, access=mmap.ACCESS_WRITE): size = os.path.getsize(filename) fd = os.open(filename, os.O_RDWR) return mmap.mmap(fd, size, access=access)
为了使用这个函数,你需要有一个已创建并且内存不为空的文件下面这个例子叫你如何初始化创建一个文件并将其内容扩充到指定大小:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15size =1000000 with open('data' ,'wb') as fp: f.seek(size-1) f.write(b'x00') #创建一个文件并且扩充到指定的大小 m = memory_map('data') print(len(m)) print(m[0:10]) print(m[0]) m[0:11] = b'Hello World' m.close() with open('data', 'rb') as fp: print(fp.read(11))
输出结果为:
1
2
3
4
51000000 b'x00x00x00x00x00x00x00x00x00x00' 0 b'Hello World'
mmap() 返回的 mmap 对象同样也可以作为一个上下文管理器来使用, 这时候底层的文件 会被自动关闭。
memeory_map() 函数打开的文件同时支持读和写操作。 任何的修改内容都会 复制回原来的文件中。 如果需要只读的访问模式,可以给参数 access 赋值为 mmap.ACCESS_READ 。比如:
m = memory_map(filename, mmap.ACCESS_READ)
如果你想在本地修改数据,但是又不想将修改写回到原始文件中,可以使用
mmap.ACCESS_COPY :
为了随机访问文件的内容,使用 mmap 将文件映射到内存中是一个高效和优雅的方法。 例 如,你无需打开一个文件并执行大量的 seek() , read() , write() 调用, 只需要简单 的映射文件并使用切片操作访问数据即可。
注意:
内存映射一个文件并不会导致整个文件被读取到内存中。 也就是 说,文件并没有被复制到内存缓存或数组中。相反,操作系统仅仅为文件内容保留了一段 虚拟内存。 当你访问文件的不同区域时,这些区域的内容才根据需要被读取并映射到内 存区域中。 而那些从没被访问到的部分还是留在磁盘上。所有这些过程是透明的,在幕 后完成!
文件路径名的操作
使用os.path模快中的函数来操作路径名。
1
2
3
4
5
6
7
8
9
10
11
12
13
14import os path = '/Users/Data/data.csv' os.path.basename(path) #data.csv os.path.dirname(path)#获取目录名 #/Users/Data os.path.join('tmp', 'data', os.path.basename(path))#将路径组件连接在一起 #tmp/data/data.csv path = '~/Data/data.csv' os.path.expanduser(path)#展开用户的主目录 #C:Userslenovo/Data/data.csv os.path.splitext(path)#拆分文件扩展名 #('~/Data/data', '.csv')
测试文件是否存在
测试一个文件或者是目录是否存在。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15import os os.path.exists('ppt.py') #True #### ###还可以判断文件类型 #### os.path.isfile('ppt.py')#是常规文件 #True os.path.iddir('ppt.py')#是目录 #False os.path.islink("/usr/local/bin/python3")#是一个符号链接 #True os.path.realpath('/usr/local/bin/python3')#将文件链接到 #'/usr/local/bin/python3.3'
如果你还想获取元数据(比如文件大小或者是修改日期),也可以使用 os.path 模块来解 决:
1
2
3
4
5
6os.path.getsize('ppt.py') #909 import time print(time.ctime(os.path.getmtime('ppt.py'))) #Wed May 22 13:41:01 2019
注意:在写文件时候还需要考虑到文件的权限问题
获取文件夹中的文件列表
目的:想要获取文件系统中某个目录下的所有文件的列表。
1
2
3
4
5import os names = os.listdir('class_work') print(names) #['data', 'data.txt', 'exal.py', 'input_class.py', 'ppt.py', 'tang', 'word.py', '__init__.py', '__pycache__']
结果会返回目录中所有文件列表,包括所有文件,子目录,符号链接等等。 如果你需要 通过某种方式过滤数据,可以考虑结合 os.path 库中的一些函数来使用列表推导。
1
2
3
4
5
6
7import os.path names = [name for name in os.listdir('class_work') if os.path.isfile(os.path.join('somedir', name))]#得到所有的正确格式的文件。 dirnames = [name for name in os.listdir('somedir') if os.path.isdir(os.path.join('somedir', name))]#得到所有的文件目录
字符串的 startswith() 和 endswith() 方法对于过滤一个目录的内容也是很有用的。比 如:
1
2pyfiles = [name for name in os.listdir('somedir') if name.endswith('.py')]
对于文件名的匹配,你可能会考虑glob或fnmatch模块:
1
2
3
4
5
6
7import glob pyfiles = glob.glob('somedir/*.py') from fnmatch import fnmatch pyfiles = [name for name in os.listdir('somedir') if fnmatch(name, '*.py')]
获取目录中的列表是很容易的,但是其返回结果只是目录中实体名列表而已。 如果你还 想获取其他的元信息,比如文件大小,修改时间等等, 你或许还需要使用到 os.path 模 块中的函数或着 os.stat() 函数来收集数据。比如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import os import os.path import glob pyfiles = glob.glob('*.py')#会匹配当前目录中的所有文件名 # Get file sizes and modification dates name_sz_date = [(name, os.path.getsize(name), os.path.getmtime(name)) for name in pyfiles] for name, size, mtime in name_sz_date: print(name, size, mtime) # Alternative: Get file metadata file_metadata = [(name, os.stat(name)) for name in pyfiles] for name, meta in file_metadata: print(name, meta.st_size, meta.st_mtime) #os.stat()返回一个包含文件内容和文件时间。
忽略文件名编码
你想使用原始文件名执行文件的I/O操作,也就是说文件名并没有经过系统默认编码去解 码或编码过。
默认情况下,所有的文件名都会根据 sys.getfilesystemencoding() 返回的文本编码来编码 或解码
1
2
3
4
5
6
7
8
9
10
11
12>>> Directory listing (decoded) >>> import os >>> os.listdir('.') ['jalapeño.txt'] >>> # Directory listing (raw) >>> os.listdir(b'.') # Note: byte string [b'jalapenxccx83o.txt'] >>> # Open file with raw filename >>> with open(b'jalapenxccx83o.txt') as fp: print(f.read()) Spicy!
打印不合法的文件名
你的程序获取了一个目录中的文件名列表,但是当它试着去打印文件名的时候程序崩溃, 出现了 UnicodeEncodeError 异常和一条奇怪的消息—— surrogates not allowed
当打印未知的文件名时,使用下面的方法可以避免这样的错误:
1
2
3
4
5
6
7def bad_filename(filename): return repr(filename)[1:-1] try: print(filename) except UnicodeEncodeError: print(bad_filename(filename))
如果文件名中含有一个特殊的字符时:
1
2
3
4
5>>>import os >>>files = os.listdir('.') >>>files ['spam.py', 'budce4d.txt', 'foo.txt']
而当想要打印上面的文件名列表时,程序就会奔溃。
1
2
3
4
5
6
7>>> for name in files: print(name) spam.py Traceback (most recent call last): File "<stdin>", line 2, in <module> UnicodeEncodeError: 'utf-8' codec can't encode character'udce4' in position 1: surrogates not allowed
程序崩溃的原因就是字符 udce4 是一个非法的Unicode字符。 它其实是一个被称为代理 字符对的双字符组合的后半部分。 由于缺少了前半部分,因此它是个非法的Unicode。 所 以,唯一能成功输出的方法就是当遇到不合法文件名时采取相应的补救措施。
1
2
3
4
5
6
7
8
9>>> for name in files: try: print(name) except UnicodeEncodeError: print(bad_filename(name)) spam.py budce4d.txt foo.txt
在 bad_filename() 函数中怎样处置取决于你自己。 另外一个选择就是通过某种方式重新 编码,示例如下:
1
2
3
4def bad_filename(filename): temp = filename.encode(sys.getfilesystemencoding(),errors='surrogateescape') return temp.decode('latin-1')
surrogateescape: 这种是Python在绝大部分面向OS的API中所使用的错误处理器, 它能以一种优雅的方式处理由操作系统提供的数据的编码问题。 在解码出错时会将出错字节存储到一个很少被使用到的Unicode编码范围内。 在编码时将那些隐藏值又还原回原先解码失败的字节序列。 它不仅对于OS API非常有用,也能很容易的处理其他情况下的编码错误。
最后
以上就是热情小熊猫最近收集整理的关于python文件操作(明细)的全部内容,更多相关python文件操作(明细)内容请搜索靠谱客的其他文章。
发表评论 取消回复