使用其他分隔符或行终止符打印
问题:
你想使用print() 函数输出数据,但是想改变默认的分隔符或者行尾符。
解决方案:
可以使用在print() 函数中使用sep 和end 关键字参数,以你想要的方式输出。比如:
1 #正常输出 2 print('dmeon', 89 , 8, 23) 3 4 #指定分隔符, 5 print('dmeon', 89 , 8, 23, sep=',') 6 7 #指定结尾符号 8 print('dmeon', 89 , 8, 23, sep=',',end='!!!\n') 9 10 #使用end 参数也可以在输出中禁止换行。比如: 11 for i in range(5): 12 print(i, end=' ') 13 14 #另外一种添加分隔符的方法 15 print() 16 print('分隔符的方法'.center(30, '-')) 17 s1 = ('dmeon', 89 , 8, 23) 18 result = ' '.join((str(s) for s in s1)) 19 print(result) 20 21 #高逼格的方法 22 print("s1的值", s1) 23 print('高逼格的输出方式', *s1,sep=',')
以上代执行输出的结果为:
dmeon 89 8 23 dmeon,89,8,23 dmeon,89,8,23!!! 0 1 2 3 4 ------------分隔符的方法------------ dmeon 89 8 23 s1的值 ('dmeon', 89, 8, 23) 高逼格的输出方式,dmeon,89,8,23
读写字节数据
问题:
你想读写二进制文件,比如图片,声音文件等等
解决方案:
使用模式为rb 或wb 的open() 函数来读取或写入二进制数据。比如:
1 #使用with打开文件,不需要考虑文件close的问题,rb模式是使用读二进制的模式打开 2 with open('/tmp/go.pdf', 'rb') as f: 3 #获取文件中的内容 4 data = f.read() 5 6 #wb是写的二进制模式打开,当文件存在会覆盖文件已有的内容,文件不存在会创建 7 with open('/tmp/test.bin','wb') as f: 8 f.write(b'Hello World') 9 10 #天坑,读取二进制数据的时候,字节字符串和文本字符串的语义差异可能会导致一个潜在的陷阱 11 t = 'Hello World' 12 print("t中0索引第一个元素为:", t[0]) 13 14 #循环t并打印每个元素 15 for i in t: 16 print('循环t中的每个元素:', i) 17 18 #坑的地方来了 19 print('*'*30) 20 b = b'Hello World' 21 print('b中0索引的元素:', b[0]) 22 23 #循环bytes类型b变量中的每个元素 24 for x in b: 25 print('循环b中的每个元素:', x)
以上代执行输出的结果为:
t中0索引第一个元素为: H 循环t中的每个元素: H 循环t中的每个元素: e 循环t中的每个元素: l 循环t中的每个元素: l 循环t中的每个元素: o 循环t中的每个元素: 循环t中的每个元素: W 循环t中的每个元素: o 循环t中的每个元素: r 循环t中的每个元素: l 循环t中的每个元素: d ****************************** b中0索引的元素: 72 循环b中的每个元素: 72 循环b中的每个元素: 101 循环b中的每个元素: 108 循环b中的每个元素: 108 循环b中的每个元素: 111 循环b中的每个元素: 32 循环b中的每个元素: 87 循环b中的每个元素: 111 循环b中的每个元素: 114 循环b中的每个元素: 108 循环b中的每个元素: 100
如果你想从二进制模式的文件中读取或写入文本数据,必须确保要进行解码和编码操作。比如:
1 with open('/tmp/test.bin', 'rb') as f: 2 data = f.read(16) 3 print('data的数据类型:', type(data)) 4 print('data的数据:', data) 5 6 text = data.decode('utf-8') 7 print('data转码以后的数据类型:', type(text)) 8 print('text的数据:', text)
以上代执行输出的结果为:
data的数据类型: <class 'bytes'> data的数据: b'Hello World' data转码以后的数据类型: <class 'str'> text的数据: Hello World
文件不存在才能写入
问题:
你想像一个文件中写入数据,但是前提必须是这个文件在文件系统上不存在。也就是不允许覆盖已存在的文件内容
解决方案:
可以在open() 函数中使用x 模式来代替w 模式的方法来解决这个问题。比如:
1 #如果文件存在,会覆盖内容,如果文件不存在会创建文件并写入内容 2 with open('/tmp/test.bin', 'wt') as f: 3 f.write('Hello\n') 4 5 #如果文件存在,会报异常,否则创建文件并写入内容,如果文件是二进制,使用wb代替 6 try: 7 f = open('/tmp/test.bin', 'xt') 8 except Exception as e: 9 print(e) 10 else: 11 f.write('Hello\n') 12 finally: 13 f.close()
以上代执行输出的结果为:
[Errno 17] File exists: '/tmp/test.bin'
字符串的I/O 操作
问题:
你想使用操作类文件对象的程序来操作文本或二进制字符串
解决方案:
使用io.StringIO() 和io.BytesIO() 类来创建类文件对象操作字符串数据。比如:
1 import io 2 3 s = io.StringIO() 4 print(s.write('Hello World\n')) 5 6 print('This is a test', file=s) 7 8 #获取s中的内容 9 content = s.getvalue() 10 print('s中的内容为:', content) 11 12 #读取5个长度的内容 13 s = io.StringIO('Hello\nWorld\n') 14 print("五个字符的内容:") 15 print(s.read(5)) 16 17 #读取剩余的内容 18 print("剩余的内容:") 19 print(s.read())
以上代执行输出的结果为:
12 s中的内容为: Hello World This is a test 五个字符的内容: Hello 剩余的内容: World
io.StringIO 只能用于文本。如果你要操作二进制数据,要使用io.BytesIO 类来代替。比如:
1 import io 2 s = io.BytesIO() 3 s.write(b'binary data') 4 data = s.getvalue() 5 print("数据类型:", type(data)) 6 print("data的值:", data)
以上代执行输出的结果为:
数据类型: <class 'bytes'> data的值: b'binary data
注意:
需要注意的是, StringIO 和BytesIO 实例并没有正确的整数类型的文件描述符。因此,它们不能在那些需要使用真实的系统级文件如文件,管道或者是套接字的程序中使用
读写压缩文件
问题:
你想读写一个gzip 或bz2 格式的压缩文件
解决方案:
gzip 和bz2 模块可以很容易的处理这些文件。两个模块都为open() 函数提供了另外的实现来解决这个问题。比如,为了以文本形式读取压缩文件,可以这样做:
1 import gzip 2 import bz2 3 import os 4 5 #打开/tmp/bashrc的文件并获取内容 6 with open('/etc/bashrc', 'rb') as f: 7 content = f.read() 8 9 #使用gzip打开文件,xb如果文件存在就报错,不存在创建,把读取的内容写入到文件 10 try: 11 f = gzip.open('/tmp/bashrc.gz', 'xb') 12 except Exception as e: 13 print('文件已存在~') 14 else: 15 f.write(content) 16 finally: 17 f.close() 18 19 #获取文件类型 20 result = os.popen('file /tmp/bashrc.gz').read() 21 print(result) 22 23 #打开gzip文件并读取内容 24 with gzip.open('/tmp/bashrc.gz') as f: 25 content = f.read() 26 print('bashrc.gz中的内容为:') 27 print(content.decode('utf-8')) 28 29 #bz2的操作和上面一样.. 30 # with bz2.open('samefile.bz2', 'rb') as f: 31 # #f.write(content) 32 # #f.read() 33 # pass
以上代执行输出的结果为:
文件已存在~ /tmp/bashrc.gz: gzip compressed data, was "bashrc", last modified: Sat Aug 5 00:57:56 2017, max compression bashrc.gz中的内容为: # System-wide .bashrc file for interactive bash(1) shells. if [ -z "$PS1" ]; then return fi PS1='\h:\W \u\$ ' # Make bash check its window size after a process completes shopt -s checkwinsize [ -r "/etc/bashrc_$TERM_PROGRAM" ] && . "/etc/bashrc_$TERM_PROGRAM" alias cls='clear' alias ll='ls -l'
读取二进制数据到可变缓冲区中
问题:
你想直接读取二进制数据到一个可变缓冲区中,而不需要做任何的中间复制操作。或者你想原地修改数据并将它写回到一个文件中去
解决方案:
为了读取数据到一个可变数组中,使用文件对象的readinto() 方法。比如:
1 import os.path 2 3 def read_into_buffer(filename): 4 buf = bytearray(os.path.getsize(filename)) 5 with open(filename, 'rb') as f: 6 f.readinto(buf) 7 return buf 8 9 #创建文件并写入内容 10 with open('sample.bin', 'wb') as f: 11 f.write(b'Hello World') 12 13 #获取buf的内容 14 buf = read_into_buffer('sample.bin') 15 16 #修改缓存文件中五个长度的值 17 print("未修改文件中的前五个值:", buf[:5]) 18 buf[:5] = b'hello' 19 print('修改bu文件以后的值:',buf) 20 21 #再次打开文件并把修改的数据放入到里面 22 with open('sample.bin', 'wb') as f: 23 f.write(buf)
以上代执行输出的结果为:
未修改文件中的前五个值: bytearray(b'Hello') 修改bu文件以后的值: bytearray(b'hello World')
内存映射的二进制文件
问题:
你想内存映射一个二进制文件到一个可变字节数组中,目的可能是为了随机访问它的内容或者是原地做些修改
解决方案:
使用mmap 模块来内存映射文件。下面是一个工具函数,向你演示了如何打开一个文件并以一种便捷方式内存映射这个文件
1 import os 2 import mmap 3 4 #使用mmap模块内存映射文件 5 def memory_map(filename, access=mmap.ACCESS_WRITE): 6 size = os.path.getsize(filename) 7 fd = os.open(filename, os.O_RDWR) 8 return mmap.mmap(fd, size , access=access) 9 10 11 #以创建并且内容不为空的文件 12 size = 1000000 13 with open('data', 'wb') as f: 14 f.seek(size-1) 15 f.write(b'\x00') 16 17 #memory_map()函数内存映射 18 print('内存映射'.center(30, '*')) 19 m = memory_map('data') 20 print(len(m)) 21 print(m[:10]) 22 #重新赋值 23 m[:11] = b'hello world' 24 m.close() 25 26 #获取内存映射的内容 27 print('获取内存映射的内容:'.center(30, '-')) 28 with open('data', 'rb') as f: 29 print(f.read(11))
以上代执行输出的结果为:
*************内存映射************* 1000000 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' ----------获取内存映射的内容:---------- b'hello world'
为了随机访问文件的内容,使用mmap 将文件映射到内存中是一个高效和优雅的方法。例如,你无需打开一个文件并执行大量的seek() , read() , write() 调用,只需要简单的映射文件并使用切片操作访问数据即可。
文件路径名的操作
问题:
你需要使用路径名来获取文件名,目录名,绝对路径等等
解决方案:
使用os.path 模块中的函数来操作路径名。下面是一个交互式例子来演示一些关键的特性:
1 import os 2 path = '/etc/passwd/passwd.bak' 3 4 #获取文件名 5 basename = os.path.basename(path) 6 print('file name:', basename) 7 8 #获取目录路径 9 dirname = os.path.dirname(path) 10 print("dirname:", dirname) 11 12 #做多个目录拼接在拼接上文件 13 path_join = os.path.join('/tmp/a/b/c', 'd',os.path.basename(path)) 14 print('path join:', path_join) 15 16 #获取当前用户的家目录信息 17 path = '~/Data/data.csv' 18 print("user:", os.path.expanduser(path)) 19 20 21 #获取后缀名 22 split_path = os.path.splitext(path) 23 print("splitext:", split_path)
以上代执行输出的结果为:
file name: passwd.bak dirname: /etc/passwd path join: /tmp/a/b/c/d/passwd.bak user: /Users/demon/Data/data.csv split: ('~/Data/data', '.csv')
测试文件是否存在
问题:
你想测试一个文件或目录是否存在
解决方案:
使用os.path 模块来测试一个文件或目录是否存在。比如:
1 import os 2 3 #判断/etc/passwd是否存在 4 print('/etc/passwd exists:', os.path.exists('/etc/passwd')) 5 6 #判断/tmp/spam是否存在 7 print('/tmp/spam exists:', os.path.exists('/tmp/spam')) 8 9 #判断/etc/passwd是否为一个文件 10 print('/etc/passwd is file:', os.path.isfile('/etc/passwd')) 11 12 #判断/tmp是否为一个目录 13 print('/tmp is directory:', os.path.isdir('/tmp')) 14 15 #判断/usr/local/bin/python3是否为一个链接文件 16 print('/usr/local/bin/python3 is link:', os.path.islink('/usr/local/bin/python3')) 17 18 #获取软连接的绝对路径 19 print('/usr/local/bin/python3 real directory:', os.path.realpath('/usr/local/bin/python3')) 20 21 #获取文件的大小 22 print('file size:', os.path.getsize('/etc/passwd')) 23 24 #文件的修改时间 25 print('file mtime:', os.path.getmtime('/etc/passwd')) 26 27 #文件的改变时间 28 import time 29 print('file ctime:', time.ctime(os.path.getctime('/etc/passwd')))
以上代执行输出的结果为:
/etc/passwd exists: True /tmp/spam exists: False /etc/passwd is file: True /tmp is directory: True /usr/local/bin/python3 is link: True /usr/local/bin/python3 real directory: /Library/Frameworks/Python.framework/Versions/3.6/bin/python3.6 file size: 5925 file mtime: 1456440624.0 file ctime: Sat Aug 6 07:46:53 2016
获取文件夹中的文件列表
问题:
你想获取文件系统中某个目录下的所有文件列表
解决方法:
使用os.listdir() 函数来获取某个目录中的文件列表:
1 import os 2 3 #获取当前目录下的文件 4 # names = os.listdir('somedir') 5 6 #获取所有的文件 7 path = '/tmp' 8 #列表推到式的写法 9 names = [name for name in os.listdir(path) if os.path.isfile(os.path.join(path,name))] 10 print('所有文件名的列表:', names) 11 12 #另外一种通过高阶函数filter 13 filter_names = list(filter(lambda name:os.path.isfile(os.path.join(path,name)),os.listdir(path))) 14 print('filter的所有文件名列表:', filter_names) 15 16 17 #获取所有的目录 18 dirnames = list(filter(lambda name:os.path.isdir(os.path.join(path,name)),os.listdir(path))) 19 print('所有目录的名称 :', dirnames) 20 21 #过滤内容,查找当前目录下的所有以.log结尾或者.gz结尾的文件 22 files = [name for name in names if name.endswith(('log','gz'))] 23 print("查找目录下以log或者gz结尾的文件", files) 24 25 #另外一种写法 26 from fnmatch import fnmatch 27 fnmatch_files = [name for name in names if fnmatch(name, '*.log')] 28 print('另外一种查找以log结尾的文件:', fnmatch_files)
以上代执行输出的结果为:
所有文件名的列表: ['.adobeLockFile', '.keystone_install_lock', 'adobegc.log', 'AlTest1.err', 'AlTest1.out', 'bashrc.gz', 'com.adobe.acrobat.rna.AcroCefBrowserLock', 'escalatelantern.ico', 'swtag.log'] filter的所有文件名列表: ['.adobeLockFile', '.keystone_install_lock', 'adobegc.log', 'AlTest1.err', 'AlTest1.out', 'bashrc.gz', 'com.adobe.acrobat.rna.AcroCefBrowserLock', 'escalatelantern.ico', 'swtag.log'] 所有目录的名称 : ['com.apple.launchd.18kY10uuPh', 'com.apple.launchd.dVLT4hIllt'] 查找目录下以log或者gz结尾的文件 ['adobegc.log', 'bashrc.gz', 'swtag.log'] 另外一种查找以log结尾的文件: ['adobegc.log', 'swtag.log']
打印不合法的文件名
问题:
你的程序获取了一个目录中的文件名列表,但是当它试着去打印文件名的时候程序崩溃,出现了UnicodeEncodeError 异常和一条奇怪的消息—— surrogates not allowed
解决方案:
当打印未知的文件名时,使用下面的方法可以避免这样的错误:
1 import sys 2 3 names = ['spam.py', 'b\udce4d.txt', 'foo.txt'] 4 5 def bad_filename(filename): 6 return repr(filename)[1:-1] 7 8 print('第一个版本的错误处理:'.center(40, '*')) 9 for filename in names: 10 try: 11 print(filename) 12 except UnicodeEncodeError: 13 print(bad_filename(filename)) 14 15 16 #另外一个版本的解决办法 17 def bad_filename(filename): 18 #忽略编码的错误,sys.getfilesystemencoding是获取当前系统的编码 19 temp = filename.encode(sys.getfilesystemencoding(), errors='surrogateescape') 20 return temp.decode('latin-1') 21 22 print('第二个版本的错误处理:'.center(40, '-')) 23 for filename in names: 24 try: 25 print(filename) 26 except UnicodeEncodeError: 27 print(bad_filename(filename))
以上代执行输出的结果为:
**************第一个版本的错误处理:*************** spam.py b\udce4d.txt foo.txt --------------第二个版本的错误处理:--------------- spam.py bäd.txt foo.txt
序列化Python 对象
问题:
你需要将一个Python 对象序列化为一个字节流,以便将它保存到一个文件、存储到数据库或者通过网络传输它
解决方案:
对于序列化最普遍的做法就是使用pickle 模块。为了将一个对象保存到一个文件中,可以这样做:
1 import pickle 2 3 ''' 4 data = 'same python object....' 5 #序列化对象到文件存储中 6 f = open('somefile', 'wb') 7 pickle.dump(data, f) 8 ''' 9 10 #实例 11 #利用pickle分别把列表 字符串 字典类型的数据写入到/tmp/test文件中 12 f = open('/tmp/test', 'wb') 13 pickle.dump([1, 2, 3, 4], f) 14 pickle.dump('Hello World', f) 15 pickle.dump({'name':'demon'}, f) 16 print('写入完毕') 17 f.close() 18 19 #读取文件中的内容 20 f = open('/tmp/test', 'rb') 21 print(pickle.load(f)) 22 print(pickle.load(f)) 23 print(pickle.load(f))
以上代执行输出的结果为:
写入完毕 [1, 2, 3, 4] Hello World {'name': 'demon'}