文件
变量是把数据保存到内存中,如果程序重启/主机重启,内存中的数据就会丢失,要想能让数据被持久化储存,就可以把数据储存到硬盘中,也就是在文件中保存。
在ws中的目录名之间使用 \ 来分割 但是使用 / 来分给也是可以的,但是我们一般用 / ,因为 \ 在编译语言的在字符串中有特定的含义,用来表示转义字符。如果我们想用反斜杠,那么就要用 \\ , \\ 在字符串里面才表示字符 \ 的含义。
变量就是在内存中,文件就是在外存中,我们要用python实现对文件操作。
文件操作
打开文件
在Python中我们使用 open 函数打开一个文件:
open('d:/Python环境/test.txt','r')
open函数中的第一个参数是我们要打开文件的绝对路径,第二个参数是我们打开这个文件的方式,‘r’ 表示read,按照读的方式打开;而open函数的返回值是一个文件对象,用来表示文件:
f = open('d:/Python环境/test.txt','r')print(f)print(type(f))
输出:
<_io.TextIOWrapper name='d:/Python环境/test.txt' mode='r' encoding='cp936'>
这个 文件对象 -- class '_io.TextIOWrapper' 是python专门给这个类型取了个名字。
文件对象-- 文件的内容是在硬盘上的,而我们的文件对象,则是内存上的一个变量,我们写的python程序是在内存上运行的,后序读写文件操作,要在内存中实现对硬盘中文件的修改,都是拿着这个文件对象来进行操作。此处的文件对象,就像一个遥控器一样,我们直接操作硬盘中的内存不好操作,我们就在内存中操作这个‘遥控器“,通过这个文件对象间接操作这个硬盘。
在计算机中,也把这样的远程操作的“遥控器:称为 ” 句柄 “ (handler)
当文件不存在的时候,我们再用open打开的话就会抛出异常:
下面是关于 文件打开方式:
关闭文件
我们使用 close 方法来对文件进行删除:
f = open('d:/Python环境/test.txt','r')print(f)print(type(f))f.close()
我们的文件打开 (open函数) 和文件关闭 (close 方法)是一对离不开的孪生兄弟,文件打开之后,是用完之后,必须把文件关闭。这时因为,打开文件其实是在申请一定的系统资源,当我们不在使用这个文件的时候,这个资源就该及时释放,如果不释放,以后其他人在打开文件就用不了。而且资源是有限的,我们打开文件的个数也是有限的。
flist = []count = 0while True: f = open('d:/Python环境/test.txt','r') flist.append(f) count += 1 print(f'打开文件的个数:{count}')
我们发现在打开一定数量的文件后,文件打开操作就失败了。(文件资源泄漏)
我们发现我们最终打开了8189 个文件个数,但是这不是我们这个程序所能打开的最大的文件个数,对于一个程序可以打开多少文件,我们在系统里面可以配置,但是无论配置多少,都不是无穷无尽的,因此我们要记得及时关闭文件。
我们的这个 8189 + 3 = 8192 => 2的13次方,计算机是使用二进制来表示数据的,因此计算机里的很多数据都是按照2的多少次方这样的方式来表示的。那么我们上述的这个 “3” 是怎么来的 呢?
我们每一个程序来启动的时候,都会默认打开三个文件:
- 标准输入 键盘 input() 通过标准输入来读数据
- 标准输出 显示器 print() 往标准输出文件里写数据
- 标准错误 显示器
这三个文件很特殊,这三个文件不是在我们的磁盘里,而是在我们的键盘,显示器当中的。
文件资源泄漏,是很重要的问题,因为这个问题不会第一时间报错出来,我们在比较大的程序中,这问题很难发现,当程序报错的时候,不是在我们写的那一时间报错,可能会在后面的时候发生文件资源泄漏,这时候我们不好再去找这个问题了。
我们上述的代码中,还有一个地方,就是我打开文件之后,我是把这个open返回的文件对象用一个 flist 列表来存储,当我们不用这个列表来存储的时候,我们打开的文件就不止 8189 个了:
flist = []count = 0while True: f = open('d:/Python环境/test.txt','r') # flist.append(f) count += 1 print(f'打开文件的个数:{count}')
我们发现此时我们打开了1200000 + 以上都没有停止,这是为什么呢?
这是因为在Python中有一个重要的机制,叫做垃圾回收机制(GC):可以自动的把不使用的变量给进行释放~~
例如上面这个代码,我们在每一次循环 用 open打开函数创建了一个 f 的文件对象的时候,因为我们没有对他进行使用,所以Python认为他是一个垃圾,所以就把这个文件对象个释放掉了,也就是python自动的帮我们调用了close方法了。
当我们把这个文件对象保存到我们 flist 这个列表当中的时候,我们的垃圾回收就不知道我们后序会不会再次使用这个文件对象,就不会帮我们自动删除了。
但是这个垃圾回收机制还是不是很及时的反应释放,所以我们不能依赖这个机制,每一次使用完文件之后都要关闭文件。
写文件
用 write 方法来写文件
只写方式打开来用write 方法写:
f = open('d:/Python环境/test.txt','w')f.write('hello')f.close()
我们打开文件发现:
’hello‘字符串已经被我们写入了。
需要注意的是,我们在open打开的时候是用 w 的方式打开的,如果我们在写文件的时候用 r 的方式打开的,则会抛出异常:
报错:
报错说:这是一个不支持的操作(UnsupportedOperation)不可写的(not table)
像 w 方法打开的话,会先把文件里面的内容给清空掉然后再执行其他的操作,如果我们用 w 方式open打开文件什么都不执行然后就 close 关闭文件的话,会出现下面的情况:
f = open('d:/Python环境/test.txt','w')f.close()
我们发现之前我们在曾 test.txt 文件中写入的 ’hello’字符串被清空了。
所以我们写文件还有第二种方式:
用 a 方式在文件中追加内容:
#用 w 的方式写文件f = open('d:/Python环境/test.txt','w')f.write('1111')f.close()#用 a 的方式写文件f = open('d:/Python环境/test.txt','a')f.write('2222')f.close()
我们发现此时在test.txt 中在 1111 的基础上追加了 2222 这个字符串。
我们发现我们之前在每一次打开open打开之后,都去 把这个文件关闭(close)了,如果文件对象已经被关闭,那么意味着系统中的和该文件相关的内存资源已经释放了,如果我们在文件关闭的情况下去强行去写的话就会出现异常:
#用 w 的方式写文件f = open('d:/Python环境/test.txt','w')f.write('1111')f.close()f.write('2222')
编译器会提示你 这个 I/0 操作针对了一个被关闭的文件上,当然这操作是不能修改文件中的操作的:
可以看到文件中只有第一次操作有效,2222没有被输入。
读文件
- 再读文件的时候读 open 函数需要使用 ‘r'的方式来打开文件;
- 然后使用 read 方法来完成读操作,参数表示为“读取几个字符”
我们要读取这个文件中的内容。
f = open('d:/Python环境/test.txt','r')result = f.read(2) #表示读前两个字符print(result)f.close()
这个报错是关于字符编码的问题,我们文件中是中文的内容,中文和英文类似,在计算机中,都是使用“数字”来表示字符的,但是具体是哪个数字对应这个汉子,在计算机中国可以有多个版本,我们最主要的版本是 GBK 和 UTF-8 这两种版本。GBK 只能表示我们的一些简体中文,表示的范围有限,一些跟复杂的符号就无法翻译了;而UTF - 8是使用更广泛的编码方式,全世界任一一种语言都可以用这个编码方式来翻译。
在实际开发的时候,就需要保证我们文件内容的编码方式和代码中操作文件的编码方式,得匹配。如果不匹配就容易出现上面的问题。
在代码中是尝试按照gbk来进行解析,而我们查看我们的文件编码发现是:UTF - 8的编码方式。
解决方式就是,让我们的代码按照UTF-8的编码方式来进行度文件的操作,我们在使用open函数的时候在后面多调用一个 encoding 参数:
f = open('d:/Python环境/test.txt','r',encoding = 'utf8')
这个open函数里面的前两个参数叫做 位置参数 ;第三个参数叫做 关键字参数。对于encoding这样的参数,是有默认值的,不传这个参数,在windows系统中,我们不传encoding参数那么它默认值为 GBK 编码方式进行编码。
f = open('d:/Python环境/test.txt','r',encoding = 'utf8')result = f.read(2) #表示读前两个字符print(result)f.close()#床前
现在我们可以读出并打印这个文件里面的前两个字符了,这时我们发现,打印了“床前"这两个汉字,说明在Python中,一个汉字对应这一个字符。
利用for循环,按行读取文件内容
之前我们用read方法,可以读取文件中任意字符的内容,现在我们用for循环来按行来读取文件内容,这个其实是我们更常见的读取文件内容的方法:
f = open('d:/Python环境/test.txt','r',encoding = 'utf8')for line in f: print(f'line = {line}')f.close()#line = 床前明月光#line = 疑似地上霜#line = 举头望明月#line = 低头思故乡
我们在for 循环中创建了一个临时变量--line,而这个for循环的意思是,在f 这个文件对象中,每一次循环,我读取一行的内容给给line这个变量;
我们在打印的时候发现,我们每输出一行就要空出一行,然后在输出下一行。
这是因为,我们本来读取到的文件内容(这一行内容,末尾就有一个 '/n') ; 而此处每一次使用print() 函数打印的时候,就会自动多加一个换行符。
如果不想有print()自动添加换行的行为,可以给print函数多加一个参数:
print(f'line = {line}',end = ' ')
此处的 end = ‘ ’ 中我们在 ' ' 中放的是一个 空格 ,这个参数的意思是,我们每一次调用print 函数的时候,在打印的最后以 空格 的形式来结束这一次打印。同样,按照之前的说法,这个参数也是有默认值的,默认值为 ‘\n’ 。
改进之后输出:
使用 readlines 方法直接把整个文件所有内容都读出来,按照行组织到一个列表里
f = open('d:/Python环境/test.txt','r',encoding = 'utf8')lines = f.readlines()print(lines) #['床前明月光\n', '疑似地上霜\n', '举头望明月\n', '低头思故乡']f.close()
我们发现输出的是一个列表,这个列表里的每一个元素都是文件中每一行的内容,我们发现每一行的后面都有一个 ‘\n' ,如此也就验证了我们之前的操作。
这个方法相对于之前使用 for 循环来读取全部文件操作,有一个好处,就是我们这个 readlines 方法是一次性全部读取完的,而for 循环是每一次循环就读一行;当我们的文件小 的时候还好,当我们文件里内容很多的时候,使用for循环比较低效,因为他需要一行一行的一次一次循环读取。
当然,readlines 方法也有坏处,就是我们能一次使用这个方法来读完文件里全部内容,是因为这个文件本来就不大,如果这个文件里内容太多,我们的内存根本就装不下。
上下文管理器
我们之前说过文件有打开就必须由关闭,但是我们不是每一次度可以考虑到文件 close 操作的,比如我定义一个函数:
def func() f = open('d:/xxxxxxx/xxxxxx','r') #中间来写其他的操作文件的代码 #万一中间的代码,有条件判定,函数返回,就会导致我们的文件泄漏 if 条件: #进行条件处理 f.close() return #代码 # 代码 # 代码 # 代码 if 条件: #进行条件处理 return #这里我就忘记写 f.close() 关闭文件了
我们在条件判读,函数返回的时候,可能不是每一次都可以注意到文件关闭,这样就会导致文件泄漏,那么接下来,我们的上下文管理器就派上用场了。
def func(): with open('d:/Python环境/test.txt','r',encoding = 'utf8') as f: #进行文件处理逻辑 lines = f.readlines() #我们在处理文件操作的时候,可能就是使用if return出函数了 if 条件: return
我们此处之前是吧 open函数的返回值直接赋值给 f,现在我们就不再用 = 来赋值,我们用 with as :语句来进行赋值。我们的 with as : 语句的下面是一个代码块,这个代码块里面就是需要我们对文件的操作代码,最后无论我们在if条件判断,跳出函数,有没有 close 文件,当这个with as :语句执行完,就会自动的帮我们进行 文件关闭操作。
python中库的使用
所谓库也就是别人已经写好的代码,我们拿来直接使用,在python中的库,使用模块的方式去体现的。在python中有python的标准库和其他人写的第三方库,python中的第三方库的种类和数量都是远远大于标准库的。
标准库
我们在Python的官方文档里面就可以看到这些库的内容:
The Python Standard Library — Python 3.10.8 documentationhttps://docs.python.org/3.10/library/index.html
当我们安装了python之后,这个文档也会自动的下载在我们的python目录里:
这个文档是直接下载好的不需要加载。
下面我们举例来讲解库的使用操作:
日期计算器
给定两个日期,计算这两个日期之前相差多少天。
在python中有一个datetime这个库可以帮助我们实现我们的操作:
- 根据日期构造出datetime类型的变量
- 把两个变量进行相减,得到的结果纪委所求
import datetime#先构造 datetime 变量date1 = datetime.datetime(2012,2,14)date2 = datetime.datetime(year = 2016,month = 2,day = 3)print(date2 - date1) #1450 days, 0:00:00
import语句用来导入其他python文件(称为模块module),使用该模块里定义的类、方法或者变量,函数。这个调用的模块可以是第三方库,标准库 或者是自己写的模块。上面的 import datetime 就是调用了 datetime 这个模块。
而datetime.datetime 意思是在datetime 模块中 创建了一个 datetime 这个类型(前面的为模块,后面的为模块中的类型)而后括号中的是这个类型的参数(按照左到右的顺序是 年 月 日 时 分 秒,这些参数的默认值都是 0 )
我们还可以这样写:
from datetime import datetime#先构造 datetime 变量date1 = datetime(2012,2,14)date2 = datetime(year = 2016,month = 2,day = 3)print(date2 - date1) #1450 days, 0:00:00
最上面的 from datetime import datetime 意思是在 datetime 这个模块里 import 一下 datetime这个类型。这个时候我们发现 下面的 赋值操作就不需要在前面加一个 datetime. (模块名. ) 来说明这时datetime 模块里面的内容了。
还可以这样写:
import datetime as dt#先构造 datetime 变量date1 = dt.datetime(2012,2,14) #从前面的某一天到2012.2.14这天有多少天date2 = dt.datetime(year = 2016,month = 2,day = 3) #从前面某一天带2016.2.3这天有多少天print(date2 - date1) #1450 days, 0:00:00
import datetime as dt 的意思就是 我们先import出 datetime 这个模块,然后 as dt 就是把这个模块其一个别名叫做 dt。这个时候,我们在下面创建 datetime 类型的变量的时候,前面的模块申明就可以直接写成 dt ,不用再写成 datetime了。
字符串操作
字符串是 Python 的内置类型,字符串的很多方法不需要导入额外的模块,即直接使用就行了。
翻转单词顺序
输入一个英文句子,翻转句子中的英文单词的顺序,但是单词内字符串的顺序不变。为简单起见,标点符号和普通字母一样处理,例如:
输入字符串” I am a student," ,则输出 "student , a am I" 。我们使用空格来分割单词。
思路:
- 针对我们输入的字符串,使用空格进行切分,字符串 split 方法,可以指定分隔符,把一个字符串分出多个部分,放到一个列表里。
- 在针对刚才的切分列表,进行逆序
- 再把逆序后的列表,给组合起来, 用 join方法进行组合列表。
def reversWord(s): tokens = s.split(' ') #用 空格 来进行分割字符串 tokens.reverse() #对这个列表进行逆序 return ' '.join(tokens) #把 分割逆序之后 tokens里面的每一个字符串 重新拼接print(reversWord('I am a student.')) #student. a am I
我们在pycharm中 在对某一变量后面输入 “ . ” 之后他会把这个类型的方法给列出来方便我们的快捷输入:
但是我们在输入上述代码的时候pycharm没有给这样的提示:
我们对代码进行一下修改:
def reversWord(s: str): tokens = s.split(' ') #用 空格 来进行分割字符串 tokens.reverse() #对这个列表进行逆序 return ' '.join(tokens) #把 分割逆序之后 tokens里面的每一个字符串 重新拼接print(reversWord('I am a student.')) #student. a am I
其中我们对这个 s 变量进行类型声明,这是因为python是一个动态语言,在我们输入之前s的值之前,编译器是不知道这个 s 到底是什么类型,不知道什么类型也就不知道这个变量到底可以用什么方法,我们其中用的 split 方法是否可以用,编译器也是不知道的。所以我们在函数接收参数的时候,给这个变量声明一个 str 类型,告诉编译器这是一个字符串。
我们用以变量来接收 split 方法所传回来的 用空格分割好的字符串列表,这个列表的每个元素是 s 中字符串分割出的每一个单词。
然后用 reverse 方法来对字符串进行逆序。
然后再函数return 返回时候,我们用了 ' '.join(tokens) ;其中 ‘ ’ 是一个空格的字符串,而字符串里面有一个 join 方法,可以帮我们把 tokens 里面的内容填写进去。
最后我们打印这个函数的返回值就可以实现这个效果了。、
旋转字符串
给定两个字符串, s 和 goal ,如果在若干次旋转操作之后, s 能变成 goal ,那么返回true
s 的 旋转操作就是将 s 最左边的字符移动到最右边。
例如: s = 'abcde',在旋转一次之后,结果就是'bcdea'
思路:
我们想到字符串的拼接,假如:s = 'abcde' 我们让 s + s 得到一个新的字符串 => 'abcdeabcde',然后我们在去下标为 [ 1 ] 到 [ 6 ] 的字符串出来,而这个取出来的字符串不就是我们将 s 旋转之后得到的字符串吗?
当然,以上情况是我们知道了 输入的 s 字符串里字符的个数,当我们不知道字符串里字符的个数的时候,那我们取出的下标应该是 [ 1 ] ~~ [ len - 1 ] 其中len的长度是 我们输入的 s 的字符串长度。
而我们发现,我们在 s + s 得到一个更大的字符串,这个字符串其实就包含了我们 s 是的所有旋转之后的结果。
def rotateSyring(s,goal): if len(s) != len(goal): #先判断这两个字符串里的字符个数是否相等,不相等那么说明不管s怎么选旋转都不可能=goal return False return goal in s + s #判断在 s + s 这个大字符串里面有没有和goal相等的子集
统计字符串前缀
给定一个字符串列表 words 和一个字符串 s ,其中 words[i] 和 s 只包括小写英文字母;请你返回words中是字符串 s 前缀的字符串数目。
注:一个字符串前缀是出现在字符串开头的子字符串。子字符串是一个字符串中的连续字符序列;
例:
思路:
遍历一遍word,取出每个字符串,判定当前这个字符串,是否是s 的前缀就行了(s 是否是以曾字符串开头的)
在python中的字符串类型 有一个 startswith 方法判定某一个字符串,他的前缀是不是另一个字符串。是就返回 True 不是就返回 False。
def countPrefixes(words: list, s: str): count = 0 for word in words: #用 for 循环 遍历 words 这个列表 if s.startswith(word): 每一次遍历,只要这个元素是 s 的前缀就让 count + 1 #s 是以word 开头 count += 1 return countprint(countPrefixes(['a','b','c','ab','bc','abc'],'abc')) #3
来源地址:https://blog.csdn.net/chihiro1122/article/details/127894953