如何从列表元素中移除 \n?
我正在尝试让Python从一个.txt文件中读取一行,并把这一行的元素放到一个列表里。文件里的元素是用制表符分开的,所以我用了split("\t")
来分隔这些元素。因为这个.txt文件里有很多元素,所以我把每一行找到的数据保存到了一个单独的列表里。
我现在遇到的问题是,列表显示成这样:
['Name1', '7.3', '6.9', '6.6', '6.6', '6.1', '6.4', '7.3\n']
我该怎么做才能把列表最后一个元素中的\n
去掉,让它变成'7.3'
呢?
15 个回答
听起来你想要的功能类似于 Perl 里的 chomp()
函数。
在 Python 中实现这个功能非常简单:
def chomp(s):
return s[:-1] if s.endswith('\n') else s
... 前提是你使用的是 Python 2.6 或更高版本。如果不是的话,可以用稍微复杂一点的方法:
def chomp(s):
if s.endwith('\n'):
return s[:-1]
else:
return s
如果你想要去掉字符串末尾的所有换行符(在某些奇怪的情况下,可能会有多个换行符):
def chomps(s):
return s.rstrip('\n')
显然,正常情况下,Python 文件对象的 readline()
或 readlines()
方法不会返回这样的字符串。
我见过有人盲目地从文件 readline()
和类似函数的结果中去掉最后一个字符(用 s[:-1]
切片)。这其实是个坏主意,因为如果文件的最后一行不是以换行符结束,这样做会导致错误。
一开始,你可能会觉得随意去掉读取行的最后字符是安全的。如果你用普通的文本编辑器创建测试文件,大多数编辑器会在最后一行的末尾默默添加一个换行符。要创建一个有效的测试文件,可以用类似下面的代码:
f = open('sometest.txt', 'w')
f.write('some text')
f.close()
... 然后如果你重新打开这个文件,使用 readline()
或 readlines()
方法读取,你会发现文本是没有末尾换行符的。
很多年来,文本文件以非换行符结尾的问题一直困扰着许多 UNIX 工具和脚本语言。这是一个愚蠢的边缘案例错误,时不时会出现在代码中,虽然不常见,但足以让人烦恼。我们可以争论说,没有最终换行符的“文本”文件是“损坏的”或不标准的;这在某些编程规范中可能是合理的。
然而,在编码时忽视这些边缘情况是很容易的,而这种无知会在依赖你代码的人身上造成问题。正如我妻子所说:在编程时……要注意安全!
从Python3开始
在Python3中,map
不再返回一个list
,而是返回一个mapObject
,所以结果看起来会像这样:
>>> map(lambda x:x.strip(),l)
<map object at 0x7f00b1839fd0>
你可以在Python 3.0的新特性中了解更多信息。
map()
和filter()
返回的是迭代器。如果你真的需要一个list
,可以用list(map(...))
来快速解决。
那么现在有哪些方法可以解决这个问题呢?
案例1 - 用lambda
的map
调用
map
返回一个迭代器。list
是一个可以把迭代器转换成列表的函数。因此你需要在map
外面加一个list
的调用。这样答案就变成了:
>>> l = ['Name1', '7.3', '6.9', '6.6', '6.6', '6.1', '6.4', '7.3\n']
>>> list(map(lambda x:x.strip(),l))
['Name1', '7.3', '6.9', '6.6', '6.6', '6.1', '6.4', '7.3']
很好,我们得到了输出。现在我们来检查这段代码执行所需的时间。
$ python3 -m timeit "l = ['Name1', '7.3', '6.9', '6.6', '6.6', '6.1', '6.4', '7.3\n'];list(map(lambda x:x.strip(),l))"
100000 loops, best of 3: 2.22 usec per loop
2.22微秒。这还不错。但有没有更高效的方法呢?
案例2 - 不用lambda
的map
调用
在Python社区中,很多人对lambda
并不太喜欢(包括Guido)。此外,它会大大降低程序的速度。因此我们尽量避免使用它。这里可以使用顶层函数str.strip
来帮助我们。
可以不使用lambda
,用str.strip
重写map
,如下:
>>> list(map(str.strip,l))
['Name1', '7.3', '6.9', '6.6', '6.6', '6.1', '6.4', '7.3']
现在来看看时间。
$ python3 -m timeit "l = ['Name1', '7.3', '6.9', '6.6', '6.6', '6.1', '6.4', '7.3\n'];list(map(str.strip,l))"
1000000 loops, best of 3: 1.38 usec per loop
太棒了。你可以看到这两种方法之间的效率差异。它快了近60%。因此,不使用lambda
的方法在这里是更好的选择。
案例3 - 遵循指南,常规方法
来自Python 3.0的新特性的另一个重要点是建议我们尽量避免使用map
。
特别棘手的是为了函数的副作用而调用
map()
;正确的做法是使用常规的for
循环(因为创建一个列表只是浪费)。
所以我们可以通过使用常规的for
循环来解决这个问题。
最简单的解决方法(暴力法)是:
>>> l = ['Name1', '7.3', '6.9', '6.6', '6.6', '6.1', '6.4', '7.3\n']
>>> final_list = []
>>> for i in l:
... final_list.append(i.strip())
...
>>> final_list
['Name1', '7.3', '6.9', '6.6', '6.6', '6.1', '6.4', '7.3']
时间设置
def f():
l = ['Name1', '7.3', '6.9', '6.6', '6.6', '6.1', '6.4', '7.3\n']
final_list = []
for i in l:
final_list.append(i.strip())
import timeit
print(min(timeit.repeat("f()","from __main__ import f")))
结果是。
1.5322505849981098
如你所见,暴力法在这里稍微慢一点。但对于普通程序员来说,它比map
的写法更易读。
案例4 - 列表推导式
这里也可以使用列表推导式,和Python2中的用法一样。
>>> [i.strip() for i in l]
['Name1', '7.3', '6.9', '6.6', '6.6', '6.1', '6.4', '7.3']
现在来看时间:
$ python3 -m timeit "l = ['Name1', '7.3', '6.9', '6.6', '6.6', '6.1', '6.4', '7.3\n'];[i.strip() for i in l]"
1000000 loops, best of 3: 1.28 usec per loop
如你所见,列表推导式比map
(即使不使用lambda
)更有效。因此,在Python3中,使用列表推导式而不是map
是一个好规则。
案例5 - 就地修改和空间效率(时间-空间权衡)
最后一种方法是在列表内部进行就地修改。这将节省很多内存空间。可以使用enumerate
来实现。
>>> l = ['Name1', '7.3', '6.9', '6.6', '6.6', '6.1', '6.4', '7.3\n']
>>> for i,s in enumerate(l):
... l[i] = s.strip()
...
>>> l
['Name1', '7.3', '6.9', '6.6', '6.6', '6.1', '6.4', '7.3']
时间结果是1.4806894720022683
。不过这种方法在空间上是有效的。
结论
以下是一个时间比较列表(Python 3.4.3和Python 3.5.0)
----------------------------------------------------
|Case| method | Py3.4 |Place| Py3.5 |Place|
|----|-----------------|-------|-----|-------|-----|
| 1 | map with lambda | 2.22u | 5 | 2.85u | 5 |
| 2 | map w/o lambda | 1.38u | 2 | 2.00u | 2 |
| 3 | brute-force | 1.53u | 4 | 2.22u | 4 |
| 4 | list comp | 1.28u | 1 | 1.25u | 1 |
| 5 | in-place | 1.48u | 3 | 2.14u | 3 |
----------------------------------------------------
最后要注意,列表推导式是最好的方法,而使用lambda
的map
是最差的。但再次强调——仅在PYTHON3中
如果你只想去掉最后一个元素中的 \n
,可以用这个:
t[-1] = t[-1].strip()
如果你想去掉所有元素中的 \n
,可以用这个:
t = map(lambda s: s.strip(), t)
你也可以考虑在分割行之前先去掉 \n
:
line = line.strip()
# split line...