4.5 追加写文件
有时候我们不想将文件内容全部重置了,需要在文件现有内容后面追加内容怎么办?
4.5.1 open
将文件打开方式设置为追加写模式。
rootDir=os.path.dirname(os.path.abspath(__file__)) "\\TFSE"
newFile=rootDir "\\newFile.py"
fo=open(newFile,"a")
iffo:
fo.write("福哥又说话了")
fo.close()
fc=""
fo=open(newFile,"r")
iffo:
whileTrue:
line=fo.readline()
ifnotline:
break
fc =line
fo.close()
print(fc)12345678910111213141516171819

4.6 文件指针
我们以可读可写模式打开文件后,通过seek移动文件指针,在指定位置进行读写操作,这种方式可以避免将文件全部内容加载到内存当中就可以完成很多情况的读写操作,可以解决针对超大文件内容的格式化数据的编辑的难题。
4.6.1 文件数据库
举例:我们制作一个文件数据库,将用户名和密码存储到一个文件里面,用户名和密码格式如下
[用户名(最长50个字符)][密码(取MD5哈希串,所以固定32个字符)][换行]1
然后我们只要保证每一组用户信息都是一样的固定长度就可以通过简单计算知道某一个用户ID对应的用户信息在文件的哪个位置上面,进而通过fseek就可以快速定位并读取用户信息了。

因为我们规定了文件内的用户数据格式,那么通过操控文件指针就可以实现这个文本数据库的增、删、改、查功能了,是不是觉得很神奇?
4.6.2 文本数据库定义
这是文本数据库TFSimpleTextDb的定义。
classTFSimpleTextDb:
rootDir=""
fp=""
fo=None
fs=0
lastUserId=0
lastLoginUserId=0
def__init__(self,rootDir):
self.rootDir=rootDir
self.fp=""
self.fo=None
self.fs=0
self.lastUserId=0
self.lastLoginUserId=0
defstrPad(self,s,padLen,chr):
strLen=len(s)
padLen-=strLen
foriinrange(0,padLen):
s =chr
returns
defstrRepeat(self,chr,num):
s=""
foriinrange(0,num):
s =chr
returns
defconn(self):
self.fp=self.rootDir "/users.tongfu.net.txt"
#数据库文件不存在就创建它
ifnotos.path.exists(self.fp):
withopen(self.fp,"w")asfo:
fo.close()
try:
#打开数据库文件
self.fo=open(self.fp,"r ")
#记得当前文件尺寸
self.fs=os.path.getsize(self.fp)
returnTrue
exceptExceptionase:
print(e)
returnFalse
defadd(self,user,pwd):
iflen(user)>50:
returnfalse
#移动文件指针到文件末尾
self.fo.seek(self.fs)
#写入一行数据保存用户名和密码
#用户名用strpad补位,保证数据结构一致
line="%s%s\n"%(
self.strPad(user,50,''),
md5(pwd).hexdigest()
)
self.fo.write(line)
self.fs =len(line)
#立即写入新内容到磁盘
self.fo.flush()
#新用户ID就是最新一行的行号
self.lastUserId=int(self.fs/(50 32 1))
returnTrue
defmod(self,userId,pwd):
#移动文件指针到指定userId对应的用户信息位置
self.fo.seek((50 32 1)*(userId-1))
#按约定数据长度读取数据栏位上的数据
userInDb=(self.fo.read(50)).strip()
pwdInDb=self.fo.read(32)
ifuserInDb=="":
returnFalse
#修改密码
#后退32个字符,在密码栏位开始处开始写
self.fo.seek(self.fo.tell()-32)
self.fo.write(md5(pwd).hexdigest())
#立即写入新内容到磁盘
self.fo.flush()
returnTrue
defdelete(self,userId):
#移动文件指针到指定userId对应的用户信息位置
self.fo.seek((50 32 1)*(userId-1))
#按约定数据长度读取数据栏位上的数据
userInDb=(self.fo.read(50)).strip()
pwdInDb=self.fo.read(32)
ifuserInDb=="":
returnFalse
#修改密码
#后退82个字符,在用户名栏位开始处开始写
#写入82个空字符表示用户已经被删除了
self.fo.seek(self.fo.tell()-82)
self.fo.write(self.strRepeat("",82))
#立即写入新内容到磁盘
self.fo.flush()
returnTrue
deflogin(self,user,pwd=""):
fo=open(self.fp,"r")
iffo:
whileTrue:
#解析出用户名和密码
userInDb=fo.read(50)
ifnotuserInDb:
break
pwdInDb=fo.read(32)
ifnotpwdInDb:
break
LF=fo.read(1)
ifnotLF:
break
userInDb=userInDb.strip()
#验证用户名
ifuser==userInDb:
#验证密码
ifmd5(pwd).hexdigest()==pwdInDb:
#计算登录用户ID
self.lastLoginUserId=int(fo.tell()/(50 32 1))
return0
else:
return1#密码错误
fo.close()
return2#用户名不存在
defclose(self):
self.fo.close()
defgetLastUserId(self):
returnself.lastUserId
defgetLastLoginUserId(self):
returnself.lastLoginUserId123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
4.6.3 文本数据库测试
下面是对这个TFSimpleTextDb的测试代码。
myTFSimpleTextDb=TFSimpleTextDb(rootDir)
myTFSimpleTextDb.conn()
ifmyTFSimpleTextDb.login("福哥")==2:
print("创建用户福哥\n")
myTFSimpleTextDb.add("福哥","123456")
print(myTFSimpleTextDb.getLastUserId())
ifmyTFSimpleTextDb.login("鬼谷子叔叔")==2:
print("创建用户鬼谷子叔叔\n")
myTFSimpleTextDb.add("鬼谷子叔叔","abcdef")
print(myTFSimpleTextDb.getLastUserId())
ifmyTFSimpleTextDb.login("同福大哥")==2:
print("创建用户同福大哥\n")
myTFSimpleTextDb.add("同福大哥","123456")
print(myTFSimpleTextDb.getLastUserId())
print("修改鬼谷子叔叔的密码\n")
print(myTFSimpleTextDb.mod(2,"123456"))
print("使用新密码登录鬼谷子叔叔\n")
print(myTFSimpleTextDb.login("鬼谷子叔叔","123456"))
print(myTFSimpleTextDb.getLastLoginUserId())
print("删除鬼谷子叔叔\n")
print(myTFSimpleTextDb.delete(2))
print("再次登录鬼谷子叔叔\n")
print(myTFSimpleTextDb.login("鬼谷子叔叔","123456"))
myTFSimpleTextDb.close()123456789101112131415161718192021222324252627

超大文件读写需要避免将文件里面的内容全部读出来放到变量里这种行为,因为变量都是存在于内存当中的,如果变量里面的数据太多会把内存用光,就会导致系统瘫痪,这个太可怕了~~
5. 将一个10G文件里的“福哥”换成“鬼谷子叔叔”
这个时候我们可以通过开启两个文件指针,一个文件指针负责读现有文件,一个文件指针负责写新的临时文件,完成后删除现有文件,再将临时文件重命名为原来的文件,就搞定了~~
rootDir=os.path.dirname(os.path.abspath(__file__)) "\\TFSE"
newFile=rootDir "\\newFile.py"
tmpFile=rootDir "\\newFile.py.tmp"
foNew=open(newFile,"r")
foTmp=open(tmpFile,"w")
iffoNewandfoTmp:
whileTrue:
tmpLine=foNew.readline()
ifnottmpLine:
break
tmpLine=tmpLine.replace("福哥","鬼谷子叔叔")
foTmp.write(tmpLine)
foNew.close()
foTmp.close()
os.unlink(newFile)
os.rename(tmpFile,newFile)
fc=""
withopen(newFile,"r")asfo:
fc =fo.readline()
print(fc)12345678910111213141516171819202122
