本篇文章由网友“小黑”投稿发布。Python提供了两个内建函数,可以从标准的输入设备(一般是键盘)中读取数据。这两个函数分别是raw_input与input...

    页面导航: 前言:

    本篇文章由网友“小黑”投稿发布。

    本篇文章是根据《Python Tutorial》第16章的内容来写的学习笔记。该英文教程的下载地址如下:

    百度盘地址:http://pan.baidu.com/s/1c0eXSQG

    DropBox地址:点此进入DropBox链接

    Google Drive:点此进入Google Drive链接

    本篇文章也会涉及到Python的C源代码,这些C源码都是2.7.8版本的。想使用gdb来调试python源代码的话,就需要按照前面"Python基本的操作运算符"文章中所说的,使用configure --with-pydebug命令来重新编译安装python。

    如果Python的官方网站取消了Python-2.7.8.tgz的源码包的话,可以在以下两个链接中下载到:

    DropBox:Python-2.7.8.tgz的DropBox网盘链接

    Google Drive:Python-2.7.8.tgz的Google Drive网盘链接

    文章中的测试代码都是通过python 2.7.x的版本来执行的。

从键盘读取输入数据:

    Python提供了两个内建函数,可以从标准的输入设备(一般是键盘)中读取数据。这两个函数分别是raw_input与input。

raw_input函数:

    raw_input函数的语法格式如下:

raw_input([prompt]) -> string

    它可以接受一个可选的prompt参数,如果提供了该参数,则会先将此参数的字符串格式显示出来。然后等待用户的输入操作,当用户输入完数据并按回车后,该函数会将用户输入的数据以字符串的形式作为结果返回(返回的字符串中已经去除了末尾的换行符)。

    例如下面这个例子:

str = raw_input("Enter raw input:")
print str


    这段代码的执行结果如下:

black@slack:~/test$ python test.py
Enter raw input:hello world
hello world
black@slack:~/test$ 


    与raw_input内建函数相关的底层C函数定义在Python/bltinmodule.c文件中,对应的底层C函数为builtin_raw_input,有兴趣的读者可以对其进行分析以加深理解。

input函数:

    input函数的语法格式如下:

input([prompt]) -> value 等效于 eval(raw_input([prompt])) -> value

    input函数在Python内部会先通过raw_input来接受用户输入的字符串数据,接着会将用户输入的字符串作为python代码进行解释执行,并将执行的结果返回。

    因此,input([prompt])相当于eval(raw_input([prompt])),如果提供了prompt这个可选参数的话,input会将其直接传递给raw_input,以生成一个输入提示符,也就是将prompt以字符串的形式显示出来,再等待用户输入。

    下面是一个简单的例子:

val = input("Enter input:")
print val

val = eval(raw_input("Enter input:"))
print val


    这段代码的执行结果如下:

black@slack:~/test$ python test.py
Enter input:[x*5 for x in range(2,10,2)]
[10, 20, 30, 40]
Enter input:[x*5 for x in range(2,10,2)]
[10, 20, 30, 40]
black@slack:~/test$ 


    与input相关的底层C函数为builtin_input,该C函数也定义在Python/bltinmodule.c文件中,从这个C函数里也可以看到,input会先调用raw_input来接受输入数据,再将输入数据当作Python代码来执行。有兴趣的读者可以对其进行分析。

open函数:

    前面介绍了与标准输入相关的函数,下面再介绍下与文件操作相关的函数。

    在我们对文件进行读写操作之前,需要先通过open内建函数将指定的文件打开,再通过此函数返回的文件对象来进行具体的读写操作。open函数的语法格式如下:

open(name[, mode[, buffering]]) -> file object

    第一个参数name表示需要打开的文件名。

    第二个可选参数mode表示打开文件时的访问模式,可以是只读模式,只写模式,读写模式等。具体的模式下面会进行介绍,默认情况下是只读模式。

    第三个可选参数buffering用于指定文件的缓存机制,很多时候,写入文件的操作并不会立即作用到物理磁盘上,数据会先缓存到内存中,当条件成熟时,才被写入到磁盘里。

    当buffering为0时,可以关闭文件缓存,所有的写入操作都会立即作用到物理磁盘上。

    当buffering为1时,会采用line buffering(行缓存),只有当遇到换行符时,才会将缓存数据写入到磁盘中,也就是会缓存一行数据。

    当buffering为大于1的值时,就会采用full buffering(当缓冲区填满时再写入磁盘),python会使用你提供的buffering值在内部创建一个缓冲区,当缓冲区填满时,系统才将缓存区里的数据写入到磁盘中。

    当buffering为负数时(open函数的默认值就是-1),会采用系统内部的默认缓存,这个缓存一般也是full buffering,只不过缓存区域位于glibc指定的地方,而非Python创建的。

    上面的mode参数是一个与访问模式相关的字符串,在Python内部并没有定义具体的值,这是因为Python最终是通过fopen这个C标准库函数去执行打开文件的操作的,因此,mode的实际可用的值是由fopen来指定的,在Linux系统中可以通过man fopen来查看到:

black@slack:~/test$ man fopen
FOPEN(3)                   Linux Programmer's Manual                  FOPEN(3)

NAME
       fopen, fdopen, freopen - stream open functions

SYNOPSIS
       #include <stdio.h>

       FILE *fopen(const char *path, const char *mode);
       .............................................

       The argument mode points to a string beginning with one of the  follow‐
       ing sequences (Additional characters may follow these sequences.):

       r      Open  text  file  for  reading.  The stream is positioned at the
              beginning of the file.

       r+     Open for reading and writing.  The stream is positioned  at  the
              beginning of the file.

       w      Truncate  file  to  zero length or create text file for writing.
              The stream is positioned at the beginning of the file.

       w+     Open for reading and writing.  The file is created  if  it  does
              not  exist, otherwise it is truncated.  The stream is positioned
              at the beginning of the file.

       a      Open for appending (writing at end of file).  The file  is  cre‐
              ated  if it does not exist.  The stream is positioned at the end
              of the file.

       a+     Open for reading and appending (writing at end  of  file).   The
              file is created if it does not exist.  The initial file position
              for reading is at the beginning  of  the  file,  but  output  is
              always appended to the end of the file.

       The  mode string can also include the letter 'b' either as a last char‐
       acter or as a character between the characters in any of the  two-char‐
       acter strings described above.  This is strictly for compatibility with
       C89 and has no effect; the 'b' is ignored on all POSIX conforming  sys‐
       tems,  including Linux.  (Other systems may treat text files and binary
       files differently, and adding the 'b' may be a good idea if you do  I/O
       to a binary file and expect that your program may be ported to non-UNIX
       environments.)
       .............................................
black@slack:~/test$ 


    从上面的输出中可以看到,r表示以只读模式打开文件,同时文件指针位于文件的开头(这样就可以从头开始读取文件内容),open函数默认就是"r"只读模式。

    r+表示以读写模式打开文件,文件指针也位于文件的开头。

    w表示以只写的方式打开文件,该模式在打开文件时,会先将文件中原有的内容清空。如果指定的文件不存在,则会创建该文件,打开文件时,文件指针也位于文件的开头。

    w+表示以读写模式打开文件,如果指定的文件不存在,则会创建该文件,如果文件存在,则会清空原有的文件内容。打开文件时,文件指针位于文件的开头。

    a表示以追加的方式打开文件(也就是在文件末尾追加数据),如果指定的文件不存在,则会创建该文件。打开文件时,文件指针位于文件的结尾。

    a+表示以读取和追加的方式打开文件,如果指定的文件不存在,则会创建该文件。如果一开始执行读操作的话,会从文件的开头位置处读取数据。而执行写入操作的话,则会将数据写入到文件的结尾位置。

    在单字符模式后面,例如r,w,a后面可以接一个字符b,也就是rb,wb,ab模式。此外,在r+,w+,a+的中间也可以接一个字符b,即rb+,wb+,ab+模式。在所有POSIX标准的系统中(包括Linux在内),b字符会被忽略掉。只有在那些区分了文本文件与二进制文件的系统中(比如windows系统),b字符才会起到作用。

    虽然fopen的标准C库函数中,在Linux系统下会忽略掉字符b。但是,在Python内部,当检测到模式里包含了字符b时,会在返回的文件对象的C结构体中设置一个f_binary字段,也就是把文件当作二进制文件,对于二进制文件,写入的数据不会经过特殊的处理,都会原样写入到文件中。如果没有检测到字符b,则说明打开的是非二进制的文本文件,当写入Unicode字符串时,Python会将字符串先转换为文件指定的编码格式(或者转为系统默认编码),再写入到文本文件中。(这个结论是参考Python源代码中的Objects/fileobject.c文件里的fill_file_fields函数以及file_write函数得出来的,有兴趣的读者可以看一下)

    在windows系统下,在没有添加字符b时,写入的数据中,所有的'\n'字符都会被转为'\r\n',也就是被转为windows换行符,然后再存入磁盘。当读取文件时,磁盘数据里的所有的'\r\n'又都会被转换为'\n'字符(也就是转为C语言换行符)。如果是添加了字符b的模式的话,那么打开的文件就会被当作二进制文件,windows就不会对其进行换行符的转换处理,也就是写入与读取的数据会保持原样。

    open函数会返回一个file文件对象,Python的文件对象主要有以下几个属性:
  • file.closed:该属性用于判断文件的打开状态,如果是True则说明文件已经关闭了(此时不可以再进行读写操作),如果是False则说明文件处于打开状态。
  • file.mode:该属性记录了文件的访问模式,可以是上面的r,w,a之类的模式。
  • file.name:该属性存储了文件名。
  • file.softspace:该属性主要用于print指令,下面会举例说明,在3.x的Python版本中已经取消了该属性。
    先来看个简单的例子:

fo = open("foo.txt", "wb")
print "Name of the file: ", fo.name
print "Closed or not : ", fo.closed
print "Opening mode : ", fo.mode
print "Softspace flag : ", fo.softspace


    这段代码的执行结果如下:

black@slack:~/test$ python test.py
Name of the file:  foo.txt
Closed or not :  False
Opening mode :  wb
Softspace flag :  0
black@slack:~/test$  


    softspace属性主要用于print指令,例如下面这段代码:

import sys

lst = ['hello', 'world', 'test', 'haha']

print type(sys.stdout)

for x in lst:
	print x

print ''

for x in lst:
	print x,

print '\n'

for x in lst:
	print x,
	sys.stdout.softspace = 0


    这段代码的执行结果如下:

black@slack:~/test$ python test.py
<type 'file'>
hello
world
test
haha

hello world test haha 

helloworldtesthaha
black@slack:~/test$ 


    sys.stdout是与标准输出相关的文件对象,写入到该文件对象中的数据,最终会显示输出到终端上。print指令在输出字符串时,会先判断sys.stdout.softspace是否为0,如果为0,就不会输出空格,直接输出用户指定的字符串,如果不为0,则会先输出空格,再输出用户指定的字符串。

    对于下面这段代码:

print 'hello'
print 'world'

    会生成的字节码如下(这里的字节码只是起说明作用的伪代码):

# sys.stdout.softspace的初始值默认是0
PRINT_ITEM 'hello'
    # PRINT_ITEM 的大致执行过程:
    if(sys.stdout.softspace != 0)
	output " " # 如果softspace不为0则先输出空格符
    output 'hello' # 输出hello字符串
    sys.stdout.softspace = 1 # 将softspace设置为1

PRINT_NEWLINE
    # PRINT_NEWLINE的执行过程:
    output "\n" # 输出换行符
    sys.stdout.softspace = 0 # 将softspace重置为0

PRINT_ITEM 'world'
    # PRINT_ITEM 的大致执行过程:
    if(sys.stdout.softspace != 0)
	output " " # 如果softspace不为0则先输出空格符
    output 'world' # 输出world字符串
    sys.stdout.softspace = 1 # 将softspace设置为1

PRINT_NEWLINE
    # PRINT_NEWLINE的执行过程:
    output "\n" # 输出换行符
    sys.stdout.softspace = 0 # 将softspace重置为0


    因此,就是先输出hello,接着输出换行符,再输出world,再输出换行符,输出换行符的时候,已经将softspace重置为0了,因此,输出world的时候,并没有先输出空格符。

    而对于下面这段代码:

print 'hello',
print 'world',

    注意在每条print语句后面都多了一个逗号,会生成的字节码如下:

# sys.stdout.softspace的初始值默认是0
PRINT_ITEM 'hello'
    # PRINT_ITEM 的大致执行过程:
    if(sys.stdout.softspace != 0)
	output " " # 如果softspace不为0则先输出空格符
    output 'hello' # 输出hello字符串
    sys.stdout.softspace = 1 # 将softspace设置为1

PRINT_ITEM 'world'
    # PRINT_ITEM 的大致执行过程:
    if(sys.stdout.softspace != 0)
	output " " # 如果softspace不为0则先输出空格符
    output 'world' # 输出world字符串
    sys.stdout.softspace = 1 # 将softspace设置为1


    由于没有了PRINT_NEWLINE字节码,因此,输出字符串hello后,由于softspace被设置为了1,因此,在输出world之前,就先输出了空格符,再输出world。

    因此,输出的结果就是"hello world"。

    如果在上面两条print指令之间加入一条sys.stdout.softspace = 0的语句的话,也就是:

import sys
print 'hello',
sys.stdout.softspace = 0
print 'world',

    那么,这种情况下,由于在输出字符串hello后,我们人为的将softspace重置为了0,因此,在输出'world'时,就不会输出空格符了,得到的结果就会是"helloworld",此时,'hello'与'world'中间就不会有空格符。

    print指令所生成的字节码,是参考Python源代码中的Python/ceval.c文件里的PyEval_EvalFrameEx这个C函数得来的,这个C函数中就有PRINT_ITEM与PRINT_NEWLINE两个字节码的处理过程,有兴趣的读者可以通过gdb调试器对其进行分析。

文件对象的close方法:

    open函数返回的file文件对象,还支持close方法,该方法可以关闭打开的文件,同时将文件缓存中的数据写入到磁盘中。一旦关闭文件后,就不能再对其进行读写操作了。

    close方法的语法格式如下:

fileObject.close()

    下面是个简单的例子:

# Open a file
fo = open("foo.txt", "wb")
print "Name of the file: ", fo.name
print "Closed or not: ", fo.closed
print "Now close the file"
# Close opened file
fo.close()
print "Closed or not: ", fo.closed


    这段代码的执行结果如下:

black@slack:~/test$ python test.py
Name of the file:  foo.txt
Closed or not:  False
Now close the file
Closed or not:  True
black@slack:~/test$ 


    如果将上例中的fo赋予别的文件对象的话,那么之前打开的文件对象会自动关闭。此外,如果在代码中没有使用close方法手动关闭文件对象的话,Python在执行结束时也会自动关闭掉打开的文件对象。不过作者还是建议手动关闭掉不需要再用到的文件,这样可以防止文件被锁住,不至于影响其他程序正常使用文件。

文件对象的write方法:

    我们可以使用文件对象的write方法将数据写入到文件中。

    write方法的语法格式如下:

fileObject.write(data)

    data参数表示要写入到文件中的数据,这个数据可以是字符串数据,也可以是字节数据:

fo = open("foo.txt", "w")
fo.write("hello world\nBlack in Python\n")
fo.flush()
fo = open("foo2.txt", "wb")
bytes = [0x7b, 0x03, 0xff, 0x00, 0x66]
fo.write(bytearray(bytes))
fo.close()


    这段代码的执行结果如下:

black@slack:~/test$ python test.py
black@slack:~/test$ cat foo.txt
hello world
Black in Python
black@slack:~/test$ od -t x1 foo2.txt
0000000 7b 03 ff 00 66
0000005
black@slack:~/test$ 


    由于默认情况下存在文件缓存,因此,write写入的数据,会先存储在缓存里,除非缓存填满了(前面提到的full buffering机制下),才会写入磁盘文件。如果你想将数据立即写入到磁盘文件中的话,可以使用上面例子中用到的flush方法或者是close方法。

文件对象的read方法:

    通过文件对象的read方法可以从文件中读取数据,并将这些数据以字符串的形式返回。Python的字符串对象不仅可以存储普通的文本数据,还可以存储二进制数据。

    read方法的语法格式如下:

fileObject.read([count]);

    如果提供了count参数,则read方法只会将前count个字节的数据给读取出来。如果没提供count参数,那么read方法将根据文件的长度以及当前的文件指针,尽可能的将能读取的数据都读取出来。

    下面是一个简单的例子:

fo = open('foo.txt',"r")
str = fo.read()
print str
fo = open('foo2.txt', "rb")
data = bytearray(fo.read(4))
for x in data:
	print hex(x),


    这段代码的执行结果如下:

black@slack:~/test$ cat foo.txt
hello world
Black in Python
black@slack:~/test$ od -t x1 foo2.txt
0000000 7b 03 ff 00 66
0000005
black@slack:~/test$ python test.py
hello world
Black in Python

0x7b 0x3 0xff 0x0
black@slack:~/test$ 


    我们可以通过bytearray将read方法返回的字符串转换为字节数组,这样,就可以对文件中的二进制数据进行处理了。

文件指针的位置:

    read与write方法都是根据当前文件指针所在的位置来进行读写操作的,我们可以根据文件对象的tell()方法得到文件指针的当前位置,也就是文件指针距离文件开头的字节偏移值。

    此外,还可以通过文件对象的seek方法来改变当前文件指针的位置。seek方法的语法格式如下:

fileObject.seek(offset[, from])

    它是以from参数作为参考点,在该点基础上进行offset偏移,就可以得到新的文件指针位置。当from为os.SEEK_SET或者整数0时,就是相对于文件的开头的第一个字节进行偏移。当from为os.SEEK_CUR或者整数1时,就是相对于当前的文件指针进行偏移。当from为os.SEEK_END或者整数2时,就是相对于文件的结尾位置处进行偏移。如果没提供from参数的话,offset就是相对于文件的开头进行偏移。

    例如下面这段代码,代码中用到的foo.txt文件在之前的例子中已经创建好了:

import os

fo = open("foo.txt", "r")

str = fo.read(5);
print "Read String is : ", str

position = fo.tell();
print "Current file position : ", position

position = fo.seek(0, os.SEEK_SET);
str = fo.read(5);
print "Again read String is : ", str

fo.close()


    这段代码的执行结果如下:

black@slack:~/test$ python test.py
Read String is :  hello
Current file position :  5
Again read String is :  hello
black@slack:~/test$ 


重命名与删除文件的操作:

    Python的os模块也提供了一些方法,可以用于执行一些基本的文件操作,例如:os.rename方法可以用于重命名文件,os.remove方法则可以删除某文件。当然在使用这些方法之前,需要先导入os模块。

os模块的rename方法:

    rename方法的语法格式如下:

os.rename(src, dst)

    src对应源文件名,dst对应目标文件名,rename方法可以将src重命名为dst。在Linux系统下,如果目标文件已存在,则只要用户有足够的权限,就可以替换掉已存在的目标文件。但是在Windows系统中,如果目标文件已存在,则rename方法会抛出相关的错误。

    下面是一个简单的例子:

import os

os.rename('foo.txt', 'foo2.txt')
print 'foo.txt rename to foo2.txt'


    这段代码的执行结果如下:

black@slack:~/test$ ls
foo.txt test.py 
black@slack:~/test$ python test.py
foo.txt rename to foo2.txt
black@slack:~/test$ ls
foo2.txt test.py 
black@slack:~/test$ 


    如果在windows系统中,foo2.txt已经存在了的话,那么这段代码在执行时,就会抛出如下错误:

G:\Python27\mytest>..\python.exe test.py
Traceback (most recent call last):
  File "test.py", line 3, in <module>
    os.rename('foo.txt', 'foo2.txt')
WindowsError: [Error 183]

G:\Python27\mytest>


os模块的remove方法:

    remove方法的语法格式如下:

os.remove(path)

    通过path参数来指定文件路径,如果path对应的是一个目录的话,则会抛出错误,下面是一个简单的例子:

import os

os.remove('foo2.txt')
print 'delete file: foo2.txt success'
os.remove('/home/black/test.txt')
print 'delete file: /home/black/test.txt success'
print 'try to delete dir: ./Phone, this will fail!'
os.remove('Phone')


    这段代码的执行结果如下:

black@slack:~/test$ python test.py
delete file: foo2.txt success
delete file: /home/black/test.txt success
try to delete dir: ./Phone, this will fail!
Traceback (most recent call last):
  File "test.py", line 8, in <module>
    os.remove('Phone')
OSError: [Errno 21] Is a directory: 'Phone'
black@slack:~/test$ 


结束语:

    限于篇幅,本章先到这里,其他的内容将放到以后的章节中。

    OK,就到这里,休息,休息一下 o(∩_∩)o~~

    有一类卑微的工作是用坚苦卓绝的精神忍受着的,最低微的事情往往指向最高大的目标。

——  莎士比亚
 
上下篇

下一篇: Python基本的I/O操作 (二)

上一篇: Python定义和使用模块

相关文章

Python三角函数

Python的数字类型及相关的类型转换函数

Python的calendar模块

Python基本的I/O操作 (三)

Python的流程控制语句

Python的变量类型