12.文件和输入输出

 

 

一、文件对象

1、文件对象不仅可以用来访问普通的磁盘文件, 而且也可以访问任何其它类型抽象层面上的"文件".

2、一旦设置了合适的"钩子", 你就可以访问具有文件类型接口的其它对象, 就好像访问的是普通文件一样.

3、内建函数 open() 返回一个文件对象 ,对该文件进行后继相关的操作都要用到它

4、文件只是连续的字节序列
5、数据的传输经常会用到字节流, 无论字节流是由单个字节还是大块数据组成

 

二、文件内建函数:open()和file()

 

 

打开文件

open()和file()都可以操作文件,打开文件后时候会返回一个文件对象,否则引发一个错误;当操作失败, Python 会产生一个 IOError 异常

1、open() 和 file() 函数具有相同的功能, 可以任意替换
2、您所看到任何使用 open() 的地方, 都可以使用 file() 替换它
3、建议使用 open() 来读写文件

4、 基本语法:file_object = open(file_name, access_mode='r', buffering=-1)

file_name 是包含要打开的文件名字的字符串, 它可以是相对路径或者绝对路径

可选变量access_mode 也是一个字符串, 代表文件打开的模式
使用 'r' 或 'U' 模式打开的文件必须是已经存在的
如果没有给定 access_mode , 它将自动采用默认值 'r' .
另外一个可选参数 buffering 用于指示访问文件所采用的缓冲方式 ;其中 0 表示不缓冲, 1表示只缓冲一行数据, 任何其它大于 1 的值代表使用给定值作为缓冲区大小
不提供该参数或者给定负值代表使用系统默认缓冲机制, 既对任何类电报机( tty )设备使用行缓冲, 其它设备使用正常缓冲

 


注意点:

 

1.python解释器打开文件时,是对硬盘进行操作,需要内核态才可以操作硬盘,故此时python解释器是调用操作系统的文件读取接口。
windows中文版本默认使用GBK编码表,linux默认使用utf-8,所有如果操作的文件在windows下,非GBK编码的,需要在open函数中声明编码类型,
使操作系统运用相应的编码规则进行解码读取,防止串码,乱码现象。

2.open主要有三种模式,读(r),写(w),追加(a),其中,默认为读模式

 

关闭文件

 

关闭文件有两组方式:

1.使用f.close()  ,f为open返回的句柄赋值的变量名。

2.程序结束后,自动关闭。第一个方法容易造成文件写操作时,数据的丢失。原因是写数据时,数据会先保存在内存中,文件关闭时才会写入硬盘,此时如果文件未关闭,
软件因为异常崩溃,导致内存中的数据丢失,且未写入硬盘中。作为第一种关闭方法的优化,是使用:with open('filename') as f 。
with会创建一个程序块,将文件操作置于with程序块下,这样with控制块结束,文件也会自动关闭。

 

语法如下:

with open('f1.txt') as f1 , open('f2.txt') as f2:
    ......

 

 

 文件对象访问模式

 

file对象有自己的属性和方法。先来看看file的属性。(+和b可以和其他的字符组合成mode,例如rb以二进制只读方式打开,mode参数是可选的,如果没有默认为r)

(注意:文件打开之后,应当被及时关闭,可以查看f.closed属性以确认文件是否被关闭)

 

mode

function

r

只读模式(默认,文件不存在,则发生异常)文件的指针将会放在文件的开头

w

只写模式(不可读,文件不存在则创建,存在则删除内容,再打开文件)

a

追加模式(只能写,文件不存在则创建,存在则追加内容)

r+

可读写模式(可读,可写,可追加),如果文件存在,则覆盖当前文件指针所在位置的字符,如原来文件内容是"Hello,World",打开文件后写入"hi"则文件内容会变成"hillo, World"

b

以二进制方式打开(如:FTP发送上传ISO镜像文件,linux可忽略,windows处理二进制文件时需标注)

w+

先写再读(可读,可写,可追加) 如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。

a+

同a(可读可写,文件不存在则创建,存在则追加内容)。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。

rb

以二进制读方式打开,只能读文件 , 如果文件不存在,会发生异常

wb

以二进制写方式打开,只能写文件, 如果文件不存在,创建该文件

ab

二进制追写文件。 从文件顶部读取内容 从文件底部添加内容 不存在则创建

rt

以文本读方式打开,只能读文件 , 如果文件不存在,会发生异常

wt

以文本写方式打开,只能读文件 , 如果文件不存在,创建该文件。如果文件存在。先清空,再打开文件

at

以文本读写方式打开,只能读文件 , 如果文件不存在,创建该文件。如果文件存在。先清空,再打开文件

rb+

以二进制读方式打开,可以读、写文件 , 如果文件不存在,会发生异常  

wb+

以二进制写方式打开,可以读、写文件, 如果文件不存在,创建该文件.如果文件存在。先清空,再打开文件

ab+

追读写二进制。从文件顶部读取内容 从文件底部添加内容 不存在则创建

 

 注:以b方式打开时,读取到的内容是字节类型,写入时也需要提供字节类型

 

 

通用换行符支持(UNS)

1、不同平台用来表示行结束的符号是不同的
2、当你使用 'U' 标志打开文件的时候, 所有的行分割符 通过 Python 的输入方法返回时都会被替换为换行符 NEWLINE(\n)
3、UNS 只用于读取文本文件

 

三、文件内建方法

 

 

>>> help(open)
Help on built-in function open in module __builtin__:

open(...)
    open(name[, mode[, buffering]]) -> file object
    
    Open a file using the file() type, returns a file object.  This is the
    preferred way to open a file.  See file.__doc__ for further information.

>>> help(file)
Help on class file in module __builtin__:

class file(object)
 |  file(name[, mode[, buffering]]) -> file object
 |  
 |  Open a file.  The mode can be 'r', 'w' or 'a' for reading (default),
 |  writing or appending.  The file will be created if it doesn't exist
 |  when opened for writing or appending; it will be truncated when
 |  opened for writing.  Add a 'b' to the mode for binary files.
 |  Add a '+' to the mode to allow simultaneous reading and writing.
 |  If the buffering argument is given, 0 means unbuffered, 1 means line
 |  buffered, and larger numbers specify the buffer size.  The preferred way
 |  to open a file is with the builtin open() function.
 |  Add a 'U' to mode to open the file for input with universal newline
 |  support.  Any line ending in the input file will be seen as a '\n'
 |  in Python.  Also, a file so opened gains the attribute 'newlines';
 |  the value for this attribute is one of None (no newline read yet),
 |  '\r', '\n', '\r\n' or a tuple containing all the newline types seen.
 |  
 |  'U' cannot be combined with 'w' or '+' mode.
 |  
 |  Methods defined here:
 |  
 |  __delattr__(...)
 |      x.__delattr__('name') <==> del x.name
 |  
 |  __enter__(...)
 |      __enter__() -> self.
 |  
 |  __exit__(...)
 |      __exit__(*excinfo) -> None.  Closes the file.
 |  
 |  __getattribute__(...)
 |      x.__getattribute__('name') <==> x.name
 |  
 |  __init__(...)
 |      x.__init__(...) initializes x; see help(type(x)) for signature
 |  
 |  __iter__(...)
 |      x.__iter__() <==> iter(x)
 |  
 |  __repr__(...)
 |      x.__repr__() <==> repr(x)
 |  
 |  __setattr__(...)
 |      x.__setattr__('name', value) <==> x.name = value
 |  
 |  close(...)
 |      close() -> None or (perhaps) an integer.  Close the file.
 |      
 |      Sets data attribute .closed to True.  A closed file cannot be used for
 |      further I/O operations.  close() may be called more than once without
 |      error.  Some kinds of file objects (for example, opened by popen())
 |      may return an exit status upon closing.
 |  
 |  fileno(...)
 |      fileno() -> integer "file descriptor".
 |      
 |      This is needed for lower-level file interfaces, such os.read().
 |  
 |  flush(...)
 |      flush() -> None.  Flush the internal I/O buffer.
 |  
 |  isatty(...)
 |      isatty() -> true or false.  True if the file is connected to a tty device.
 |  
 |  next(...)
 |      x.next() -> the next value, or raise StopIteration
 |  
 |  read(...)
 |      read([size]) -> read at most size bytes, returned as a string.
 |      
 |      If the size argument is negative or omitted, read until EOF is reached.
 |      Notice that when in non-blocking mode, less data than what was requested
 |      may be returned, even if no size parameter was given.
 |  
 |  readinto(...)
 |      readinto() -> Undocumented.  Don't use this; it may go away.
 |  
 |  readline(...)
 |      readline([size]) -> next line from the file, as a string.
 |      
 |      Retain newline.  A non-negative size argument limits the maximum
 |      number of bytes to return (an incomplete line may be returned then).
 |      Return an empty string at EOF.
 |  
 |  readlines(...)
 |      readlines([size]) -> list of strings, each a line from the file.
 |      
 |      Call readline() repeatedly and return a list of the lines so read.
 |      The optional size argument, if given, is an approximate bound on the
 |      total number of bytes in the lines returned.
 |  
 |  seek(...)
 |      seek(offset[, whence]) -> None.  Move to new file position.
 |      
 |      Argument offset is a byte count.  Optional argument whence defaults to
 |      0 (offset from start of file, offset should be >= 0); other values are 1
 |      (move relative to current position, positive or negative), and 2 (move
 |      relative to end of file, usually negative, although many platforms allow
 |      seeking beyond the end of a file).  If the file is opened in text mode,
 |      only offsets returned by tell() are legal.  Use of other offsets causes
 |      undefined behavior.
 |      Note that not all file objects are seekable.
 |  
 |  tell(...)
 |      tell() -> current file position, an integer (may be a long integer).
 |  
 |  truncate(...)
 |      truncate([size]) -> None.  Truncate the file to at most size bytes.
 |      
 |      Size defaults to the current file position, as returned by tell().
 |  
 |  write(...)
 |      write(str) -> None.  Write string str to file.
 |      
 |      Note that due to buffering, flush() or close() may be needed before
 |      the file on disk reflects the data written.
 |  
 |  writelines(...)
 |      writelines(sequence_of_strings) -> None.  Write the strings to the file.
 |      
 |      Note that newlines are not added.  The sequence can be any iterable object
 |      producing strings. This is equivalent to calling write() for each string.
 |  
 |  xreadlines(...)
 |      xreadlines() -> returns self.
 |      
 |      For backward compatibility. File objects now include the performance
 |      optimizations previously implemented in the xreadlines module.
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  closed
 |      True if the file is closed
 |  
 |  encoding
 |      file encoding
 |  
 |  errors
 |      Unicode error handler
 |  
 |  mode
 |      file mode ('r', 'U', 'w', 'a', possibly with 'b' or '+' added)
 |  
 |  name
 |      file name
 |  
 |  newlines
 |      end-of-line convention used in this file
 |  
 |  softspace
 |      flag indicating that a space needs to be printed; used by print
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |  
 |  __new__ = <built-in method __new__ of type object>
 |      T.__new__(S, ...) -> a new object with type S, a subtype of T
(END)

 

输入

 

[root@host-192-168-3-6 ~]# cat b.py 
#!/usr/bin/env python 
import sys 
print sys.argv

 

 read()方法

1.read() 方法用来直接读取字节到字符串中, 最多读取给定数目个字节
2.如果没有给定size参数(默认值为-1)或者size值为负,文件将被读取直至末尾

3.未来的某个版本可能会删除此方法

4.如果使用读了多次,那么后面读取的数据是从上次读完后的位置开始的

5.语法:read([size])

>>> fobj = open('b.py')
>>> data  = fobj.read()
>>> print data
#!/usr/bin/env python
import sys
print sys.argv

 注:只有当文件有读权限时,才可以操作这个函数

 

两次读取文件内容

读取内容:yesterday

somehow, it seems the love I knew was always the most destructive kind
不知为何,我经历的爱情总是最具毁灭性的的那种
Yesterday when I was young
昨日当我年少轻狂

 

代码:

#!/usr/bin/env python
#-*- coding:utf-8 -*-

f = open("yesterday",'r')
#第1次读取
data = f.read()
#第2次读取
data2 = f.read()
print data
print "----------------data2-----%s----"%(data2)
f.close()

 

执行结果:

somehow, it seems the love I knew was always the most destructive kind
不知为何,我经历的爱情总是最具毁灭性的的那种
Yesterday when I was young
昨日当我年少轻狂
----------------data2---------

 

这边不经意间有一个小疑问:为什么data2的数据为空?

因为在文件中 ,维护一个类似文件指针的一个东西,这个文件指针类似于我们平时操作文件时的光标的东西,所以当第1次读文件时,文件指针已经指向最后一个位置,所以第2次再去读取的时候,是从最后一个位置开始读取的,所以读取的为空。

那怎么再重新读取数据呐?把光标移动到开始位即可。

 

 readline方法

1.读取打开文件的一行(读取下个行结束符之前的所有字节),包括行结束符,作为字符串返回
2.它也有一个可选的size参数,默认为-1,代表读至行结束符
3.如果提供了该参数,那么在超过size个字节后会返回不完整的行

4.语法:readline([size])

>>> fobj = open('b.py')
>>> data  = fobj.readline()
>>> print data
#!/usr/bin/env python

 

readlines方法

1.readlines()方法读取所有(剩余的)行然后把它们作为一个字符串列表返回

2.它的可选参数 sizhint 代表返回的最大字节大小
3.如果它大于 0 , 那么返回的所有行应该大约有 sizhint 字节(可能稍微大于这个数字, 因为需要凑齐缓冲区大小).

4.语法:readlines([size])

>>> fobj = open('b.py')
>>> data = fobj.readlines()
>>> print data
['#!/usr/bin/env python \n', 'import sys \n', 'print sys.argv\n']

 

 例如:yesterday

 

Somehow, it seems the love I knew was always the most destructive kind
不知为何,我经历的爱情总是最具毁灭性的的那种
Yesterday when I was young
昨日当我年少轻狂

 

代码:

#!/usr/bin/env python
#-*- coding:utf-8 -*-
import io
f = io.open("yesterday",encoding="utf-8")
print str(f.readlines()).decode("unicode_escape")
f.close()

 

 python 2.7版本解决TypeError: 'encoding' is an invalid keyword argument for this function

使用io模块

 

 

或者:python3

f = open("yesterday2","r",encoding="utf-8")
print(f.readlines())
f.close()

 

执行结果:

[u'somehow, it seems the love I knew was always the most destructive kind
', u'不知为何,我经历的爱情总是最具毁灭性的的那种
', u'Yesterday when I was young
', u'昨日当我年少轻狂']

 

 

  我们正常循环读取文件中的每一行,如下:

#!/usr/bin/env python
#-*- coding:utf-8 -*-
import io
f = io.open("yesterday",encoding="utf-8")
for index,line in enumerate(f.readlines()):
    #当下标值为2时,不打印
    if index == 2:
        print "--------data--------"
        continue
    print line.strip()
f.close()

 

 

执行结果:

somehow, it seems the love I knew was always the most destructive kind
不知为何,我经历的爱情总是最具毁灭性的的那种
--------data--------
昨日当我年少轻狂

 

这种方法已经达到我们的目的了,可以顺利的读取每一行,但是,当我们遇到2G,20G,甚至200G的文件时,你这样读取会导致内存不够用,会使程序变的很慢,因为你的内存只有几个G,你把几十个G的数据放到内存,内存肯定是受不了的,所以这种方法只适合小文件,不适合大文件。那怎么办呢?我们于是就有下面这种方法,如下:

#!/usr/bin/env python
#-*- coding:utf-8 -*-
import io
f = io.open("yesterday",encoding="utf-8")
#计数器
count = 0
#f文件变成迭代器
for line in f:
    if count == 2:
        print "------data-----"
        count += 1
        continue
    print line.strip() 
    count += 1
f.close()

 

执行结果:

somehow, it seems the love I knew was always the most destructive kind
不知为何,我经历的爱情总是最具毁灭性的的那种
------data-----
昨日当我年少轻狂

 

 

 

以上这种写法的好处在于,读取文件时,是一行一行的读取,而且,读取一行删除一行,内存中只保留一行。原因:f文件变成迭代器,它已经不再是一个列表的形式了,不能通过下标值来获取,需要一个计数器来计数。

 

 

输出

 

write()

1. write()内建方法功能与read()和readline()相反。它把含有文本数据或二进制数据块的字符串写入到文件中去

2.写入文件时,不会自动添加行结束标志,需要程序员手工输入

>>> fobj.write('Hello World!\n')

  注:写功能只有当打开文件模式是写(w)或者追加(a)才可操作。

 

writelines方法

1.和readlines()一样,writelines()方法是针对列表的操作
2.它接受一个字符串列表作为参数,将它们写入文件
3.行结束符并不会被自动加入,所以如果需要的话,必须在调用writelines()前给每行结尾加上行结束符

 

>>> fobj.writelines(['Hello World!\n', 'python programing\n'])

 

核心笔记:保留行分隔符
1、当使用输入方法如 read() 或者 readlines() 从文件中读取行时, Python 并不会删除行结束符

例如这样的代码在 Python 程序中很常见 :

f = open('myFile', 'r')
data = [line.strip() for line in f.readlines()]
f.close()

2、类似地, 输出方法 write() 或 writelines() 也不会自动加入行结束符. 你应该在向文件写入数据前自己完成

 

 

文件的指针定位与查询

文件指针

 文件被打开后,其对象保存在 f 中, 它会记住文件的当前位置,以便于执行读、写操作,这个位置称为文件的指针( 一个从文件头部开始计算的字节数 long 类型 )。

以"r"   "r+"   "rb+" 读方式, "w"   "w+"   "wb+"写方式 打开的文件,一开始,文件指针均指向文件的头部。

 

 

seek(offset[, whence])

1.在文件中移动文件指针到不同的位置

2.whence的值,0表示文件开头,1表示当前位置,2表示文件的结尾

  如果whence被设为0,这意味着将文件的开头作为移动字节的参考位置。

  如果设为1,则使用当前的位置作为参考位置。

  如果它被设为2,那么该文件的末尾将作为参考位置。

 

3.offset -- ,相对于某个位置的偏移量偏移量,也就是代表需要移动偏移的字节数,注意是按照字节算的,字符编码存每个字符所占的字节长度不一样。

    如“好好学习” 用gbk存是2个字节一个字,用utf-8就是3个字节,因此以gbk打开时,seek(4) 就把光标切换到了“飞”和“学”两个字中间。
    但如果是utf8,seek(4)会导致,拿到了飞这个字的一部分字节,打印的话会报错,因为处理剩下的文本时发现用utf8处理不了了,因为编码对不上了。少了一个字节。

 

例子1:把位置设置为:从文件开头,偏移5个字节

 

yesterday内容:

abcdefghkh13799999999110120119111
123456
2435234
3456
2132435
2345T34542311

 

代码:

#!/usr/bin/env python
#-*- coding:utf-8 -*-
# 打开一个已经存在的文件
f = open("yesterday", "r")
str = f.read(30)
print "读取的数据是 : ", str

# 查找当前位置
position = f.tell()
print "当前文件位置 : ", position

# 重新设置位置
f.seek(5,0)
# 查找当前位置
position = f.tell()
print "当前文件位置 : ", position

f.close()

 

执行结果:

 

读取的数据是 :  abcdefghkh13799999999110120119
当前文件位置 :  30
当前文件位置 :  5

 

例子2:离文件末尾,3字节处

#!/usr/bin/env python
#-*- coding:utf-8 -*-


# 打开一个已经存在的文件
f = open("yesterday", "r")

# 查找当前位置
position = f.tell()
print "当前文件位置 : ", position

# 重新设置位置
f.seek(-3,2)

# 读取到的数据为:文件最后3个字节数据
str = f.read()
print "读取的数据是 : ", str

 

执行结果:

当前文件位置 :  0
读取的数据是 :  311

 

 


tell():返回当前文件指针的位置

 

代码:

#!/usr/bin/env python
#coding:utf-8
# 打开一个已经存在的文件
f = open("yesterday", "r")
str = f.read(3)
print "读取的数据是 : ", str

# 查找当前位置
position = f.tell()
print "当前文件位置 : ", position

str = f.read(3)
print "读取的数据是 : ", str

# 查找当前位置
position = f.tell()
print "当前文件位置 : ", position

 

执行结果:

读取的数据是 :  abc
当前文件位置 :  3
读取的数据是 :  def
当前文件位置 :  6

 

 

 

 

文件迭代

1.如果需要逐行处理文件,可以结合for循环迭代文件
2.迭代文件的方法与处理其他序列类型的数据类似

yesterday内容

Somehow, it seems the love I knew was always the most destructive kind
不知为何,我经历的爱情总是最具毁灭性的的那种
Yesterday when I was young
昨日当我年少轻狂

 

代码:

#!/usr/bin/env python
#coding:utf-8

f = open('yesterday')
for i in f:
    print i.strip()

 

执行结果:

Somehow, it seems the love I knew was always the most destructive kind
不知为何,我经历的爱情总是最具毁灭性的的那种
Yesterday when I was young
昨日当我年少轻狂

 

 

其他方法

 

1、close() 通过关闭文件来结束对它的访问 ,Python 垃圾收集机制也会在文件对象的引用计数降至零的时候自动关闭文件 ;这在文件只有一个引用时发生


2、在重新赋另个文件对象前关闭这个文件. 如果你不显式地关闭文件, 那么你可能丢失输出缓冲区的数据.


3、fileno() 方法返回打开文件的描述符 ,这是一个整数, 可以用在如 os 模块( os.read() )的一些底层操作上


4、调用 flush() 方法会直接把内部缓冲区中的数据立刻写入文件, 而不是被动地等待输出缓冲区被写入


5、isatty() 是一个布尔内建函数, 当文件是一个类 tty 设备时返回 True ,否则返回False


6、truncate() 方法将文件截取到当前文件指针位置或者到给定 size , 以字节为单位 ;
  注意:仅当以 "r+" "rb+" "w" "wb" "wb+"等以可写模式打开的文件才可以执行该功能

 

 

行分隔符和其它文件系统的差异

1、操作系统间的差异之一是它们所支持的行分隔符不同

2、在 Unix 系列或 Mac OS X系统上, 行分隔符是 换行符\n ;

3、旧的MacOS下是RETURN ( \r ) , 而 DOS 和Wind32 系统下结合使用了 \r\n
4、另一个不同是路径分隔符,POSIX 使用 "/", DOS 和 Windows 使用 "\", 旧版本的 MacOS 使用":"。 它用来分隔文件路径名, 标记当前目录和父目录
5、用os模块可以解决这些问题:不管你使用的是什么平台, 只要你导入了 os 模块, 这些变量自动会被设置为正确的值, 减少了你的麻烦

 

os模块属性 描述
linesep 用于在文件中分隔行的字符串
sep 用来分隔文件路径名的字符串
pathsep pathsep
curdir 当前工作目录的字符串名称
pardir (当前工作目录的)父目录字符串名称

 

 

注意:

1、print 语句默认在输出内容末尾后加一个换行符, 而在语句后加一个逗号就可以避免这个行为
2、readline() 和 readlines() 函数不对行里的空白字符做任何处理,所以你有必要加上逗号
3、如果你省略逗号, 那么显示出的文本每行后会有两个换行符, 其中一个是输入是附带的, 另个是 print 语句自动添加的

文件修改

  

 

硬盘的存储原理就是,当你把文件存到硬盘上,就在硬盘上划了一块空间,存数据,等你下次打开这个文件,会seek到一个位置,每改一个字,就是把原来的覆盖掉,如果要插入,是不可能的,因为后面的数据在硬盘上不会整体向后移动,所以就会出现当前这个情况,你要插入,却变成把旧内容覆盖掉。

 

所以修改文件,就不要在硬盘上修改,把内容全部读到内存里,数据在内存里可以随便增删改查,修改之后,把内容全部写回硬盘,把原来的数据全部覆盖掉。

 

当然了,如果有些文件特别大,比如5G,那么一下吃掉这么大内存,非常浪费资源,所以更好的方法就是,如果不像占内存,那么就边读边写,也就是不修改源文件,但是可以打开丢文件的同时,生成一个新文件,边从旧的里面读,边往新的里面写,遇到需要修改的就改了再写道新文件,这样在内存里面一直只存一行内容,就不占内存了,但是也有一个缺点就是,虽然不占内存,但是占硬盘,每次修改,都要生成一份新文件,虽然改完后,可以把旧的覆盖掉,但是在改的过程中,还是有两份数据。

 

 

3.6.1文件修改占硬盘

 

yesterday内容:

123435
125343432
3432432434

 

代码:

 

 

#!/usr/bin/env python
#coding:utf-8
import io
f_name = 'yesterday'
f_new_name = 'yesterday2'
old_str = '123'
new_str = '123123123'

f = io.open(f_name, 'r', encoding='utf-8')
f_new = io.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()
print old_str
print new_str

 

执行结果:生成一个新文件

123123123435
125343432
3432432434

 

 

 

上面的代码会生成一个修改后的新文件,源文件不动,若想覆盖源文件

代码如下:

import os
os.replace(file_new_name,file_name)

 

3.6.2文件修改占内存

  这样不需要占用两个文件,只需要写一个文件,直接r+就可以,但是会出现一个问题,就是要是以前文件内容多,现在少了,那会出错,为了避免这种错误的发生,使用truncate()就ok

 userinfo.txt的内容:

1:www.baidu.com
2:www.google.com
3:www.tencent.com
4:www.tianmao.com
5:www.jingdong.com

 

例子:

f.truncate([size])
把文件裁成规定的大小,默认的是裁到当前文件操作标记的位置。
如果size比文件的大小还要大,依据系统的不同可能是不改变文件,
也可能是用0把文件补到相应的大小,也可能是以一些随机的内容加上去。

    
file = open('userinfo.txt','r+',encoding='utf-8')
print('文件名为:',file.name)
 
line =file.readline()
print('读取第一行:%s' %line)
 
#截断剩下的字符串
file.truncate()
 
#尝试再次读取数据
line =file.readline()
print('读取数据:%s' %line)
 
file.close()
 
# 结果:
# 文件名为: userinfo.txt
# 读取第一行:1:www.baidu.com
#
# 读取数据:2:www.google.com

 

例子2:

file = open('userinfo.txt','r+',encoding='utf-8')
print('文件名为:',file.name)
 
line =file.readline()
print('读取第一行:%s' %line)
 
#截断10的字符串
file.truncate(10)
 
#尝试再次读取数据
line =file.readline()
print('读取数据:%s' %line)
 
file.close()
 
结果:
文件名为: userinfo.txt
读取第一行:1:www.baid
读取数据:

 

 

文件处理习题

 

举例如下:

f = open(file=‘userinfo.txt',mode='r',encoding='utf-8')
 
data = f.read()
 
f.close()

 

上述操作语法解释如下:

file=‘userinfo.txt'  表示文件路径
mode='r'                       表示只读(可以修改为其他)
encoding='utf-8'               表示将硬盘上的 0101010 按照utf-8的规则去“断句”,
                    再将“断句”后的每一段0101010转换成unicode的 01010101,unicode对照表中有01010101和字符的对应关系。
f.read()                                          表示读取所有内容,内容是已经转换完毕的字符串。
f.close()                                         表示关闭文件

 

再看一个例子:

f = open(file='userinfo.txt',mode='rb')
data = f.read()
f.close()

上述操作语法解释:

file='userinfo.txt'      表示文件路径
mode='rb'                表示只读(可以修改为其他)
f.read()                 表示读取所有内容,内容是硬盘上原来以某种编码保存010101010,即:某种编码格式的字节类型
f.close()                表示关闭文件

问:两个例子的区别在哪?

答:在于示例2打开文件时并未指定encoding,这是为何?是因为直接以rb模式打开了文件 ,rb是指二进制模式,数据读到内存里直接是bytes格式,如果想内容,还需要手动decode,因此在文件打开阶段,不需要指定编码

 

问:假设你不知道你要处理的文件是什么编码可怎么办?

 

yesterday内容:

 

 

#!/usr/bin/env python
#coding:utf-8

import chardet
result = chardet.detect(open('yesterday').read())
print result

 

执行结果:

{'confidence': 1.0, 'language': '', 'encoding': 'ascii'}

 

或:

 

import chardet
 
f = open('log',mode='rb')
data = f.read()
f.close()
 
result = chardet.detect(open('log',mode='rb').read())
print(result)

 

输出:

{'encoding': 'GB2312', 'confidence': 0.99, 'language': 'Chinese'}

 

注意:

 

    文件操作时,以 “r”或“rb” 模式打开,则只能读,无法写入;
    硬盘上保存的文件都是某种编码的0101010,打开时需要注意:
        rb,直接读取文件保存时原生的0101010,在Python中用字节类型表示
        r和encoding,读取硬盘的0101010,并按照encoding指定的编码格式进行断句,再将“断句”后的每一段0101010转换成unicode的 010101010101,
     在Python中用字符串类型表示

 

文件操作的其他功能

 

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
       判断文件是否可写

 

 

fileno()

返回文件句柄在内存中的编号

 

f = open("yesterday",'r',encoding="utf-8")
print(f.fileno())
f.close()
#输出
3

 

seekable()

 不是所有的文件都可以移动光标,比如tty文件,可以移动的,返回True

f = open("yesterday2",'r',encoding="utf-8")
print(f.seekable())
f.close()
#输出
True

 

readable()

文件是否可读

f = open("yesterday",'r',encoding="utf-8")
print(f.readable())
f.close()
#输出
True

 

writeable()

文件是否可写

f = open("yesterday",'r',encoding="utf-8")
print(f.writable())
f.close()
#输出
False #文件不可写

 

flush()

写数据时,写的数据不想存内存中缓存中,而是直接存到磁盘上,需要强制刷新

>>> f = open("yesterday2","w",encoding="utf-8")
#这时'hello word'在缓存中
>>> f.write("hello word")
10
#强刷到磁盘上
>>> f.flush()

 

 这个怎么实验呢?在cmd命令行中,cd到你文件所在的路径下,然后输入python,在Python命令行中输入上面代码

①cd d:\PycharmProjects\pyhomework\day3下(因为我的被测文件在这个文件夹下)

②在这个目录下输入Python命令行,然后进行测试

③强制刷新之前

 ④执行强刷命令之后

 

⑤强刷后文件中的内容变化

 

注:以写的模式打开文件,写完一行,默认它是写到硬盘上去的,但是其实它不一定写到硬盘上去了。当你刚写完一行,如果此时断电,有可能,你这行就没有写进去,因为这一样还在内存的缓存中(内存中的缓存机制),所以你有不想存缓存,所以就要强制刷新。那一般在什么情况下用呐?比如:存钱

 

truncate(截取字符的数)

截取文件中的字符串,打开文件模式一定是追加模式(a),不能是写(w)和读(r)模式

#没有指针
f = open("yesterday2","a",encoding="utf-8")
f.truncate(10)
f.close()
#截取结果
Somehow, i
 
#有指针
f = open("yesterday2","a",encoding="utf-8")
f.seek(5)
f.truncate(10)
f.close()
#截取结果
Somehow, i

 说明truncate截取文件中的字段,并不受指针(seek)所在位置影响。

 

四、文件内建属性

1、文件对象除了方法之外,还有一些数据属性. 这些属性保存了文件对象相关的附加数据

文件对象的属性 描述
file.closed True 表示文件已经被关闭, 否则为 False
file.encoding 文件所使用的编码 - 当 Unicode 字符串被写入数据时, 它们将自动使
用 file.encoding 转换为字节字符串; 若 file.encoding 为 None 时使
用系统默认编码
file.mode 文件打开时使用的访问模式
file.name 文件名
file.newlines 未读取到行分隔符时为 None , 只有一种行分隔符时为一个字符串, 当
文件有多种类型的行结束符时,则为一个包含所有当前所遇到的行结束
符的列表
file.softspace 为 0 表示在输出一数据后,要加上一个空格符,1 表示不加。这个属性一般程序员用不着,由程序内部使用

 

 打印文件的编码【encoding】

f = open("yesterday2",'r',encoding="utf-8")
print(f.encoding)
f.close()
#输出
utf-8

 

name

返回文件名

f = open("yesterday2",'r',encoding="utf-8")
print(f.name)
f.close()
#输出
yesterday2

 

isatty()

判断是否是一个终端设备(比如:打印机之类的)

f = open("yesterday2",'r',encoding="utf-8")
print(f.isatty())
f.close()
#输出
False #表示不是一个终端设备

 

closed

判断文件是否关闭

f = open("yesterday2","r",encoding="utf-8")
f.read()
print(f.closed)
#输出
False

 

 

 

 

五、标准文件

 

程序一执行,就可以访问三个标准文件
1、标准输入:一般是键盘,使用sys.stdin

2、标准输出:一般是显示器缓冲输出,使用sys.stdout

3、标准错误:一般是显示器的非缓冲输出,使用sys.stderr

 4、Python 中可以通过 sys 模块来访问这些文件的句柄. 导入 sys 模块以后, 就可以使用sys.stdin , sys.stdout 和 sys.stderr 访问

5、print 语句通常是输出到 sys.stdout ; 而内建raw_input() 则通常从 sys.stdin 接受输入

>>> import sys
>>> sys.stdout.write('hello world!\n')
hello world!
>>> hi = sys.stdin.readline()
hello
>>> hi
'hello\n

 

四,补充内容

4.1 各种系统操作

  注意:虽然python中提供了各种拼接目录的函数,但是,函数并不能保证字符编码不出问题,很大可能导致程序错误。所以最好还是自己拼接。

   python中对文件、文件夹(文件操作函数)的操作需要涉及到os模块和shutil模块。

得到当前工作目录,即当前Python脚本工作的目录路径: os.getcwd()
 
返回指定目录下的所有文件和目录名:os.listdir()
 
函数用来删除一个文件:os.remove()
 
删除多个目录:os.removedirs(r“c:\python”)
 
检验给出的路径是否是一个文件:os.path.isfile()
 
检验给出的路径是否是一个目录:os.path.isdir()
 
判断是否是绝对路径:os.path.isabs()
 
检查是否快捷方式os.path.islink ( filename )
 
检验给出的路径是否真地存:os.path.exists()
 
返回一个路径的目录名和文件名:os.path.split() eg os.path.split('/home/swaroop/byte/code/poem.txt') 结果:('/home/swaroop/byte/code', 'poem.txt')
 
分离扩展名:os.path.splitext()
 
获取路径名:os.path.dirname()
 
获取文件名:os.path.basename()
 
运行shell命令: os.system()
 
读取和设置环境变量:os.getenv() 与os.putenv()
 
给出当前平台使用的行终止符:os.linesep Windows使用'\r\n',Linux使用'\n'而Mac使用'\r'
指示你正在使用的平台:os.name 对于Windows,它是'nt',而对于Linux/Unix用户,它是'posix'
重命名:os.rename(old, new)
 
创建多级目录:os.makedirs(r“c:\python\test”)
 
创建单个目录:os.mkdir(“test”)
 
获取文件属性:os.stat(file)
 
修改文件权限与时间戳:os.chmod(file)
 
终止当前进程:os.exit()
 
获取文件大小:os.path.getsize(filename)

 

4.2 各种目录操作

 

os.mkdir("file") 创建目录
 
复制文件:
shutil.copyfile("oldfile","newfile") oldfile和newfile都只能是文件
shutil.copy("oldfile","newfile") oldfile只能是文件夹,newfile可以是文件,也可以是目标目录
 
复制文件夹:
shutil.copytree("olddir","newdir") olddir和newdir都只能是目录,且newdir必须不存在
 
重命名文件(目录)
os.rename("oldname","newname") 文件或目录都是使用这条命令
 
移动文件(目录)
shutil.move("oldpos","newpos")
 
删除文件
os.remove("file")
 
删除目录
os.rmdir("dir")只能删除空目录
shutil.rmtree("dir") 空目录、有内容的目录都可以删
 
转换目录
os.chdir("path") 换路径
  
ps: 文件操作时,常常配合正则表达式:
img_dir = img_dir.replace('\\','/')

 

五,文件处理习题

 

 

 5.2文件操作的其他功能

 

 

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
       判断文件是否可写

 

5.4 例题:读取一个文件下的所有文件

 

#!/usr/bin/env python
#coding:utf-8

import os


def read_image(image_dir):
    photofile_list = os.listdir(image_dir)
    for i in range(len(photofile_list)):
        photo_path = os.path.join(image_dir, photofile_list[i])
        print(photo_path)

read_image("G:/PycharmProject/test/")

 

执行结果:


G:/PycharmProject/test/.idea
G:/PycharmProject/test/a.txt
G:/PycharmProject/test/b.txt
G:/PycharmProject/test/example.ini
G:/PycharmProject/test/test.py
G:/PycharmProject/test/test2.py
G:/PycharmProject/test/yesterday
G:/PycharmProject/test/yesterday2

 

 

 

 5.5 例题:判定目录是否存在,若不存在即创建

import os
if not os.path.isdir(dir_name):
    os.makedir(dir_name)

 

 

s.mkdir()创建路径中的最后一级目录,而如果之前的目录不存在并且也需要创建的话,就会报错。

os.makedirs()创建多层目录,如果中间目录都不存在的话,会自动创建。

 5.6 读取文件的行数

import csv
a=open("Train_A_wear.csv","r")
b=len(a.readlines())
print(b)

 

 

5.7  批量重命名文件名

import os
 
class BatchRename():
    # 批量重命名文件夹中的图片文件
    def __init__(self):
        self.path_A = '../binary_variable/A'
        self.path_B = '../binary_variable/B'
 
 
    def rename(self):
        filelist_A = os.listdir(self.path_A)
        filelist_B = os.listdir(self.path_B)
        total_num_A = len(filelist_A)
        total_num_B = len(filelist_B)
        i = 1
        for item in filelist_A:
            # 初始的图片的格式为jpg格式的,或者png
            if item.endswith('.jpg') or item.endswith('png'):
                src = os.path.join(os.path.abspath(self.path_A), item)
                # 处理后的格式也为jpg格式的,当然这里也可以改成png格式
                dst = os.path.join(os.path.abspath(self.path_A), str(i) + '.jpg')
 
                try:
                    os.rename(src, dst)
                    i = i + 1
                except:
                    continue
 
        for item in filelist_B:
            # 初始的图片的格式为jpg格式的,或者png
            if item.endswith('.jpg') or item.endswith('png'):
                src = os.path.join(os.path.abspath(self.path_B), item)
                # 处理后的格式也为jpg格式的,当然这里也可以改成png格式
                dst = os.path.join(os.path.abspath(self.path_B), str(i) + '.jpg')
 
                try:
                    os.rename(src, dst)
                    i = i + 1
                except:
                    continue
 
if __name__ == '__main__':
    demo = BatchRename()
    demo.rename()

 

下面是一些实例练习:

 

 模拟 cp 操作

 方案
'拷贝'一个文件,可以首先在目标位置创建一个空文件,然后再将源文件读出,写入到新文件中。
打开文件时并不消耗太多内存,但是将文件内容读到变量中,会根据读入的数据大小消耗相应的内存。

为了防止一次性读入大文件消耗太多的内存,可以采用循环的方式,多次少量读取数据

 

方法1:

#!/usr/bin/env python
#coding:utf8
sfname = '/bin/ls'
dfname = '/root/ls'

src_fobj = open(sfname) #以读方式打开老文件
dst_fobj = open(dfname,'w') #以写方式打开新文件

while True:  #因为不确定要循环多少次,设置条件永远为真
    data = src_fobj.read(4096)  #每次只读入 4096 字节
    if not data: 
        break
    dst_fobj.write(data)

src_fobj.close()
dst_fobj.close()

 

方法2:

#!/usr/bin/env python
dstfile = '/root/ls'
srcfile = '/bin/ls'
oldf = open(srcfile) #以读方式打开老文件
newf = open(dstfile, 'w') #以写方式打开新文件
while True: #因为不确定要循环多少次,设置条件永远为真 data = oldf.read(4096) #每次只读入 4096 字节 if len(data) == 0: #如果文件已经全部读取完毕则中断循环 break newf.write(data)
oldf.close() newf.close()

 

 

 

 

 编写 makeTextFile.py 脚本,主要要求如下:
1、编写一个程序,要求用户输入文件名

2、如果文件已存在,要求用户重新输入

3、提示用户输入数据,每行数据先写到列表中

4、将列表数据写入到用户输入的文件名中

 

方案
首先通过循环不断要求用户输入文件名,如果文件已经存在,要求用户输入新文件名。

然后提示用户输入文件内容,将用户的输入信息保存到一个列表中。最后将列表中的每个字符串尾部都加上行结束标志,并写入文件。

为了程序的跨平台性,可以调用 os.linesep 确定行结束标志。在 Linux 系统中,行结束标志为’\n’;在 Windows 系统中,行结束标志为’\r\n’

 

 

#!/usr/bin/env python
#coding:utf8

import os

contents = []

while True:
    fname = raw_input("filename: ")
    if not os.path.exists(fname):
        break
    print "%s already exists.Try again" % fname


while True:
    data = raw_input("(Enter to quit)>")
    if not data:
        break
    contents.append(data + '\n')

fobj = open(fname,'w')
fobj.writelines(contents)
fobj.close()
                                                        

 

 

ppp1.py内容:

#!/usr/bin/env python
#coding:utf8


import sys
import time 

for i in range(1,6):
    sys.stdout.write("%s" % i)
    time.sleep(1)

 

执行结果:

12345

 

ppp2.py内容:

 

#!/usr/bin/env python
#coding:utf8


import sys
import time

for i in range(1,6):
    sys.stderr.write("%s" % i)
    time.sleep(1)

执行结果:

12345

 

 ppp3.py内容:

#!/usr/bin/env python
#coding:utf8

import time
import sys

for i in range(1,11):
    sys.stdout.write("%s" % i)
    sys.stderr.flush()
    time.sleep(0.2) 

执行结果:

12345678910

 railway.py内容:

#!/usr/bin/env python
#coding:utf8

import sys 
import time 

sys.stdout.write('#' * 20)
sys.stdout.flush()
counter = 0


while True:
    sys.stdout.write('\r%s@%s' % ('#' * counter,'#' * (19-counter)))
    sys.stdout.flush()
    counter += 1
    time.sleep(0.2)
    if counter == 20:
        counter = 0

执行结果:

#################@##

 

参考:

http://www.cnblogs.com/wj-1314/p/8476315.html

 

posted @ 2019-06-18 22:29  钟桂耀  阅读(478)  评论(0编辑  收藏  举报