一、with语法
一般情况打开一个文件,经过操作之后,都要显式的执行xx.close() 将文件关闭 。
with 用于需要打开、关闭成对的操作,可以自动关闭打开对象 .
with open('/tmp/passwd','w') as f:
print(f.closed)
print(f.write('hello'))
print(f.closed)
我们能看到with语句只对他内部的文件起到打开作用,当我们查看全局的f文件的关闭状态时,返回值为True;这样也就解决了上一节我们提出的问题,open只能对相对于小一些的文件进行操作,当真正需要我们去处理文件时,则需要with语句来解决,帮助我们节省空间;我们可以用实验验证:
# 判断文件对象是否可迭代
from collections import Iterable
f = open("/etc/passwd")
print(isinstance(f,Iterable))
# 文件对象是可迭代,默认遍历文件的每一行;
for line in f.readlines:
print(line)
针对于大型文件我们的内存空间有限,只能使用with语句来进行文件的操作;
二、对文件内容的读取
对于上面的文件操作我们还可以利用我们上一节中学到的时间装饰器来看看哪种方式的读取速度更快;
import time
import random
import functools
def timeit(fun): # fun=add
@functools.wraps(fun)
def wrapper(*args, **kwargs): # 1,2 args=(1,2)
"""wrapper functions"""
start_time = time.time()
res = fun(*args, **kwargs) # 参数解包add(*args, **kwargs)
end_time = time.time()
print("%s运行时间为%ss" %(fun.__name__,end_time-start_time))
return res
return wrapper
@timeit
def open1():
with open("/etc/passwd") as f:
# f.readlines(), 会把文件的所有内容加载到内存中;适用于小文件;
for line in f.readlines():
line.split(":")
@timeit
def open2():
with open("/etc/passwd") as f:
# 迭代----> 生成器
for line in f:
line.split(":")
open1()
open2()
惊奇的是,第一种方式f.readlines()速度更快一些,速度快了那么其他地方也就相对的要做出一定的贡献-----占用大量内存空间;所以我们更推荐你使用第二种打开方式;
三、文件的复制
def copy1(sourcefile, destfile):
# 要点: python2.7及以后,with支持对多个文件的上下文管理;
with open(sourcefile) as f1, open(destfile,'a+') as f2:
for line in f1:
f2.write(line)
print("拷贝 %s 为 %s 成功" %(sourcefile,destfile))
copy1('ips.txt', 'hello_02.txt')
拷贝结果为:
上面则为我们在文件复制当中用到的函数;
同样我们也可以使用时间装饰器来查看文件运行的时间:
import functools
import time
def timeit(fun): # fun=add
@functools.wraps(fun)
def wrapper(*args, **kwargs): # 1,2 args=(1,2)
"""wrapper functions"""
start_time = time.time()
res = fun(*args, **kwargs) # 参数解包add(*args, **kwargs)
end_time = time.time()
print("%s运行时间为%ss" %(fun.__name__,end_time-start_time))
return res
return wrapper
@timeit
def copy1(sourcefile, destfile):
# 要点: python2.7及以后,with支持对多个文件的上下文管理;
with open(sourcefile) as f1, open(destfile,'a+') as f2:
for line in f1:
f2.write(line)
print("拷贝 %s 为 %s 成功" %(sourcefile,destfile))
copy1('ips.txt', 'hello_02.txt')
四、文件读取编程题---京东二面题
题目要求:
1. 生成一个大文件ips.txt,要求1200行, 每行随机为172.25.254.0/24段的ip;
2. 读取ips.txt文件统计这个文件中ip出现频率排前10的ip;
答:1:
import random
from collections import Counter
def create_ips_file(filename):
ips = ['172.25.254.' + str(i) for i in range(1, 255)]
with open(filename, 'a+') as f:
for count in range(1200):
f.write(random.sample(ips,1)[0]+'\n')
random.sample(seq, n) 从序列seq中选择n个随机且独立的元素;在ips列表中随机拿出一个来写入到filename文件中;
运行结果为上图;
2:
def sorted_by_ip(filename, count=10):
ips_dict = dict()
with open(filename) as f:
for ip in f:
if ip in ips_dict:
ips_dict[ip] += 1
else:
ips_dict[ip] = 1
sorted_ip = sorted(ips_dict.items(),key=lambda x:x[1],reverse=True)[:count]
return sorted_ip
print(sorted_by_ip('ips.txt'))
创建一个字典,遍历我们上面创建的1200个ip的文件,把ip当作key值;判断,若这个key值存在那么他的value值+1,如若不存在,则被这个ip对应的value值赋值为1;字典(Dictionary) items() 函数以列表返回可遍历的(键, 值) 元组数组。调用soryed方法,并且以倒叙输出(也就是按照题目要求,从多到少排列)
Counter类介绍
为了体现我们python的强大的数据库;我们有一个counter模块可以提供给我们使用,方便我们排序:
在2.4版本中新加入,源代码Lib/collections.py和Lib/_abcoll.py。该模块实现了专用的容器数据类型来替代python的通用内置容器:dict(字典),list(列表), set(集合)和tuple(元组)。
一个Counter是dict子类,用于计数可哈希的对象。这是一个无序的容器,元素被作为字典的key存储,它们的计数作为字典的value存储。Counts允许是任何证书,包括0和负数。Counter和其它语言中的bags或者multisets类似。
from collections import Counter
def new_method_sorted_by_ip(filename):
with open(filename) as f:
ipcount = Counter(f)
print(ipcount.most_common(10))
new_method_sorted_by_ip("ips.txt")
此处我们不做深入的探究,如果你有兴趣请自行百度。