我有一个多行字符串定义如下:
foo = """
this is
a multi-line string.
"""
这个字符串用作我正在编写的解析器的测试输入。parser函数接收一个file
对象作为输入并对其进行迭代。它还直接调用next()
方法来跳过行,所以我真的需要一个迭代器作为输入,而不是iterable。
我需要一个迭代器,它可以像file
对象那样遍历字符串的每一行,对象可以遍历文本文件的每一行。我当然可以这样做:
lineiterator = iter(foo.splitlines())
有更直接的方法吗?在这种情况下,字符串必须遍历一次进行拆分,然后由解析器再次遍历。这在我的测试用例中并不重要,因为那里的字符串很短,我只是出于好奇而问。Python有很多有用且高效的内置组件,但是我找不到任何适合这种需要的东西。
我不知道你说的“然后再由解析器”是什么意思。拆分完成后,不再遍历字符串,只遍历被拆分字符串的列表。这可能是实现这一点的最快方法,只要字符串的大小不是非常大。python使用不可变字符串的事实意味着您必须始终创建一个新字符串,因此无论如何这必须在某个时刻完成。
如果字符串非常大,缺点是内存使用:同时在内存中有原始字符串和拆分字符串列表,所需的内存将增加一倍。迭代器方法可以帮您节省这一点,根据需要构建一个字符串,尽管它仍然要支付“拆分”的代价。但是,如果字符串太大,则通常要避免内存中包含未拆分的字符串。最好只是从文件中读取字符串,它已经允许您以行的形式遍历它。
但是,如果内存中已经有一个巨大的字符串,一种方法是使用StringIO,它为字符串提供类似文件的接口,包括允许按行迭代(内部使用.find查找下一个换行符)。然后你得到:
有三种可能性:
运行这个作为主脚本确认这三个函数是等价的。使用
timeit
(和* 100
用于foo
以获得更精确测量所需的大量字符串):注意,我们需要
list()
调用来确保遍历迭代器,而不仅仅是构建迭代器。听着,这种简单的实现要快得多,甚至一点也不好笑:比我尝试的
find
调用快6倍,而这又比低级方法快4倍。要记住的教训:度量总是一件好事(但必须是准确的);像
splitlines
这样的字符串方法以非常快的方式实现;通过在非常低的级别编程(特别是通过非常小的片段的+=
循环)将字符串组合在一起可能会非常慢。编辑:添加了@Jacob的建议,稍加修改以获得与其他建议相同的结果(保留一行的尾随空白),即:
测量给出:
还不如基于
.find
的方法——仍然值得记住,因为它可能不太容易被一个bug所忽略(任何出现+1和-1的循环,就像上面的f3
,都应该由一个怀疑自动触发——许多缺少这种调整并应该有它们的循环也应该如此——尽管相信我的代码也是正确的,因为我可以用其他函数检查它的输出。但基于拆分的方法仍然适用。
旁白:可能更好的
f4
样式是:至少,不那么冗长了。遗憾的是,删除尾部
\n
的需要禁止用return iter(stri)
更清晰、更快地替换while
循环(我相信,在现代版本的Python中,iter
部分是多余的,从2.3或2.4开始,但它也是无害的)。或许值得一试:或者它的变体——但是我在这里停下来,因为这几乎是一个基于
strip
的、最简单和最快的理论练习。如果我正确地阅读了
Modules/cStringIO.c
,这应该非常有效(尽管有些冗长):相关问题 更多 >
编程相关推荐