文件中的.readlines()列表未索引值
我有一个文本文件,里面的内容是一些列表,像这样:
[1,2,3,4]
[5,6,7,8]
我用以下代码把这些列表放进了一个大列表里:
t = open('filename.txt', 'r+w')
contents = t.readlines()
alist = []
for i in contents:
alist.append(i)
当我运行
alist[0]
时,我得到了
[1,2,3,4]
但是当我运行
for a in alist:
print a[0]
时,我得到了
[
而不是列表中的第一个值。
3 个回答
你现在手里有一个字符字符串的列表。里面有括号和逗号的字符串,并不意味着它就是一个列表,它只是一个包含括号和逗号的字符串而已。
alist
就是这个列表。在你的循环中,a
是这个列表中的一个项目:首先是alist[0]
,然后是alist[1]
,依此类推。所以,a[0]
其实是在请求alist[0][0]
、alist[1][0]
等等:也就是每一行的第一个字符。这就是你得到的结果。
如果你想把它转换成真正的Python列表,可以使用ast.literal_eval()
。
如果你刚刚继承或下载了这些文件,无法改变它们的格式,并且你知道它们应该被当作Python的 list
来处理,那么使用 ast.literal_eval
是最好的选择,正如steveha所解释的那样:
t = open('filename.txt', 'r')
alist = []
for i in contents:
alist.append(ast.literal_eval(i))
如果你继承或下载了这些文件,并且只是猜测它们的格式,可能它们实际上是应该被当作JSON格式来读取,因为它们确实是有效的JSON,同时也是有效的Python字面量。在这种情况下:
t = open('filename.txt', 'r')
alist = []
for i in contents:
alist.append(json.loads(i))
但是如果这些文件是你自己创建的,那么你应该以一种适合序列化的方式来创建它们。
比如,不要这样做:
t = open('filename.txt', 'w')
for i in alist:
print >>t, i
而是应该这样做:
t = open('filename.txt', 'w')
json.dump(alist, t)
这样你就可以像这样写读取代码:
t = open('filename.txt', 'r')
alist = json.load(t)
像JSON、YAML或Pickle这样的序列化格式的主要目的是让你可以写入一个值,然后再读取回那个值。
像 print
、str
等函数并不是为了这个目的设计的;它们是为了让你以最漂亮的方式展示一个值,即使这样做可能会让你在之后很难或根本无法再读取这个值。
而 repr
函数则介于两者之间。它是为了让人类在交互式提示符下使用而设计的,所以如果可能的话,它会给你一个字符串,你可以在提示符中输入这个字符串来得到相同的值。这意味着在某些情况下, ast.literal_eval
是 repr
的反向操作,就像 json.load
是 json.dump
的反向操作一样。但即使在适用的情况下,也不应该完全依赖这个特性。
关于你的代码,有几点附加说明:
t = open('filename.txt', 'r+w')
如果你只是想读取文件,不要尝试以写入模式打开它。而且,如果你确实想同时进行读取和写入,正确的模式字符串是 r+
,而不是 r+w
。(你这样做在技术上是错误的,但大多数Python版本会忽略 w
,所以你可以逃过一劫。)
如果模式是 r
,你根本不需要指定它,因为这是默认值。
同时,你从来没有 close
文件。最简单的做法是使用 with
语句。
contents = t.readlines()
几乎没有好的理由去调用 readlines()
。这个方法会给你一系列的行,但文件本身已经是一个行的序列。你只是在多做一份副本而已。
alist = []
for i in contents:
alist.append(i)
这种模式——创建一个空列表然后在循环中添加元素——是非常常见的,Python为此提供了一个快捷方式,叫做列表推导式。推导式比显式循环更简洁、更易读、更不容易出错,而且速度更快,所以大多数情况下值得使用。
最后,给你的变量起有意义的名字是更好的做法。特别是如果你希望其他人(或者六个月后的你自己)能够调试你的代码。如果代码运行得很好,我们可以通过变量的作用来理解它们的含义,但如果不行,我们就无法修复它,除非我们能猜到它们“应该”代表什么,而名字是传达这一点的最佳方式。
所以,综合来看,你的原始代码可以写成:
with open('filename.txt') as textfile:
alist = [line for line in textfile]
而各种修正版本是:
with open('filename.txt') as textfile:
alist = [ast.literal_eval(line) for line in textfile]
with open('filename.txt') as textfile:
alist = [json.loads(line) for line in textfile]
with open('filename.txt') as textfile:
alist = json.load(textfile)
.readlines()
是用来读取文件中的每一行,并把它们当作字符串来处理。这个字符串的第一个字符是一个 [
符号。
如果你想读取文本文件并把它转换成数据结构,最简单的方法就是使用 Python 内置的 eval()
函数。不过,更安全的做法是使用 ast.literal_eval()
。
http://docs.python.org/2/library/ast.html?highlight=literal#ast.literal_eval
推荐的代码:
import ast
with open("filename.txt") as f:
alist = [ast.literal_eval(line) for line in f]
print(type(alist[0])) # prints: <type 'list'>
print(alist[0]) # prints: [1,2,3,4]
我们几乎不想使用 .readlines()
,因为它会一次性把文件中的所有行都读进来,如果文件很大,就会让你的程序占用很多内存。打开的文件句柄对象(在我的例子中是 f
)可以用作迭代器,每次迭代时它会返回文件中的一行。因此,使用 for
循环或者列表推导式可以一次从文件中提取一行。这样,这个示例程序就不会把整个文件都放在内存中,而是每次只保留一行,同时构建列表。如果这个程序调用了 .readlines()
,它会把所有行都保存在内存中,还会保存列表,这样内存的峰值使用量就会更高。(当然,对于像这个例子这样的小文件来说,这并没有太大关系。但为了节省内存,尽量采用高效的方式,为什么不呢?)
使用 with
来打开文件总是个好习惯。这样你就可以确保在完成操作后,文件会被正确关闭。
我们使用列表推导式来构建 ast.literal_eval()
的结果列表,对于给定的输入文件,每一行都会返回一个列表,所以 alist
将会是一个列表的列表。