疯狂Python讲义学习笔记(含习题)之 文件 I/O

一、使用pathlib模块操作目录

pathlib模块提供了一组面向对象的类,这些类可代表各种操作系统上的路径,程序可通过这些类操作路径。

该模块包含了以下类:

● PurePath:代表并不访问实际文件系统的“纯路径”。PurePath只负责对路径字符串执行操作,并不关心字符串是否是真实路径。该类有两个子类(PurePosixPath)和(PureWindowsPath),分别为UNIX风格和Windows风格。

UNIX风格的路径和Windows风格的路径主要区别在于根路径和路径分隔符:UNIX风格的路径的根路径是斜杠(/),而windows风格的路径的根路径是盘符(c:);UNIX风格的路径分隔符是斜杠(/),而windows风格的路径的分隔符是反斜杠(\)。

● Path:代表访问实际文件系统的“真正路径”。Path对象可用于判断对应的文件是否存在、是否为文件、是否为目录等。Path同样有两个子类,PosixPath和WindowsPath。

(一)PurePath的基本功能

使用PurePath创建PurePath对象会自动识别操作系统,在UNIX或Mac OS X系统上会返回PurePosixPath对象,在Windows系统上则返回PureWindowsPath对象。也可以使用PurePosixPath或PureWindowsPath类明确指定要创建的对象是什么类型的。

创建PurePath和Path时可以传入单个路径字符串,也可以传入多个路径字符串,PurePath会自动将他们拼接成一个字符串。

from pathlib import *
​
# 创建PurePath,实际上使用PureWindowsPath
pp = PurePath('setup.py')
print(type(pp))  # 
pp = PurePath('ib-top', 'some/path', 'info')
# 看到输出Wiondows风格的路径
print(pp)  # 'ib-top\some\path\info'
pp = PurePath(Path('ib-top'), Path('info'))
# 看到输出Windows风格的路径
print(pp)  # 'ib-top\info'
# 明确指定创建PurePosixPath
pp = PurePosixPath('ib-top', 'some/path', 'info')
# 看到输出UNIX风格的路径
print(pp)  # 'ib-top/some/path/info'

如果创建PurePath时不传入任何参数,则会默认为创建当前路径的PurePath,也就是相当于传入了一个点号(.代表当前路径)作为参数。

如果在创建PurePath时传入了多个路径参数,则最后一个根路径及后面的子路径生效:

pp = PurePosixPath('/etc', '/usr', 'lib64')
print(pp)    # /usr/lib64
pp = PureWindowsPath('c:/Windows', 'd:/info')
print(pp)    # d:/info
※在windows风格的路径中,只有盘符才算根路径,仅有斜杠不算。



pp = PureWindowsPath('c:/windows', '/Program Files')
print(pp)    # c:\Program Files

创建PurePath时传入路径包含多余的斜杠和点号会被系统忽略,除了两个点号(..),因为两个点号代表上一级路径。

pp = PurePath('foo//bar')
print(pp)    # foo\bar
pp = PurePath('foo/./bar')
print(pp)    # foo\bar
pp = PurePath('foo/../bar')
print(pp)    # foo\..\bar,相当于找和foo同一级的bar路径

PurePath对象支持比较运算符

from pathlib import *
​
# 比较两个UNIX风格的路径,区分大小写
print(PurePosixPath('info') == PurePosixPath('INFO'))  # False
# 比较两个Windows风格的路径,不区分大小写
print(PureWindowsPath('info') == PureWindowsPath('INFO'))  # True
# windows风格路径不区分大小写
print(PureWindowsPath('INFO') in {PureWindowsPath('info')})  # True
# UNIX风格的路径区分大小写,所以'D:'小于'c:'
print(PurePosixPath('D:') < PurePosixPath('c:'))  # True
# windows风格路径不区分大小写,所以'd:'(D:)大于'c:'
print(PureWindowsPath('D:') < PureWindowsPath('c:'))  # False
# 不同风格的路径可以判断是否相等(总不相等)
print(PureWindowsPath('ib-top') == PurePosixPath('ib-top'))  # False
# 不同风格的路径不能判断大小,否则会引发错误
# print(PureWindowsPath('info') < PurePosixPath('info'))  # TypeError
# PurePath对象支持斜杠(/)运算符,将多个路径链接起来
pp = PureWindowsPath('abc')
# 将多个路径连接起来(Windows风格的路径)
print(pp / 'xyz' / 'wawa')  # abc\xyz\wawa
pp = PurePosixPath('abc')
# 将多个路径连接起来(UNIX风格路径)
print(pp / 'xyz' / 'wawa')  # abc/xyz/wawa
pp2 = PurePosixPath('haha', 'hehe')
# 将pp、pp2两个路径连接起来
print(pp / pp2)  # abc/haha/hehe
# PurePath的本质其实就是字符串,可以使用str()将他们恢复成字符串对象
pp = PureWindowsPath('abc', 'xyz', 'wawa')
print(str(pp))  # abc\xyz\wawa
pp = PurePosixPath('abc', 'xyz', 'wawa')
print(str(pp))  # abc/xyz/wawa

(二)PurePath的属性和方法

属性/方法 描述
PurePath.parts 返回路径字符串中所包含的各部分
PurePath.drive 返回路径字符串中的驱动器盘符
PurePath.root 返回路径字符串中的根路径
PurePath.anchor 返回路径字符串中的盘符和根路径
PurePath.parents 返回当前路径的全部父路径
PurePath.parent 返回当前路径的上一级路径,相当于parents[0]的返回值
PurePath.name 返回当前路径中的文件名
PurePath.suffixes 返回当前路径中的文件所有后缀名
PurePath.suffix 返回当前路径中的文件后缀名,相当于suffixes属性返回的列表的最后一个元素
PurePath.stem 返回当前路径中的主文件名
PurePath.as_posix() 将当前路径转换为UNIX风格的路径
PurePath.as_uri() 将当前路径转换成URI。只有绝对路径才能转换,否则会引发ValueError
PurePath.is_absolute() 判断当前路径是否为绝对路径
PurePath.joinpath(*other) 将多个路径连接在一起,类似于斜杠运算符
PurePath.match(pattern) 判断当前路径是否匹配指定通配符
PurePath.relative_to(*other) 获取当前路径中去除基准路径之后的结果
PurePath.with_name(name) 将当前路径中的文件名替换成新文件名。如果当前路径中没有文件名,则引发ValueError
PurePath.with_suffix(suffix) 将当前路径中的文件后缀名替换成新的后缀名。如果当前路径中没有后缀名,则添加
from pathlib import *
​
# 访问drive属性
print(PureWindowsPath('c:/Program Files/').drive)  # c:
print(PureWindowsPath('/Program Files/').drive)  # ''
print(PurePosixPath('/etc').drive)  # ''
​
# 访问root属性
print(PureWindowsPath('c:/Program Files/').root)  # \
print(PureWindowsPath('c:Program Files/').root)  # ''
print(PurePosixPath('/etc').root)  # /
​
# 访问anchor属性
print(PureWindowsPath('c:/Program Files/').anchor)  # c:\
print(PureWindowsPath('c:Program Files/').anchor)  # c:
print(PurePosixPath('/etc').anchor)  # /
​
# 访问parents属性
pp = PurePath('abc/xyz/wawa/haha')
print(pp.parents[0])  # abc\xyz\wawa
print(pp.parents[1])  # abc\xyz
print(pp.parents[2])  # abc
print(pp.parents[3])  # .
# 访问parent属性
print(pp.parent)  # abc\xyz\wawa
​
# 访问name属性
print(pp.name)  # haha
pp = PurePath('abc/wawa/bb.txt')
print(pp.name)  # bb.txt
​
pp = PurePath('abc/wawa/bb.txt.tar.zip')
# 访问suffixes属性
print(pp.suffixes[0])  # .txt
print(pp.suffixes[1])  # .tar
print(pp.suffixes[2])  # .zip
# 访问suffix属性
print(pp.suffix)  # .zip
print(pp.stem)  # bb.txt.tar
​
pp = PurePath('abc', 'xyz', 'wawa', 'haha')
print(pp)  # abc\xyz\wawa\haha
# 转换成UNIX风格路径
print(pp.as_posix())  # abc/xyz/wawa/haha
# 将相对路径转换成URI引发异常
# print(pp.as_uri())    # ValueError
# 创建绝对路径
pp = PurePath('d:/', 'Python', 'Python3.6')
# 将绝对路径转换成URI
print(pp.as_uri())  # file:///d:/Python/Python3.6
​
# 判断当前路径是否匹配指定通配符
print(PurePath('a/b.py').match('*.py'))  # True
print(PurePath('/a/b/c.py').match('b/*.py'))  # True
print(PurePath('/a/b/c.py').match('a/*.py'))  # False
​
pp = PurePosixPath('c:/abc/xyz/wawa')
# 测试relative_to方法
print(pp.relative_to('c:/'))  # abc\xyz\wawa
print(pp.relative_to('c:/abc'))  # xyz\wawa
print(pp.relative_to('c:/abc/xyz'))  # wawa
​
# 测试with_name方法
p = PureWindowsPath('e:/Donloads/pathlib.tar.gz')
print(p.with_name('ib-top.py'))  # e:\Downloads\ib-top.py
p = PureWindowsPath('c:/')
# print(p.with_name('ib-top.py'))    # ValueError
​
# 测试with_suffix方法
p = PureWindowsPath('e:/Downloads/pathlib.tar.gz')
print(p.with_suffix('.zip'))  # e:\Downloads\pathlib.tar.zip
p = PureWindowsPath('README')
print(p.with_suffix('.txt'))  # README.TXT

(三)Path的功能和用法

Path与PurePath的用法类似,不同的是PurePath的本质还是字符串,而Path会真正访问底层文件系统,包括判断Path对应的路径是否存在,获取Path对应路径的各种属性(如是否只读、是文件还是文件夹等),并且可以对文件进行读写。

同样的Path也有两个子类,分别代表UNIX风格和Windows风格路径,PosixPath和WindowsPath。

iterdir()方法返回Path对应目录下的所有子目录和文件。glob()方法,用于获取Path对应目录及子目录下匹配指定模式的所有文件。

from pathlib import *
​
# 获取当前目录
p = Path('.')
# 遍历当前目录下的所有文件和子目录
for x in p.iterdir():
    print(x)
# 获取上一级目录
p = Path('../')
# 获取上级目录及其所有子目录下的.py文件
for x in p.glob('**/*.py'):
    print(x)
# 获取g:/publish/codes对应的目录
p = Path('g:/publish/codes')
# 获取上级目录及其所有子目录下的.py文件
for x in p.glob('**/Path_test1.py'):
    print(x)

除了基本的目录操作,Path还提供了read_bytes()和read_text(encoding=None, errors=None)方法,分别用于读取该Path对应的字节数据(二进制数据)和文本数据;也提供了write_bytes(data)和write_text(data, encoding=None, errors=None)方法来输出字节数据(二进制数据)和文本数据。

from pathlib import *
​
p = Path('a_test.txt')
# 指定以GBK字符集输出文本内容
result = p.write_text('''
有一个美丽的新世界
它在远方等我
那里有天真的孩子
还有姑娘的酒窝''', encoding='GBK')
# 返回输出的字符数
print(result)
​
# 指定以GBK字符集读取文本内容
content = p.read_text(encoding='GBK')
# 输出所读取的文本内容
print(content)
​
# 读取字节内容
bb = p.read_bytes()
print(bb)
​

二、使用os.path操作目录

在os.path模块下提供了一些操作目录的方法,这些函数可以操作系统的目录本身。该模块提供了exists()函数判断该目录是否存在;也提供了getctime()、getmtime()、getatime()函数来获取该目录的创建时间、最后一次修改时间、最后一次访问时间;还提供了getsize()函数来获取指定文件的大小。

import os
import time
​
# 获取绝对路径
print(os.path.abspath("a_test.txt"))  # 当前路径下的a_test.txt
# 获取共同前缀名
print(os.path.commonprefix(['/user/lib', '/usr/local/lib']))  # /usr/l
# 获取共同路径
print(os.path.commonpath(['/usr/lib', '/usr/local/lib']))  # \usr
# 获取目录
print(os.path.dirname('abc/xyz/README.txt'))  # abc/xyz
# 判断指定目录是否存在
print(os.path.exists('abc/xyz/README.txt'))  # False
# 获取最近一次访问时间
print(time.ctime(os.path.getatime('os_path_test.py')))
# 获取最后一次修改时间
print(time.ctime(os.path.getmtime('os_path_test.py')))
# 获取创建时间
print(time.ctime(os.path.getctime('os_path_test.py')))
# 获取文件大小
print(os.path.getsize('os_path_test.py'))
# 判断是否为文件
print(os.path.isfile('os_path_test.py'))  # True
# 判断是否为目录
print(os.path.isdir('os_path_test.py'))  # False
# 判断是否为同一个文件
print(os.path.samefile('os_path_test.py', './os_path_test.py'))  # True

三、使用fnmatch处理文件名匹配

fnmatch模块可以支持类似于UNIX shell风格的文件名匹配。

fnmatch支持如下通配符:

通配符 描述
* 可匹配任意个任意字符
? 可匹配一个任意字符
[字符序列] 可匹配括号里字符序列中的任意字符。该字符序列也支持中划线表示法。如[a-c]可代表a、b和c字符中任意一个。
[!字符序列] 可匹配不在括号中字符序列中的任意字符。

fnmatch.fnmatch(filename, pattern):判断指定文件名是否匹配指定pattern。

fnmatch.fnmatchcase(filename, pattern):该函数与fnmatch.fnmatch的功能大致相同,只是该函数区分大小写。

fnmatch.filter(names, pattern):该函数对names列表进行过滤,返回names列表中匹配pattern的文件名组成的子集合。

fnmatch.translate(pattern):该函数用于将一个UNIX shell风格的pattern转换为正则表达式pattern。

from pathlib import *
import fnmatch
​
# 遍历当前目录下的所有文件和子目录
for file in Path('.').iterdir():
    # 访问所有以_test.py结尾的文件
    if fnmatch.fnmatch(file, '*_test.py'):
        print(file)
​
names = ['a.py', 'b.py', 'c.py', 'd.py']
# 对names列表进行过滤
sub = fnmatch.filter(names, '[ac].py')
print(sub)  #['a.py', 'c.py']
​
print(fnmatch.translate('?.py'))  # (?s:.\.py)\Z
print(fnmatch.translate('[ac].py'))  # (?s:[ac]\.py\Z
print(fnmatch.translate('[a-c].py'))  # (?s:[a-c]\.py\Z

四、打开文件

Python提供了一个内置的open()函数,该函数用于打开指定文件。该函数语法格式如下:

open(file_name, [, access_mode][, buffering])

open函数只有第一个参数是必须的,代表要打开文件的路径。

文件对象支持如下属性:

● file.closed:返回文件是否已经关闭。

● file.mode:返回被打开文件的访问模式。

● file.name:返回文件的名称。

# 以默认方式打开文件
f = open('open_test.py')
# 所访问文件的编码方式
print(f.encoding)  # cp936
# 所访问文件的访问模式
print(f.mode)  # r
# 所访问文件是否已经关闭
print(f.closed)  # False
# 所访问文件对象打开的文件名
print(f.name)  # open_test.py

(一)文件打开模式

模式 意义
r 只读模式
w 写模式
a 追加模式
+ 读写模式,可与其他模式结合使用。如r+代表读写模式,w+也代表读写模式
b 二进制模式,可与其他模式结合使用。如rb代表二进制制度模式,rb+代表二进制读写模式,ab代表二进制追加模式

虽然r+和w+都是读写模式,区别是r+要求被打开文件是已存在文件,而w+没有这个限制。

疯狂Python讲义学习笔记(含习题)之 文件 I/O_第1张图片

 

(二)缓冲

如果不使用缓冲,就必须等外设输入或输出一个字节后,内存中的程序才能输出或输入一个字节。使用缓冲后,当程序执行输出时,会先将数据输出到缓冲区中,而不用等待外设同步输出,输入亦然。

open()函数的第三个参数为0(或False)时,打开的文件不带缓冲,如果是1(或者True),就带缓冲区,如果是大于1的整数,则该整数用于指定缓冲区大小,如果是任何负数,代表使用默认缓冲区大小。

五、读取文件

(一)按字节或字符读取

read()方法按字节或字符读取文件内容,如果使用了b模式,则每次读取一个字节,如果没有使用b模式,则每次读取一个字符。

f = open("test.txt", 'r', True, encoding='utf-8')
while True:
    # 每次读取一个字节
    ch = f.read(1)
    # 如果没有读取到数据,则跳出循环
    if not ch:
        break
    # 输出ch
    print(ch, end='')
f.close()

如果调用read()方法时不传入参数,该方法默认会读取全部文件内容。

Windows平台,open()函数总是使用GBK字符集,如果要读取的文件所使用的字符集和当前操作系统的字符集不匹配,有三种解决方式:

● 使用二进制模式读取,然后使用bytes的decode()方法恢复成字符串

● 利用codecs模块的open函数来打开文件,该函数在打开文件时允许指定字符集

● 为open()函数指定关键字参数encoding

(二)按行读取

● readline([n]):读取一行美容,如果指定了参数n,则只读取次行内的n个字符。

● readlines():读取文件内所有行

(三)使用fileinput读取多个输入流

fileinput模块可以把多个输入流合并在一起。

● fileinput.input(files=None, inplace=False, backup='', bufsize=0, mode='r', openhook=None),该函数返回一个FileInput对象。

参数解释:

files:文件的路径列表,默认是stdin方式,多文件可以这样传递:['1.txt', '2.txt',....]

inplace:是否将标准输出的结果写回文件,默认不取代

backup:备份文件的扩展名,只指定扩展名,如.bak。如果该文件的备份文件已存在,则会自动覆盖。

bufsize:缓冲区大小,默认为0,如果文件很大,可以修改此参数,一般默认即可

mode:读写模式,默认为只读

openhook:该钩子用于控制打开的所有文件,比如编码方式等

全局函数 描述
fileinput.filename() 返回正在读取的文件的文件名
fileinput.fileno() 返回当前文件的文件描述符(file descriptor),该文件描述符是一个整数
fileinput.lineno() 返回当前读取的行号
fileinput.filelineno() 返回当前读取的行在其文件中的行号
fileinput.isfirstline() 返回当前读取的行在其文件中是否为第一行
fileinput.isstdin() 返回最后一行是否从sys.stdin读取。程序可以使用“-”代表从sys.stdin读取
fileinput.nextfile() 关闭当前文件,开始读取下一个文件
fileinput.close() 关闭FileInput对象。

(四)文件迭代器

文件对象本身就是可遍历的,所以可以使用for循环来遍历文件内容。

sys.stdin也是一个类文件对象(类似于文件的对象,Python的很多I/O流都是类文件对象)

(五)管道输入

管道的作用:将前一个命令的输出,当成下一个命令的输入。

语法:

cmd1 | cmd2 | cmd3 ...

作用:cmd1命令的输出,将会传给cmd2命令作为输入,cmd2命令的输出又传递给cmd3命令作为输入。

(六)使用with语句

Python提供了with语句来管理资源关闭。with语句的语法格式如下:

with context_expression [as atrget(s)]:
    with 代码块

使用with语句管理的资源必须是一个实现上下文管理协议(context manage protocol)的类,这个类的对象可被称为上下文管理器。要实现上下文管理协议,必须实现如下两个方法:

● context_manager.__enter__():进入上下文管理器自动调用的方法。该方法会在with代码块之前执行。如果with语句有as子句,那么该方法的返回值会被赋值给as子句后的变量:该方法可以返回多个值,因此,在as子句后也可以指定多个变量(多个变量必须由“()”括起来组成元组)。

● context_manage.__exit__(exc_type, exc_value, exc_traceback):退出上下文管理器自动调用的方法。该方法会在with代码块执行之后执行。如果with代码块成功执行结束,程序自动调用该方法,调用该方法的三个参数都为None:如果with代码块因为异常而中止,程序也自动调用该方法,使用sys.exc_info得到异常信息将作为调用该方法的参数。

class FkResource:
​
    def __init__(self, tag):
        self.tag = tag
        print('构造方法,初始化资源:%s' % tag)
​
    # 定义__enter__方法,在with代码块执行之前执行的方法
    def __enter__(self):
        print('[__enter__ %s]:' % self.tag)
        # 将返回值作为as子句后的变量的值
        return 'fkit'  # 可以返回任意类型的值
​
    # 定义__exit__方法,在with代码块执行之后执行的方法
    def __exit__(self, exc_type, exc_val, exc_traceback):
        print('[__exit__ %s]:' % self.tag)
        # exc_traceback为None,代表没有异常
        if exc_traceback is None:
            print('没有异常时关闭资源')
        else:
            print('遇到异常时关闭资源')
            return False  # 可以省略,默认返回None,也被看作是False
​
​
with FkResource('孙悟空') as dr:
    print(dr)
    print('[with代码块] 没有异常')
print('-------------------------------')
with FkResource('白骨精'):
    print('[with代码块] 异常之前的代码')
    raise Exception
    print('[with代码块] ~~~~~~~~异常之后的代码')
​

(七)使用linecache随机读取指定行

linecache模块允许从Python源文件中随机读取指定行,并在内部使用缓存优化存储。

常用函数:

● linecache.getline(filename, lineno, module_globals=None):读取指定模块中指定文件的指定行。filename文件名,lineno指定行号。

● linecache.clearcache():清空缓存

● linecache.checkcache(filename=None):检查缓存是否有效。如果没有指定filename参数,默认检查所有缓存的数据。

六、写文件

r+、w、w+、a、a+模式都可以写入,区别在于r+、w、w+模式打开文件时,文件指针位于文件开头处,a、a+模式打开文件时,文件指针位于文件结尾处。

(一)文件指针的概念

文件指针用于标明文件读写的位置。

疯狂Python讲义学习笔记(含习题)之 文件 I/O_第2张图片

 

● seek(offset[, whence]):该方法把文件指针移动到指定位置。当whence为0时(默认值)表明从文件开头开始计算,如将offset设置为3,就是将文件指针移动到第3处,当whence为1时,表明从指针当前位置开始计算,当whence为2时,表明从文件结尾开始计算。

● tell():判断文件指针的位置。

f = open('filept_test.py', 'rb')
# 判断文件指针的位置
print(f.tell())  # 0
# 将文件指针移动到3处
f.seek(3)
print(f.tell())  # 3
# 读取一个字节,文件指针自动后移1个数据
print(f.read(1))  # 0
print(f.tell())  # 4
# 将文件指针移动到第5处
f.seek(5)
print(f.tell())  # 5
# 将文件指针向后移动5个数据
f.seek(5, 1)
print(f.tell())  # 10
# 将文件指针移动到倒数第10处
f.seek(-10, 2)
print(f.tell())
print(f.read(1))  # d
​

(二)输出内容

文件对象提供的写文件方法主要由两个:

● write(str或bytes):输出字符串或字节串。只有以二进制模式(b模式)打开的文件才能写入字节串。

● writelines(可迭代对象):输出多个字符串多个字节串。

七、os模块的文件和目录函数

(一)与目录相关的函数

函数 描述
os.getcwd() 获取当前目录
os.chdir(path) 改变当前目录
os.fchdir(fd) 通过文件描述符改变当前目录。该函数与上一个函数的功能类似,只是该函数以文件描述符作为参数来代表目录
os.chroot(path) 改变当前进程的根目录
os.listdir(path) 返回path对应目录下的所有文件和子目录
os.mkdir(path[, mode]) 创建path对应的目录,其中mode用于指定该目录的权限。该mode参数代表一个UNIX风格的权限,如0o777代表所有者可读/可写/可执行
os.makedirs(path[, mode]) 作用类似于mkdir(),它可以递归创建目录。如abc/xyz/wawa目录,如果当前目录下没有abc目录,那么使用mkdir()会报错,而makedirs则会递归创建所有目录
os.rmdir(path) 删除path对应的空目录。如果目录非空,则抛出OSError异常,程序可以先使用os.remove()删除文件
os.removedirs(path) 递归删除目录。
os.rename(src, dst) 重命名文件或目录,将src重命名为dst
os.renames(old, new) 对文件或目录进行递归重命名。

(二)与权限相关的函数

函数 描述
os.access(path, mode) 检查path对应的文件或目录是否具有指定权限。该函数的第二个参数可以是一下四个状态值的一个或多个:
● os.F_OK:判断是否存在
● os.R_OK:判断是否可读
● os.W_OK:判断是否可写
● os.X_OK:判断是否可执行
os.chmod(path, mode) 更改权限。其中mode参数代表要改变的权限,该参数支持一下值的组合:
● stat.S_IXOTH:其他用户有执行权限。● stat._S_IWOTH:其他用户有写权限
● stat.S_TROTH :其他用户有读权限。● stat.S_IRWXO :其他用户有全部权限。
● stat.S_IXGRP:组用户有执行权限。● stat.S_IWGRP :组用户有写权限。
● stat.S_IRGRP :组用户有读权限。● stat.S_IRWXG :组用户有全部权限。
● stat.S_IXUSR:所有者有执行权限。● stat.S_IWUSR : 所有者有写权限。
● stat.S_IRUSR :所有者有读权限。● stat.S_IRWXU :所有者有全部权限。
● stat.S_IREAD: Windows 将该文件设为只读的。● stat.S_IWRITE: Windows 将该文件设为可写的。
os.chown(path, uid, gid) 更改文件的所有者。其中uid代表用户id,gid代表组id。该命令在UNIX文件系统下有效
os.fchmod(fd, mode) 改变一个文件的访问权限,该文件由文件描述符fd指定。
os.fchown(fd, uid, gid) 改变文件的所有者,该文件由文件描述符fd指定。

(三)与文件访问相关的函数

函数 描述
os.open(file, flags[, mode]) 打开一个文件,并且设置打开选项,mode参数可选。该函数返回文件描述符。其中flags代表打开文件的旗标,支持如下一个或多个选项:
● os.O_RDONLY :以只读的方式打开。
● os.O_WRONLY :以只写的方式打开。
● os.O_RDWR:以读写的方式打开。
● os.O_NONBLOCK :打开时不阻塞。
● os.O_APPEND :以追加的方式打开。
● os.O_CREAT :创建井打开一个新文件。
● os.O_TRUNC :打开一个文件并截断它的长度为0 (必须有写权限〉。
● os.O_EXCL :在创建文件时,如果指定的文件存在,则返回错误。
● os.O_SHLOCK:自动获取共享锁。
● os.O_EXLOCK :在|动获取独立锁。
● os.O_DIRECT :消除或减少缓存效果。
● os.O_FSYNC :同步写入。
● os.O_NOFOLLOW :不追踪软链接。
os.read(fd, n) 从文件描述符fd中读取最多n个字节,返回读到的字节串。如果文件描述符fd对应的文件已达到结尾,则返回一个空字符串
os.write(fd, str) 将字节串写入文件描述符fd中,返回实际写入的字节串长度
os.close(fd) 关闭文件描述符fd
os.lseek(fd, pos, how) 该函数同样用于一定文件指针。其中how参数指定从哪里开始移动,how=0或者SEEK_SET,从文件开头开始移动;how=1或SEEK_CUR,从当前位置开始移动;how=2或SEEK_END,从文件结束处开始移动
os.fdopen(fd[, mode[,bufsize]]) 通过文件描述符fd打开文件,并返回对应的文件对象
os.closerange(fd_low, fd_high) 关闭从fd_low(包含)到fd_high(不包含)范围的所有文件描述符
os.dup(fd) 复制文件描述符
os.dup2(fd, fd2) 将一个文件描述符fd复制到另一个文件描述符fd2中
os.ftruncate(fd, length) 将fd对应的文件截断到length长度,因此此处传入的length参数不应该超过文件大小
os.remove(path) 删除path对应的文件,如果path是一个文件夹,则抛出OSError错误。
os.link(src, dst) 创建从src到dst的硬链接。硬链接是UNIX系统的概念,Windows中表示复制目标文件
os.symlink(src, dst) 创建从src到dst的符号链接,对应与windows的快捷方式

八、使用tempfile模块生成临时文件和临时目录

常用函数:

● tempfile.TemporaryFile(mode='w+b', buffering=None, encoding=None, newline=None, suffix=None, prefix=None, dir=None):创建临时文件。该函数返回一个类文件对象,也就是支持文件I/O

● tempfile.NamedTemporaryFile(mode='w+b', buffering=None, encoding=None, newline=None, suffix=None, prefix=None, dir=None, delete=True):创建临时文件,生成临时文件在文件系统中有文件名。

● tempfile.SpooledTemporaryFile(max_size=0, mode='w+b', buffering=None, encoding=None, newline=None, suffix=None, prefix=None, dir=None):创建临时文件。当程序向该临时文件输出数据时,会先输出到内存中,直到超过max_size才会真正输出到物理磁盘中。

● tempfile.TemporaryDirectory(suffix=None, prefix=None, dir=None):生成临时目录。

● tempfile.gettempdir():获取系统的临时目录。

● tempfile.gettempdirb():与gettempdir()相同,只是该函数返回字节串。

● tempfile.gettempprefix():返回用于生成临时文件的前缀名。

● tempfile.gettempprefixb():与gettempprefix()相同,只是该函数返回字节串。

 

本章练习:

1. 有两个磁盘文件text1.txt和text2.txt ,各存放一行英文字母,要求把这两个文件中的信息合并(按字母顺序排列),然后输出到一个新文件text3.txt 中。

# 打开并读取第一个文件,将文件内容保存到变量text1
with open('text1.txt', 'r') as f1:
    text1 = f1.read()
# 打开并读取第二个文件,将文件内容保存到变量text2
with open('text2.txt', 'r') as f2:
    text2 = f2.read()
# 合并两个文件的内容,存入变量text3
text3 = (list(text1) + list(text2))
# 对文件内容进行排序
text3.sort()
# 创建文件text3.txt,将排序后的内容写入文件
with open('text3.txt', 'w+') as f3:
    f3.write("".join(text3))
2. 提示用户不断地输入多行内容,程序自动将该内容保存到my.txt 文件中,直到用户输入exit为止。



from pathlib import Path
import os
​
# 使用while循环,提示用户输入,直到用户输入exit
while True:
    user_input = input('请输入一行内容,结束请输入exit')
    if user_input.lower() == 'exit':
        break
    else:
        # 判断my.txt文件是否存在
        my_file = Path('my.txt')
        if my_file.exists():
            # 如果文件存在则以追加方式打开文件
            with open(my_file, 'a+') as f:
                # 写入用户输入数据
                f.write(user_input + os.linesep)
        else:
            # 如果文件不存在则以w+方式打开文件
            with open(my_file, 'w+') as f:
                # 写入用户输入数据
                f.write(user_input + os.linesep)

3. 实现一个程序,该程序提示用户输入一个文件路径,程序读取这个可能包含手机号码的文本文件(该文件内容可能很大),要求程序能识别出该文本文件中所有的手机号码,并将这些手机号码保存到phone.txt 文件中。

from pathlib import Path
import re, sys, codecs, os
​
# 提示用户输入一个文件路径
u_input = input("请输入文件路径,使用绝对路径:")
# 使用用户输入路径创建Path对象
u_path = Path(u_input)
# 判断路径是否有效,是否为文件
if not u_path.exists() or not u_path.is_file():
    # 提示用户输入错误,并退出
    print('您输入的不是有效的文件路径')
    sys.exit(0)
​
​
# 创建函数根据传入的文件对象,一次读取一行,对行内容进行匹配,将匹配到的
# 手机号码写入文件phone.txt
def read_file(f, wf):
    while True:
        line = f.readline()
        # 如果没有读取到数据则退出循环
        if not line:
            break
        # 手机号码匹配模式字符串
        phone_pattern = '1[358][0-9]\d{8}|14[579]\d{8}|16[6]\d{8}|17[0135678]\d{8}|19[89]\d{8}'
        # 对读取到的行数据进行模式匹配
        phone_list = re.findall(phone_pattern, line)
        # 如果匹配到数据则开始写入,如果没有匹配到则什么都不做
        if phone_list:
            # 遍历匹配到的手机号列表,将手机号码写入phone.txt
            for x in phone_list:
                wf.write(x + os.linesep)
    # 关闭文件流
    f.close()
​
​
with open('phone.txt', 'w+') as wf:
    # 尝试使用gbk或utf-8两种字符集读取文件
    try:
        f = codecs.open(u_path, 'r', 'gbk', buffering=True)
        # 调用函数读取并写入文件
        read_file(f, wf)
    except:
        try:
            f = codecs.open(u_path, 'r', 'utf-8', buffering=True)
            read_file(f, wf)
        except:
            print('该文件不是有效的文本文件')
            sys.exit(0)

4. 实现一个程序, 该程序提示用户输入一个目录, 程序递归读取该目录及其子目录下所有能识别的文本文件,要求程序能识别出所有文件中的所有手机号码,并将这些手机号码保存到phones.txt 文件中。

import codecs
import os
import re
import sys
from pathlib import Path
​
# 提示用户输入一个目录
u_input = input("请输入一个目录:").strip()
# 根据用户输入创建Path对象
u_dir = Path(u_input)
# 判断用户输入的是否是一个目录
if not u_dir.exists() or not u_dir.is_dir():
    print('您输入的不是一个有效目录')
    sys.exit(0)
'''
从文件对象f,一次读入一行,对读入的文件进行匹配
如果是手机号码,则写入phones.txt
'''
​
​
def read_write(f, wf):
    while True:
        line = f.readline()
        # 如果没有读到数据,跳出循环
        if not line:
            break
        # 匹配读到的内容中的手机号码,如果没有匹配到则开始下一次循环
        pattern = '1[358][0-9]\d{8}|14[579]\d{8}|16[6]\d{8}|17[0135678]\d{8}|19[89]\d{8}'
        phone_list = re.findall(pattern, line)
        if phone_list:
            # 循环列表,写入wf文件对象
            for x in phone_list:
                wf.write(x + os.linesep)
    f.close()
​
​
with open('phones.txt', 'w+') as wf:
    # 遍历用户输入目录下的所有.txt文件
    for file in u_dir.glob("*.txt"):
        try:
            # 使用GBK编码打开文件
            f = codecs.open(file, 'r', 'gbk', buffering=True)
            read_write(f, wf)
        except:
            try:
                # 使用utf-8编码打开文件
                f = codecs.open(file, 'r', 'utf-8', buffering=True)
                read_write(f, wf)
            except:
                print('打开文件 %s 失败' % file)
                sys.exit(0)
​

5. 实现一个程序, 该程序提示用户运行该程序时输入一个路径。该程序会将该路径下(及其子目录下)的所有文件列出来。

from pathlib import Path
​
'''
判断传入的file对象是否是一个目录,如果是一个目录,则递归列出文件
'''
def list_file(file):
    for f in file.iterdir():
        if Path(f).is_dir():
            list_file(f)
        else:
            print(f)
​
​
# 提示用户输入一个路径
u_input = input('请输入一个路径:').strip()
# 根据用户输入创建Path对象
u_dir = Path(u_input)
# 判断路径是否有效,是否是一个目录
if not u_dir.exists() or not u_dir.is_dir():
    raise ValueError('输入的路径不存在或者不是一个目录')
list_file(u_dir)
​

6. 实现一个程序, 当用户运行该程序时, 提示用户输入一个路径。该程序会将该路径下的文件、文件夹的数量统计出来。

from pathlib import Path
​
# 提示用户输入一个路径
u_input = input('请输入一个路径:').strip()
# 根据用户输入创建Path对象
u_dir = Path(u_input)
# 判断用户输入路径是否是有效路径
if not u_dir.exists():
    raise ValueError('您输入的路径不存在')
​
​
# 创建函数遍历用户输入的目录,计算目录和文件数目
def count_dir(path):
    dir_count = file_count = 0
    for f in path.iterdir():
        if Path(f).is_dir():
            dir_count += 1
        else:
            file_count += 1
    return dir_count, file_count
​
​
dir_count, file_count = count_dir(u_dir)
print('%s 目录下有%d个文件' % (u_dir, file_count))
print('%s 目录下有%d个文件夹' % (u_dir, dir_count))
7. 编写仿Windows 记事本的小程序。

①使用tkinter实现简单记事本



from tkinter import *
from tkinter.filedialog import *
from tkinter.messagebox import *
import os
​
# 文件名变量
filename = ""
​
​
def author():
    '''
    显示作者信息
    '''
    showinfo(title="作者", message="ib-top.com")
​
​
def about():
    '''
    显示版权信息
    '''
    showinfo(title="版权信息", message="2019 @copyright www.ib-top.com")
​
​
def my_new():
    '''
    新建文本文件
    '''
    global top, filename, textpad
    top.title("未命名文件")
    filename = None
    textpad.delete(1.0, END)
​
​
def my_open():
    '''
    打开文件
    '''
    global filename
    filename = askopenfilename(defaultextension=".txt")
    if filename == "":
        filename = None
    else:
        top.title("记事本" + os.path.basename(filename))
        textpad.delete(1.0, END)
        f = open(filename, 'r')
        textpad.insert(1.0, f.read())
        f.close()
​
​
def my_save():
    '''
    保存文件
    :return:
    '''
    global filename
    try:
        f = open(filename, 'w')
        msg = textpad.get(1.0, END)
        f.write(msg)
        f.close()
    except:
        my_saveas()
​
​
def my_saveas():
    '''
    另存文件
    '''
    global filename
    f = asksaveasfilename(initialfile="未命名.txt", defaultextension=".txt")
    filename = f
    fh = open(f, 'w')
    msg = textpad.get(1.0, END)
    fh.write(msg)
    fh.close()
    top.title("记事本" + os.path.basename(f))
​
​
def my_cut():
    '''
    剪切
    '''
    global textpad
    textpad.event_generate("<>")
​
​
def my_copy():
    '''
    复制
    '''
    global textpad
    textpad.event_generate("<>")
​
​
def my_paste():
    '''
    粘贴
    '''
    global textpad
    textpad.event_generate("<>")
​
​
def my_undo():
    '''
    撤销
    '''
    global textpad
    textpad.event_generate("<>")
​
​
def my_redo():
    '''
    重做
    '''
    global textpad
    textpad.event_generate("<>")
​
​
def my_select_all():
    '''
    全选
    '''
    global textpad
    textpad.tag_add("Sel", "1.0", "end")
​
​
def my_find():
    '''
    查找
    '''
    t = Toplevel(top)
    t.title("查找")
    t.geometry("260x60+200+2500")
    t.transient(top)
    Label(t, text="查找:").grid(row=0, column=0, sticky="e")
    v = StringVar()
    e = Entry(t, width=20, textvariable=v)
    e.grid(row=0, column=1, padx=2, pady=2, sticky="we")
    e.focus_set()
    c = IntVar()
    Checkbutton(t, text="不区分大小写", variable=c).grid(row=1, column=1, sticky='e')
    Button(
        t, text="查找所有",
        command=lambda: my_search(v.get(), c.get(), textpad, t, e)).grid(
            row=0, column=2, sticky="e" + "w", padx=2, pady=2)
​
    def close_search():
        '''
        关闭查找窗口
        :return:
        '''
        textpad.tag_remove("match", "1.0", END)
        t.destroy()
​
    t.protocol("WM_DELETE_WINDOW", close_search)
​
​
def my_popup(event):
    '''
    弹出菜单
    :param event:
    :return:
    '''
    editmenu.tk_popup(event.x_root, event.y_root)
​
​
def my_search(needle, cssnstv, textpad, t, e):
    textpad.tag_remove("match", "1.0", END)
    count = 0
    if needle:
        pos = "1.0"
        while True:
            pos = textpad.search(needle, pos, nocase=cssnstv, stopindex=END)
            if not pos:
                break
            lastpos = pos + str(len(needle))
            textpad.tag_add("match", pos, lastpos)
            count += 1
            pos = lastpos
        textpad.tag_config('match', fg='yellow', bg='green')
        e.focus_set()
        t.title(str(count) + "个被匹配")
​
​
top = Tk()
top.title("记事本")
top.geometry("1000x600+100+50")
​
menubar = Menu(top)
​
# 文件功能
filemenu = Menu(top)
filemenu.add_command(label="新建", accelerator="Ctrl+N", command=my_new)
filemenu.add_command(label="打开", accelerator="Ctrl+O", command=my_open)
filemenu.add_command(label="保存", accelerator="Ctrl+S", command=my_save)
filemenu.add_command(label="另存为", accelerator="Ctrl+Shift+S", command=my_saveas)
# 将文件菜单加入菜单栏
menubar.add_cascade(label="文件", menu=filemenu)
​
# 编辑功能
editmenu = Menu(top)
editmenu.add_command(label="撤销", accelerator="Ctrl+Z", command=my_undo)
editmenu.add_command(label="重做", accelerator="Ctry+Y", command=my_redo)
editmenu.add_separator()
editmenu.add_command(label="剪切", accelerator="Ctrl+X", command=my_cut)
editmenu.add_command(label="复制", accelerator="Ctrl+C", command=my_copy)
editmenu.add_command(label="粘贴", accelerator="Ctrl+V", command=my_paste)
editmenu.add_separator()
editmenu.add_command(label="查找", accelerator="Ctrl+F", command=my_find)
editmenu.add_command(label="全选", accelerator="Ctrl+A", command=my_select_all)
# 将编辑菜单加入菜单栏
menubar.add_cascade(label="编辑", menu=editmenu)
​
# 关于功能
aboutmenu = Menu(top)
aboutmenu.add_command(label="作者", command=author)
aboutmenu.add_command(label="版权", command=about)
# 将关于菜单加入菜单栏
menubar.add_cascade(label="关于", menu=aboutmenu)
​
top['menu'] = menubar
​
shortcutbar = Frame(top, height=25, bg="light sea green")
shortcutbar.pack(expand=NO, fill=X)
Inlabe = Label(top, width=2, bg="antique white")
Inlabe.pack(side=LEFT, anchor='nw', fill=Y)
​
textpad = Text(top, undo=True)
textpad.pack(expand=YES, fill=BOTH)
scroll = Scrollbar(textpad)
textpad.config(yscrollcommand=scroll.set)
scroll.config(command=textpad.yview)
scroll.pack(side=RIGHT, fill=Y)
​
# 热键绑定
textpad.bind("", my_new)
textpad.bind("", my_new)
textpad.bind("", my_open)
textpad.bind("", my_open)
textpad.bind("", my_save)
textpad.bind("", my_save)
textpad.bind("", my_select_all)
textpad.bind("", my_select_all)
textpad.bind("", my_find)
textpad.bind("", my_find)
# textpad.bind("", my_cut)
# textpad.bind("", my_cut)
# textpad.bind("", my_undo)
# textpad.bind("", my_undo)
# textpad.bind("", my_cut)
# textpad.bind("", my_cut)
# textpad.bind("", my_redo)
# textpad.bind("", my_redo)
# textpad.bind("", my_copy)
# textpad.bind("", my_copy)
​
textpad.bind("", my_popup)
top.mainloop()

②使用wxPython实现简单记事本

# 引入wx包
import wx
import os
​
​
# 创建1个框架(frame),框架中包含1个可编辑文本框(text box)。
# 文本框用wx.TexCtrl来创建,默认情况下,文本框只能编辑1行文字,即不会自动换行。
# 将文本框放入panel中方便管理
# 利用wx.TE_MULTILINE参数来允许多行编辑
class MainWindow(wx.Frame):
    """
    重写wx.Frame的__init__方法。
    """
​
    def __init__(self):
        # 文件对象
        self.file = ""
        self.content = []
        self.count = 0
        self.width = 700
        self.height = 500
        wx.Frame.__init__(self, None, -1, u"记事本", size=(400, 300))
        self.panel = wx.Panel(self, -1)
        self.text = wx.TextCtrl(
            self.panel,
            -1,
            pos=(2, 2),
            size=(self.width - 10, self.height - 50),
            style=wx.TE_MULTILINE | wx.HSCROLL)
        # 状态栏设置
        self.statusbar = self.CreateStatusBar()  # 窗口底部状态栏
        self.statusbar.SetFieldsCount(3)  # 将状态栏分隔为3个区域
        self.statusbar.SetStatusWidths([-1, -2, -3])  # 将状态栏分隔比例为1:2:3
        # 状态栏区域0文本信息
        self.statusbar.SetStatusText("Area0!!!", 0)
        # 状态栏区域1文本信息
        self.statusbar.SetStatusText("Area1!!!", 1)
        # 状态栏区域2文本信息
        self.statusbar.SetStatusText("Area2!!!", 2)
​
        # 设置文件菜单栏
        menubar = wx.MenuBar()
        filemenu = wx.Menu()
        menubar.Append(filemenu, u'文件')
        # 为文件菜单添加子菜单
        # wx.ID_NEW等是wxWidgets提供的标准ID,如果有现成的标准ID,
        # 最好使用标准ID,而不要自定义,这样可以让wxWidgets知道如何显示这个组件
        menuOpen = filemenu.Append(wx.ID_OPEN, u"打开", u"打开文本文件")
        menuSave = filemenu.Append(wx.ID_SAVE, u"保存", u"保存文件")
        menuSaveas = filemenu.Append(wx.ID_SAVEAS, u"另存为", u"另存文件")
        menuExit = filemenu.Append(wx.ID_EXIT, u"退出", u"退出应用程序")
​
        # 设置编辑菜单
        EditMenu = wx.Menu()
        menubar.Append(EditMenu, u"编辑")
        # 为编辑菜单添加子菜单
        menuUndo = EditMenu.Append(wx.ID_UNDO, u"撤销", u"撤销编辑")
        menuClear = EditMenu.Append(wx.ID_CLEAR, u"清空", u"清空编辑器")
        menuCut = EditMenu.Append(wx.ID_CUT, u"剪切", u"剪切选中文本")
        menuCopy = EditMenu.Append(wx.ID_COPY, u"复制", u"复制选中文本")
        menuPaste = EditMenu.Append(wx.ID_PASTE, u"粘贴", u"粘贴剪贴板文本")
        menuSelectAll = EditMenu.Append(wx.ID_SELECTALL, u"全选", u"选中全部文本")
​
        # 创建快捷键
        menu = wx.Menu()
        ctrla = menu.Append(-1, "\tCtrl-A")
        ctrlc = menu.Append(-1, "\tCtrl-C")
        ctrlx = menu.Append(-1, "\tCtrl-X")
        ctrlv = menu.Append(-1, "\tCtrl-V")
        ctrls = menu.Append(-1, "\tCtrl-S")
        # 添加快捷键
        menubar.Append(menu, u'快捷键')
        self.SetMenuBar(menubar)
​
        # 绑定快捷键事件
        self.Bind(wx.EVT_MENU, self.OnSelect, ctrla)
        self.Bind(wx.EVT_MENU, self.OnCopy, ctrlc)
        self.Bind(wx.EVT_MENU, self.OnCut, ctrlx)
        self.Bind(wx.EVT_MENU, self.OnPaste, ctrlv)
        self.Bind(wx.EVT_MENU, self.OnTSave, ctrls)
​
        # 设置开始菜单events
        self.Bind(wx.EVT_MENU, self.OnOpen, menuOpen)
        self.Bind(wx.EVT_MENU, self.OnSave, menuSave)
        self.Bind(wx.EVT_MENU, self.OnSaveAll, menuSaveas)
        self.Bind(wx.EVT_MENU, self.OnExit, menuExit)
​
        # 设置编辑菜单events
        self.Bind(wx.EVT_MENU, self.OnBack, menuUndo)
        self.Bind(wx.EVT_MENU, self.OnClear, menuClear)
        self.Bind(wx.EVT_MENU, self.OnCut, menuCut)
        self.Bind(wx.EVT_MENU, self.OnCopy, menuCopy)
        self.Bind(wx.EVT_MENU, self.OnPaste, menuPaste)
        self.Bind(wx.EVT_MENU, self.OnSelect, menuSelectAll)
        # 设置窗口大小重置events
        self.Bind(wx.EVT_SIZE, self.OnResize)
​
​
        # 设置应用程序窗口居中显示
        self.Center()
​
    # 打开文件
    def OnOpen(self, event):
        filterFile = "All files(*.*)|*.*"
        # 创建文件打开对话框
        opendialog = wx.FileDialog(self, u"选择文件", os.getcwd(), "", filterFile,
                                   wx.FD_OPEN)
        if opendialog.ShowModal() == wx.ID_OK:
            self.file = opendialog.GetPath()
            f = open(self.file)
            self.text.write(f.read())
            f.close()
        opendialog.Destroy()
​
    def OnTOpen(self, event):
        filterFile = "All files(*.*)|*.*"
        # 创建文件打开对话框
        opendialog = wx.FileDialog(self, u"选择文件", os.getcwd(), "", filterFile,
                                   wx.FD_OPEN)
        if opendialog.ShowModal() == wx.ID_OK:
            self.file = opendialog.GetPath()
            f = open(self.file)
            self.text.write(f.read())
            f.close()
            self.content.append(self.text.GetValue())
        opendialog.Destroy()
​
    # 保存文件
    def OnSave(self, event):
        filterFile = "All files(*.*)|*.*"
        # 创建保存文件对话框
        opendialog = wx.FileDialog(self, u"保存文件", os.getcwd(), "", filterFile,
                                   wx.FD_SAVE)
        if opendialog.ShowModal() == wx.ID_OK:
            self.file = opendialog.GetPath()
            self.text.SaveFile(self.file)
​
    # 保存文件
    def OnTSave(self, event):
        if self.file == "":
            filterFile = "All files(*.*)|*.*"
            opendialog = wx.FileDialog(self, u"保存文件", os.getcwd(), "",
                                       filterFile, wx.FD_SAVE)
            if opendialog.ShowModal() == wx.ID_OK:
                self.file = opendialog.GetPath()
                self.text.SaveFile(self.file)
                self.content.append(self.text.GetValue())
                self.count = self.count + 1
        else:
            self.text.SaveFile(self.file)
            self.content.append(self.text.GetValue())
            self.count = self.count + 1
​
    # 另存文件
    def OnSaveAll(self, event):
        pass
​
    # 退出程序
    def OnExit(self, event):
        self.Close()
​
    def OnTExit(self, event):
        self.Close()
​
    # 撤销编辑
    def OnBack(self, event):
        self.text.Undo()
​
    def OnTBack(self, event):
        try:
            self.count = self.count - 1
            self.text.SetValue(self.content[self.count])
        except IndexError:
            self.count = 0
​
    # 重做
    def OnTGo(self, event):
        try:
            self.count = self.count + 1
            self.text.SetValue(self.content[self.count])
        except IndexError:
            self.count = len(self.content) - 1
​
    # 新建
    def OnTNew(self, event):
        self.text.Clear()
​
    def OnClear(self, event):
        self.text.Clear()
​
    def OnTClear(self, event):
        self.text.Clear()
​
    # 剪切
    def OnCut(self, event):
        self.text.Cut()
​
    # 复制
    def OnCopy(self, event):
        self.text.Copy()
​
    # 粘贴
    def OnPaste(self, event):
        self.text.Paste()
​
    # 全选
    def OnSelect(self, event):
        self.text.SelectAll()
​
    # 重置窗口大小
    def OnResize(self, event):
        newsize = self.GetSize()
        # 这里需要注意一点,在重新设置wx.TextCtrl之前,需要先设置panel的尺寸
        self.panel.SetSize(newsize)
        width = newsize.GetWidth() - 10
        height = newsize.GetHeight() - 50
        self.text.SetSize(width, height)
        self.text.Refresh()
​
​
def main():
    app = wx.App(False)
    myFrame = MainWindow()
    myFrame.Show()
    app.MainLoop()
​
​
if __name__ == '__main__':
    main()
​

8. 编写一个命令行工具, 这个命令行工具就像Windows 提供的cmd 命令一样,可以执行各种常见的命令,如dir 、md 、copy 、move 等。

import os
import shutil
import sys
from pathlib import *
​
while True:
    # resolve() 返回一个新的路径,这个新路径就是当前Path对象的绝对路径,如果是软链接则直接被解析
    # absolute()也可以获取绝对路径,但是推荐使用reslove()
    cmd = input('%s>' % Path('.').resolve()).strip()
    # 判断用户输入,如果输入exit则退出
    if cmd == 'exit':
        sys.exit(0)
    # 模拟dir命令
    elif cmd.startswith('dir'):
        # 如果用户输入以dir开头,则分解用户输入,提取出相应参数(默认以空格为分隔符)
        params = cmd.split()
        # 判断用户输入参数的数量
        if len(params) == 1:
            # 只输入了dir,则显示当前目录中的文件
            for filename in os.listdir(r'.'):
                print(filename)
        elif len(params) == 2:
            # 输入了2个参数,第二个参数应为路径
            if Path(params[1]).exists() and Path(params[1]).is_dir():
                # 打印用户输入路径下的文件名
                for filename in os.listdir(params[1]):
                    print(filename)
            else:
                print('您输入的路径不存在,请重新输入')
                break
        else:
            print('dir命令格式不正确,正确格式为: dir [路径]')
    # 模拟cd命令
    elif cmd.startswith('cd'):
        params = cmd.split()
        # 如果用户只输入了一个参数,则显示当前目录路径
        if len(params) == 1:
            # os.path.expanduser(path)  #把path中包含的"~"和"~user"转换成用户目录
            os.chdir(os.path.expanduser('~'))
        elif len(params) == 2 and Path(params[1]).is_dir():
            os.chdir(params[1])
        else:
            print('cd命令格式不正确,正确格式为:cd [路径]')
    # 模拟md命令
    elif cmd.startswith('md'):
        params = cmd.split()
        if len(params) < 2:
            # md命令需要至少1个参数
            print('md命令格式不正确,正确格式为:md <路径1> [路径2] ...')
        else:
            for i in range(1, len(params)):
                os.mkdir(params[i])
    # 模拟copy命令
    elif cmd.startswith('copy'):
        params = cmd.split()
        # copy命令必须包含2个参数,第一个参数为要拷贝的文件,第二个参数为拷贝到哪个路径
        if len(params) != 3 or (not Path(params[1]).is_file()) or (not Path(
                params[2]).is_dir()):
            print('copy命令格式不正确,正确格式为:copy <文件> <路径>')
        else:
            # shutil是对os中文件操作的补充,提供了--移动 复制  打包 压缩 解压等功能
            shutil.copy(params[1], params[2])
    # 模拟move命令
    elif cmd.startswith('move'):
        params = cmd.split()
        # move命令必须包含2个参数,第一个参数为要移动的文件,第二个参数为移动到哪个路径
        if len(params) != 3 or (not Path(params[1]).is_file()) or (not Path(
                params[2]).is_dir()):
            print('move命令格式不正确,正确格式为:move <文件> <路径>')
        else:
            shutil.move(params[1], params[2])
    else:
        print('无效命令')

 

你可能感兴趣的:(Python3学习,疯狂Python讲义,学习笔记,习题答案,文件,I/O)