Fork me on GitHub

第三章| 3.1文件处理

1、三元运算

简单条件语句:

if 条件成立:
    val = 1
else:
    val = 2

改成三元运算:

val = 1 if 条件成立 else 2

2、文件处理

(用python对硬盘上的文件操作)

读取文件的三个方法:read()、readline()、readlines()

三个方法均可接受一个变量用以限制每次读取的数据量,通常不使用该变量。

关于read()方法: 1、读取整个文件,将文件内容放到一个字符串变量中 2、如果文件大于可用内存,不可能使用这种处理

关于readline()方法: 1、readline()每次读取一行,比readlines()慢得多 2、readline()返回的是一个字符串对象,保存当前行的内容

关于readlines()方法: 1、一次性读取整个文件。 2、自动将文件内容分析成一个行的列表。

 

f = open(file = "contact.txt",mode="r",encoding="utf-8") #r模式以什么方式存的就要以什么方式读出来。如果文件是gbk格式,就encoding=“gbk”
data = f.read() #read方法是把文件全部读出到屏幕输出;readline是读取文件的一行信息;readlines是读取整个文件,输出屏幕的是一行包含所有的信息。
print(data)  
f.close() #一定要加上这个,不然数据文件数据就没有了,哎呀
read打印:
王心       北京    159    46    13813234424
马纤羽     深圳    173    50    13744234523
乔亦菲     广州    172    52    15823423525
罗梦竹     北京    175    49    18623423421
刘诺涵     北京    170    48    18623423765
岳妮妮     深圳    177    54    18835324553
贺婉萱     深圳    174    52    18933434452
叶梓萱     上海    171    49    18042432324
杜姗姗     北京    167    49    13324523342
black girl 河北    167    50    135423422334

readline:
王心       北京    159    46    13813234424

readlines:
['王心       北京    159    46    13813234424\n', '马纤羽     深圳    173    50    13744234523\n', '乔亦菲     广州    172    52    15823423525\n', '罗梦竹     北京    175    49    18623423421\n', '刘诺涵     北京    170    48    18623423765\n', '岳妮妮     深圳    177    54    18835324553\n', '贺婉萱     深圳    174    52    18933434452\n', '叶梓萱     上海    171    49    18042432324\n', '杜姗姗     北京    167    49    13324523342\n', 'black girl 河北    167    50    135423422334']

存在硬盘上是二进制。以什么方式存的文件,就要以什么样的方式打开这个文件。此处的encoding必须和文件在保存时设置的编码一致,不然“断句”会不准确从而造成乱码。
r文本模式, 二进制转换为字符串。open把它转一下。

rb为二进制模式,把这段内容直接读到内存里边来,硬盘怎么存的就怎么拿来,不用它转了。 视频、图片或者网络传输的文本,都是二进制模式。
f = open(file = "contact.txt",mode="rb") #二进制模式就不需要encoding了
data = f.read()
print(data)  #打印出来就是二进制格式
f.close()
打印:
b'\xe7\x8e\x8b\xe5\xbf\x83       \xe5\x8c\x97\xe4\xba\xac    159    46    13813234424\r\n\xe9\xa9\xac\xe7\xba\xa4\xe7\xbe\xbd     \xe6\xb7\xb1\xe5\x9c\xb3    173    50    13744234523\r\n\xe4\xb9\x94\xe4\xba\xa6\xe8\x8f\xb2     \xe5\xb9\xbf\xe5\xb7\x9e    172    52    15823423525\r\n\xe7\xbd\x97\xe6\xa2\xa6\xe7\xab\xb9     \xe5\x8c\x97\xe4\xba\xac    175    49    18623423421\r\n\xe5\x88\x98\xe8\xaf\xba\xe6\xb6\xb5     \xe5\x8c\x97\xe4\xba\xac    170    48    18623423765\r\n\xe5\xb2\xb3\xe5\xa6\xae\xe5\xa6\xae     \xe6\xb7\xb1\xe5\x9c\xb3    177    54    18835324553\r\n\xe8\xb4\xba\xe5\xa9\x89\xe8\x90\xb1     \xe6\xb7\xb1\xe5\x9c\xb3    174    52    18933434452\r\n\xe5\x8f\xb6\xe6\xa2\x93\xe8\x90\xb1     \xe4\xb8\x8a\xe6\xb5\xb7    171    49    18042432324\r\n\xe6\x9d\x9c\xe5\xa7\x97\xe5\xa7\x97     \xe5\x8c\x97\xe4\xba\xac    167    49    13324523342\r\nblack girl \xe6\xb2\xb3\xe5\x8c\x97    167    50    135423422334'

 

自动检测编码:(不知道原文件是什么编码)
import chardet
f = open('log',mode='rb')
data = f.read()
f.close()

result = chardet.detect(open('log',mode = 'rb').read())
print(result)  #{'encoding': 'utf-8', 'confidence': 0.99, 'language': ''}

 

print(chardet.detect(data))
   --->
   {'encoding': 'GB2312', 'confidence': 0.99, 'language': 'Chinese'}  ##confidence 检测编码 99%的可能性为GB2312 

print(data.decode("GB2312"))                                          ## 把GB2312解码为 Umcode  
  --->
  王心       北京    159    46    13813234424

 

循环文件

(边读边处理,内存就存一小块。)

f = open("contact.txt",'r',encoding="gbk")
for line in f:
    print(line)
f.close()
#循环打印出:
王心 北京 159 46 13813234424 马纤羽 深圳 173 50 13744234523 乔亦菲 广州 172 52 15823423525 罗梦竹 北京 175 49 18623423421 刘诺涵 北京 170 48 18623423765 岳妮妮 深圳 177 54 18835324553 贺婉萱 深圳 174 52 18933434452 叶梓萱 上海 171 49 18042432324 杜姗姗 北京 167 49 13324523342 black girl 河北 167 50 135423422334

 

写文件

(w是创建不是修改,有就清空覆盖了。)   w给转换,而wb 二进制的就不用转换了。

f = open("兼职.txt",'w',encoding = "gbk") #创建一个文件以gbk的形式
f.write(“路飞学城!”) #”路飞学城”是以unicode的形式写的,再编码为gbk
f.close()
f = open("兼职2.txt",'wb') #创建一个兼职2的文件
f.write(“路飞学城!”.encode("gbk")) #encode下表示以什么样的编码存在
f.close() 
f = open("兼职.txt",'wb')
f.write("原子二号".encode("gbk")) #新写进去的没有的就清除再创建,会把原来的文件被清空了。
f.close()

追加模式

(修改,追加到文件尾部。以a或者ab的模式打开)

f = open("contact.txt",'ab') #以ab的模式打开 
f.write("\n小红    北京  167   55   13312340322”.encode("gbk"))#追加到文件后面,\n为换行
f.close()

比如:追加“给岁月以文明,而不是给文明以岁月”在第五行。(就是把其他的内容往下挤,不是覆盖第五行)(见练习题)

lines = []
f = open('三体语录','r',encoding='utf-8')
for line in f:
  lines.append(line)
lines.insert(5,'给岁月以文明,而不是给文明以岁月\n')
s = ''.join(lines)  #把列表转成字符串
with open('三体语录','w',encoding='utf-8')as f_write:
  f_write.write(s)
  f_write.close()

混合操作

(既能写又能读即 读写模式。r+ )

f = open("contact.txt", 'r+',encoding="gbk")
data = f.read()
print("content\n", data)
f.write("\nnewreline 1哈哈")
f.write("\nnewreline 2哈哈")
f.write("\nnewreline 3哈哈")
f.write("\nnewreline 4哈哈")
print("new content\n", f.read()) # 新写入的newreline 哈哈没有输出read出来,因为 读的时候是一行行的读,光标到后边没东西了,就输出为空了

 

读写就相当于append,只是多了个可以读。

写读模式 w+  (几乎没什么应用场景)

读写是先读后写,以读的模式打开然后往后继续追加;写读模式,是以写的模式打开(就是创建的模式)支持你读,把你写的读出来,但是之前的被清空了。

f = open("contact.txt", 'w+',encoding="gbk")
data = f.read()
print("content\n", data)
f.write("\nnewreline 1哈哈")
f.write("\nnewreline 2哈哈")
f.write("\nnewreline 3哈哈")
f.write("\nnewreline 4哈哈")
print("new content\n", f.read()) #读的时候是一行行的读,光标到后边没东西了,就输出为空了
f.close()

输出-->
content
 
new content
 

文件操作的其他功能

   def fileno(self, *args, **kwargs): # real signature unknown
        返回文件句柄在内核中的索引值,以后做IO多路复用时可以用到
    def flush(self, *args, **kwargs): # real signature unknown
        把文件从内存buffer里强制刷新到硬盘

    def readable(self, *args, **kwargs): # real signature unknown
        判断是否可读

    def readline(self, *args, **kwargs): # real signature unknown
        只读一行,遇到\r or \n为止

    def seek(self, *args, **kwargs): # real signature unknown
        把操作文件的光标移到指定位置
        *注意seek的长度是按字节算的, 字符编码存每个字符所占的字节长度不一样。
        如“路飞学城” 用gbk存是2个字节一个字,用utf-8就是3个字节,因此以gbk打开时,seek(4) 就把光标切换到了“飞”和“学”两个字中间。
        但如果是utf8,seek(4)会导致,拿到了飞这个字的一部分字节,打印的话会报错,因为处理剩下的文本时发现用utf8处理不了了,因为编码对不上了。少了一个字节

    def seekable(self, *args, **kwargs): # real signature unknown
        判断文件是否可进行seek操作

    def tell(self, *args, **kwargs): # real signature unknown
        返回当前文件操作光标位置 

    def truncate(self, *args, **kwargs): # real signature unknown
        按指定长度截断文件
        *指定长度的话,就从文件开头开始截断指定长度,不指定长度的话,就从当前位置到文件尾部的内容全去掉。

    def writable(self, *args, **kwargs): # real signature unknown
        判断文件是否可写

 

flush 强制把内存刷到硬盘。

  flush() 方法是用来刷新缓冲区的,即将缓冲区中的数据立刻写入文件,同时清空缓冲区,不需要是被动的等待输出缓冲区写入,   正常情况下缓冲区满时,操作系统会自动将缓冲数据写入到文件中。

一般情况下,文件关闭后会自动刷新缓冲区,但有时你需要在关闭前刷新它,这时就可以使用 flush() 方法

   至于close方法,原理内部先调用flush方法来刷新缓冲区,再执行关闭操作,这样即使缓冲区数据未满也能保证数据的完整性。

f = open("flush_test.txt", 'w')
f.write('test')
f.write('\ntest2')
f.flush()
f.close()

readline()

f = open("contact.txt", 'r', encoding='utf-8')
print(f.readline())
print(f.readline())
print(f.readline())
f.readline()  #换行读 ,一行一行的读
print(f.readline())

 tell()返回当前文件操作光标位置;  seek()把操作文件的光标移到指定位置。

f = open("contact.txt", 'r', encoding='utf-8')
print(f.tell()) #0
f.seek(18)
print(f.readline())
print(f.tell())
print(f.readline())
print(f.readline())
f.readline()  #换行读 ,一行一行的读
print(f.readline())

seek(3)真的是字符吗?不是的,seek是找的是字节。  tell()是寻找光标的位置read是读的字符,tell和seek是找的字节。

utf-8中一个中文占3个字节,gbk中一个中文占2个字节。

>>>f = open('contact.txt',‘r’, encoding='gbk')   #文件里边:hello world! 
>>>f.seek(3)
3
>>>f.readline()
'lo world!'

 

#contact.txt:路飞
f = open("contact.txt", 'r', encoding='utf-8')  #utf-8是一个中文代表3个字节
print(f.tell()) #0
print(f.read(1)) #
print(f.tell()) #3
print(f.read(1)) #
f.seek(0)
print(f.read()) #路飞

 

 truncate()按指定长度截断文件 要以r+的模式打开   *指定长度的话,就从文件开头开始截断指定长度,不指定长度的话,就从当前位置到文件尾部的内容全去掉。

f = open("contact.txt", 'r+', encoding='utf-8')  #utf-8是一个中文代表3个字节print(f.tell()) #0
f.truncate() #从当前位置往后开始截断  文件变成: 空文件
f.close()

 

#contact.txt: 路飞学成-phone 
f = open("contact.txt", 'r+', encoding='utf-8') #utf-8是一个中文代表3个字节 print(f.tell()) #0 f.seek(3) f.truncate() #光标移动到第3个字节后,从当前位置往后开始截断 文件变成:路 f.close()

 

#contact.txt: 路飞学成-phone
f = open("contact.txt", 'r+', encoding='utf-8') #utf-8是一个中文代表3个字节 print(f.tell()) #0 f.seek(3) f.truncate(6) #加上数字就是从头的第6个字节开始往后截取所有的。 文件变成:路飞 f.close()

 

文件修改

(两种方式:要么占内存要么占硬盘)

 把光标seek到中间的位置就可以了。

占硬盘修改,打开旧文件一行行的读,新文件一行行的写。  最后把新文件重命名为旧文件,就把它覆盖掉了。命名要 import os

import os
f_name = "contact.txt" f_new_name = "%s.new" %f_name old_str = "乔一菲" new_str = "红娘" f = open(f_name,"r",encoding="utf-8") f_new = open(f_new_name,"w",encoding="utf-8") for line in f: if old_str in line: line = line.replace(old_str,new_str) f_new.write(line) f.close() f_new.close() os.rename(f_new_name,f_name) #把新文件名字改成原文件 的名字,就把之前的覆盖掉了

占内存方式:以r+模式打开文件全read在内存,直接.replace,全程读一遍然后再seek到文件开头,再写进去就覆盖掉了。不一定能全部覆盖,怎么办:先把文件truncate完,flush

#占内存方式修改 
f = open('contact.txt','r+',encoding='utf-8')
f1 = f.readlines()
f.seek(0)  #这个一定要加
old_str = "乔亦菲"
new_str = "Yifei Qiao"
for line in f1:
    if old_str in line:
        line = line.replace(old_str,new_str)
    f.write(line)
#f.truncate()
#f.flush()
f.close()


#################
f = open(r"info.txt", "r+")
l = f.readlines()
f.seek(0)  # 指针指向文件开头
old_str = "唐三"
new_str = ""
for line in l[0::2]:  # 只取部分字符
     if old_str in line:
         line = line.replace(old_str,new_str)
     f.write(line+"\n")
f.truncate()
# 对文件进行截取,如果文件之前为100M,
# 修改后文件只覆盖了50M,如果不使用truncate不会整体覆盖,只会部分覆盖,后面剩余的50M内容会追加新文件中,修改后的文件大小不变
# 使用truncate后,文件就只有50M,他会把剩余的50M内容从当前write的指针后面进行截取
f.close()

对文件的删除

如:删除最后一行

with open('三体语录','r',encoding='utf-8') as f:
    f1 = f.readlines()  ##readline 和readlines的区别
    with open('三体语录','w',encoding='utf-8')as f_w:
        for line in f1:
            if '25' in line:
                continue  #跳出本次循环
            f_w.write(line)

 

posted @ 2018-03-20 13:48  kris12  阅读(703)  评论(0编辑  收藏  举报
levels of contents