Python文件访问
下面讲怎么用循环来访问文件,要访问文件首先要先open(打开)
打开文件
打开文件用open内置函数
内置函数open的帮助信息:
![]()
有一个参数name(就是文件名),后面是中括号(表示可有可无选项),mode是以什么模式打开,buffering就是有一个缓存,这个函数返回的是一个文件对象,在这里mode模式主要有r、w、a这几种
r :以读方式打开(只能读不能改写,如果文件不存在会报错,必须打开一个已经存在的文件)
w :以写方式打开(如果文件不存在则会创建)
a :以追加模式(如果文件不存在也会去创建)
r+ :以读写模式打开
w+ :以读写模式打开(参见w,跟w一样)
a+ :以读写模式打开(参见a)
rb :以二进制读模式打开(文件文件也可以用二进制模式打开)
wb :以二进制写模式打开(参见w)
ab :以二进制追加模式打开(参见a)
rb+ :以二进制读写模式打开(参见r+)
wb+ :以二进制读写模式打开(参见w+)
ab+ :以二进制读写模式打开(参见a+)
with open
在命令行下打开一个文件,如果后面没写模式的话返回的是一个文件对象,通过一个变量去接收一下,一般我们都把它这个变量fd叫文件描述述,type一下是文件对象。
![]()
对象里面有很多方法
![]()
通过内置函数open打开文件后要使用close关掉,
![]()
打开之后可以往里面写入内容,有一个write方法,写只能接受字符串,由于是使用'w'模式打开的,会把原文件给覆盖。然后fd.close()把文件关掉才能看到写进去的"YJB"字符串,不关闭是看不到刚刚写进去的内容
![]()
当再次使用fd.write("YJB")方式打开时文件里面的内容又被覆盖又没有了。
如果这样写会认为是个数值,需要把数值用引号给引起来
![]()
fd.write('123'\n):自动换行
还可以以追加的方式打开,这个内容就会以追加的方式追加到文件末尾
![]()
以读的模式打开,如果这个模式没加默认以r的模式打开
fd.read方法就是读的意思
![]()
当再次执行fd = open('/tmp/tmp.txt')语句再去读时就不显示任何内容了,默认情况下是从第一个字符读到最后一个字符(就是把整个文件内容都读完了),读完之后呢指针跑到了文件的最后,所以再执行一次显示的结果就空了,
查看类型是一个字符串:
![]()
可以通过fd.seek或fd.tell来移动指针(文件倒读的方式)
后面也可以加上一些参数,表示只读几个字符,没有加任何参数表示从指针当前位置一直读到文件最后
![]()
fd.readline方法是读一行,当指针跑到最后再读就只显示空字符串了
![]()
fd.readlines():表示读多行,有多少行就读多少行,而且返回的是一个list列表,把每一行做为一个元素,后面还带一个换行符,
![]()
fd.read()和fd.readline()返回的都是字符串,而fd.readlines()则返回的是一个列表,读文件主要是这三个
写一个循环把整个文件内容都读出来
用fd.readlines,既然返回的是列表就可以用循环遍历一下,列表是序列,而且列表的每一个元素就是每一行,在每一行的后面默认还会加上换行符。这就是对文件做遍历。而且每一行后面还有一个空格,想解决这个问题只需要在print line后面加个逗号,就行了,因为列表里面的每个元素都有\n,而print默认情况下也有\n
![]()
加上逗号,来抑制print的\n,就不会出现空行了
![]()
但是有一个问题:假如这个文件很大,比如1个G,在执行fd = open('/tmp/tmp.txt')这一行命令时不会占用内存多少资源,这会在内存当中产生一个对象,而执行for line in fd.readlines(): 这一行的时候会产生一个list,这个list假如一个G,这一个G这么多的内容都在内存里,对内存开销很大,占用资源很多,可以这样写:把readlines()给去掉,跟刚才执行结果是一模一样的,for line in fd: 中的fd是一个文件对象,而文件对象呢可以通过for循环对这个文件做遍历,遍历一次会取到文件中的一行,遍历一次取到文件中的一行,不会事先在内存中产生一个G的大的列表,这样不会占用内存资源,所以这种写法比刚才写法更优化些。对象在内存资源占用的很少,在对它做遍历的时候遍历一次就会打印一行,是一行一行出,不会一下子出一个大的列表,
![]()
fd.next()方法:next是一行一行取,对文件做遍历的时候就相当于是next,遍历一次就会执行一次next方法,只不过是通过for循环来自动执行了,不需要手动来执行
我们使用for循环可以很容易地去遍历一个文件,遍历文件就是访问文件的每一行,接下来使用while循环去试一下,看while能不能实现
在这里有一个fd.reline方法,reline方法是一行一行读,返回值也带\n,当读完之后最后返回的是空字符串
![]()
空字符串是False
![]()
使用while循环通常需要一个条件才退出这个循环,不像for循环有一定的次数,while循环需要给一个条件,什么条件从循环中退出,其实这个条件是这样的,当访问到最后的时候就退出,那咋样才能判断是最后呢,当访问到最后返回一个空字符串,可以用它来作为条件
通过while去访问一个文件:
还是先打开一个文件(别忘了open文件到最后关掉文件fd.closee,不关掉最后也会回收的,不关掉当这个程序退出的时候也会close这个文件的,不过最好养成这个习惯)
while True是一个死循环,当读进一行后把这个存到line这个变量里去,然后去判断一下这行是否为空(为空就是False,not Flase就是为True,True条件成立时直接break退出),为空就是读到了文件的最后,然后把每一行再打印出来,最好加上逗号,,fd.readline在后面也会加上\n换行符。
![]()
跟for打印的结果是一样的,
![]()
别忘了在文件最后一行加上fd.close()养成好的习惯
如果忘了加close也无所谓,下面讲下with open,它就是不需要close,它跟open一个文件是一样的,看怎样使用with open:
as后面跟文件描述符(随便起的一个名字),后面就不需要用close了,with open属于语法,是个关键字,跟for、if循环一样,所以下面的代码要有缩进,with open这一行末尾也必须要有冒号:,什么时候认为完事了呢?当里面代码执行完之后,出现下一个代码而下一个代码是跟with open代码块平行的(在同一缩进里),就认为open这个文件已经结束了,会自动把这个文件给close而不需要手动去close了。
这是在Python2.6以后才支持,在Python2.5是不支持with open写法的,
![]()
对于小文件可以直接用read方法直接读整个文件
对于大文件可以直接用循环的方式访问文件,因为是对每一行来访问的,所以可以对每一行来进行操作,比如说这一行有什么特点,然后把它给拿出来,
统计系统free内存,统计完之后占多少百分比,定个脚本一执行打印出free内存,显示更友好一点(如根据值的大小显示不同的单位),收集这些信息呢通常情况下不是通过命令来看这些信息的,其实使用free也是通过cat /proc/meminfo这个系统文件查看信息的,
第一行就是Total,把MemTotal和MemFree这两个值取出来然后做个比较就能得到百分比,这里面其实就是对文件来做遍历了。
![]()
在这里要取到文件的每一行,所以最好使用for循环,read要拿到每一行比较麻烦,with...as是关键字,别忘了缩进。用for对它来做下一个遍历,这里面只要写fd就可以了,不要写fd.readlines,cat文件MemTotal和MemFree是第一行和第二行,如果这个文件确定这两个分别是第一行和第二行非常容易,就可以用刚才的fd.readlines执行再次就能得到这两个,但有的时候如果这两个不在开头的前两行取出来比较麻烦,在这里介绍下一个通用的方法,字符串有下一个方法startswith,从字面上理解呢,是以什么开头,然后就可以通过这种方法来判断,是否是以"MemTotal"开头,如果是的话取到总的值,MemTotal是下一个字符串,既是字符串那就可以进行切片处理,切的时候需要数一下数到多少,中间还有这么多空格才能取到这个数值。在这里再介绍字符串另外下一个方法split。在这里可以把"MemTotal: 2941812 kB"这一行按空格进行切分成列表,MemTotal为第一个元素,2941812为第二个元素,kB为第三个元素,那取第二个元素就是total的值,第一次呢line是第一行,然后用同样的方法去取MemFree:if line.startswith('MemFree): ,也是进行切片取第二个元素,当文件都读完之后呢应该会有total和free这两个值的,看能否取到
![]()
它是下一个prefix,返回的是下一个bool值,如果返回True则以指定的prefix开头的,如果不是的话返回False,其实就是以什么开头的,如果是就返回True,不是就返回False
![]()
![]()
字符串切片方法,a.split是切割、分割的意思,:
![]()
成功取到这两个值,然后可以通过free命令查看一下基本上是一样的:
![]()
这里就是对文件进行了操作,读文件,然后遍历每一行,遍历每一行的时候对每一行进行判断,这一行是否是以这个关键字开头的,是的话把它进行切分(这里介绍了字符串的两个方法startswith和split,split是按照某个符号来把字符串进行分割成一个列表,再通过列表的索引操作来得到这个值),为了让程序更好一些可以这样做:
如果这个条件成立的话进入下一次循环了,别再进入下面进行比较了,如果第一个if满足了那下面的if就没必要进行比较的,直接在第一个循环下面加上continue进入下一次循环,如果找到下面if语句的内容就可以break退出循环了,这样的效率会更高点
![]()
这里取到的free单位是K,可以把它变成M,现在free和这个total都是字符串,因为分割成列表,作为列表的元素都是字符串,需要把free/1024.0,必须把free加个int变成数值类型,这样写会有很多小数点,可以来个格式化,格式化不仅有%s还有%d、%f,在这里可以保留两位小数,打印完之后是个字符串,虽然后面是个浮点型,但是通过前面"%.2f"字符串格式化输出就变成字符串了。
打印结果说不支持字符串和浮点,需要在%后面部分加上小括号(),因为后面有个/除法,它认为这个/是对这个字符串做除法,做什么除法呢,做一个除于1024.0跟浮点数进行相除,它把%.2f%(int(free)当成最接近的它俩进行相除,因为这两个进行操作完之后就变成字符串了,字符串不支持除法。要以后面的值替换前面的,所以要加上括号。
![]()
这样就没问题了
![]()
然后再来个字符串连接+'M',"%.2f" % (int(free)/1024.0)整个都是字符串,
![]()
还有计算一下百分比,
例:range是取数,查看i的类型并打印出i来
![]()
也可以这样写,转换一下,之前学过%s,把i转换一下,换成%s,然后保存到一个变量里,然后print a是什么类型并打印a,发现数字都变成字符串了,后面%i这个值不管是字符串也好还是数字也好,只要通过这个%s(%s叫字符串格式化)得到的值肯定是字符串。上面求内存的例子%f也叫字符串格式化,跟%s一样的,虽然后面的值是浮点数,最终也变成了字符串。
![]()
Python的类型有数值型、字符串型、列表、元组、字典,这些数据类型是可以互相转换的,
类型转换
help(int)
它有一个参数,还有一个可选的参数,转换一个字符串或者一个数字到一个整数,也就是说这个x可以是个字符串也可以是个整数,但最终结果呢返回一个整数,小数转换会取整数部分,如果int('a')会报错,需要加个16,会认为这个a是16进制中的字符串,‘0xa'表示16进制的前缘,0x可以省略不写
![]()
![]()
![]()
![]()
十六进制的字符串转为十进制
int('12',16)
18
int('0x12',16)
18
十进制转为十六进制
hex(10)
'0xa'
10在16进制中就是a
help(hex),参数是个number,返回一个string字符串,在Python中只要放到引号中都是字符串。
![]()
字符串之间是不能进行加法运算的,只有数字之间才能进行加法或者其他运算。为什么要进行转换呢,因为有的时候要把字符串当成十六进制的字符串,然后再进行一些运算,
十进制转为字符串
str(10)
'10'
字符串转为十进制
int('10')
![]()
int('a')比较特殊,转换会报错,字符串必须是纯数字的,如果数字中有一个不是数字都会报错,可以把一个字符串转换成十进制没问题,但是这个字符串必须是纯数字的。
下面通过int、hex、str这三个函数来做一个小练习:
知道mac地址来求mac地址的下一个地址(所谓一下地址就是mac地址的最后一位+1生成一个新的地址)。通常在服务器上有多个网卡的话其实这几个mac地址都是挨着的。
先定义一个变量,先取最后两位,因为最后两位是16进制,取最后两位只需要序列的分片就可以实现了,取到最后两位后需要把十六进制转换成十进制,
![]()
然后再把十进制数用hex()转换成十六进制,如果打印D1说明没问题
![]()
前面多了个0x,0x就是表示16进制的意思,不想要前面两位可以加[2]给去掉
![]()
然后在前面再定义一个变量是mac地址的前缀,只要前面五位,也可以用切片,[:-3]
![]()
新的两位已经有了,可以把小写转换成大写
![]()
把mac地址最后两位改成01,新的mac地址应该是02,但执行后0没有只剩一个2了,在十进制中如果前面是0的话通常就会给省略了。应该先判断plus_one这个数,如果最终得到的结果是0到9之间的,前面应该补个0,需要加个判断语句,range(10)返回的是一个列表,然后用'0'+new_last_two
![]()
还有一种情况,当最后两位是0a时,下一位应该是0b,当执行到else后执行了new_last_two = hex(plus_one)[2:]这句,相当于这里0也没取到,
![]()
上面有三种情况,第一种情况就是正常的情况返回的是两个值,其实觉得就两种情况要么返回一个值要反返回两个(两位)值,如果是一个值的话前面补0就可以了,第三种情况就是一个单独的字符前面也补0,
字符串转列表(只需要一个list函数就可以了,这样就把字符串里每个元素转成列表,当成列表里每个元素)
list(string)
列表转字符串(需要通过字符串的join方法)
"".join(list)
![]()
参数是可迭代的,返回值是字符串,S是这个迭代每个元素之间是以什么分开的,就是每个元素之间的分隔符,比如用冒号:来分隔
![]()
![]()
字符串转元组(用tuple函数)
tuple(string)
元组转字符串(跟列表转字符串的方法是一样的)
"".join(tuple)
![]()
列表转元组(也是用tuple函数)
![]()
元组转列表(也是用tuple函数)
![]()
字典转换成列表(转换之后列表里的每个元素又是一个元组,而这个元组呢恰恰是这个字典里的一个key和一个value的对应关系)
![]()
列表转换成字典
如果想把这个列表转换成字典的话用dict涵数,并不是说所有的列表或者元组都可以转换成,只有列表里面每个元素呢又是列表或者是元组,而且呢里面小的元组或者列表是由两个元素组成的,其中一个是key另外一个是value。
![]()