【问题标题】:Python read streamPython读取流
【发布时间】:2014-11-25 11:57:22
【问题描述】:

我需要一种非常廉价的方式来读取 Python 中没有终止字符串(流)的缓冲区。这就是我所拥有的,但它浪费了 大量 的 CPU 时间和精力。因为它在不断地“尝试和捕捉”。我真的需要一种新方法。

这是我的代码的简化工作版本:

#! /usr/bin/env/ python
import fcntl, os, sys

if __name__ == "__main__":
    f = open("/dev/urandom", "r")
    fd = f.fileno()
    fl = fcntl.fcntl(fd, fcntl.F_GETFL)
    fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)

    ready = False
    line = ""
    while True:
        try:
            char = f.read()
            if char == '\r':
                continue
            elif char = '\n':
                ready = True
            else:
                line += char
        except:
            continue
        if ready:
            print line

不要在终端中运行它。这只是为了说明。 “urandom”会破坏你的终端,因为它会吐出很多终端模拟器无论如何都会解释的随机字符(这可以改变你当前的 shell 设置、标题等)。我正在从通过 USB 连接的 GPS 读取数据。

问题:尽可能使用 100% 的 CPU 使用率。我试过这个:

#! /usr/bin/env/ python
import fcntl, os, sys

if __name__ == "__main__":
    f = open("/dev/urandom", "r")
    fd = f.fileno()
    fl = fcntl.fcntl(fd, fcntl.F_GETFL)
    fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)

    for line in f.readlines():
        print line

但是,我得到了IOError: [Errno 11] Resource temporarily unavailable。我曾尝试使用Popen。我很茫然。有人可以提供解决方案(并请解释一切,因为我本身不是专业人士)。另外,我应该注意,这是针对 Unix(尤其是 Linux,但它必须在所有版本的 Linux 上都可移植)。

【问题讨论】:

  • 哇!我真的只是发现了它……虽然是 Python 2.6 的新手……docs.python.org/2/library/io.html
  • 不过,您需要使用无缓冲模式。
  • 我认为它对你有用; ` io.open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True)`
  • 为了清楚起见,您真的想读取 urandom 还是普通文件或管道的替代品?
  • @tdelaney 不!我正在尝试从 /dev/ttyUSB0 读取 gps puck。它隐藏在第二段的最后一句中。

标签: python filestream


【解决方案1】:

您需要将缓冲模式设置为打开文件流时要读取的块的大小。 来自python文档:

io.open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True)

"buffering 是一个可选整数,用于设置缓冲策略。传递 0 关闭缓冲(仅在二进制模式下允许),1 选择行缓冲(仅在文本模式下可用),和一个 > 1 的整数来表示固定大小的块缓冲区的大小。"

您还想在 while 循环中使用 readable() 方法,以避免不必要的资源消耗。

不过,我建议您使用 缓冲流,例如 io.BytesIOio.BufferedReader

更多信息请关注docs

【讨论】:

  • 这就是我决定使用的。我认为 io.open 应该足够了,因为我只是从非阻塞文件(流)中读取文本。我真的不太了解 Java 中的 BufferedReader 缓冲除外。如果我知道我将阅读的行不会超过n 个字符,我应该使用f = io.open("/dev/urandom", mode="r", buffering=n)吗?
  • 另外,有人评论说我要使用无缓冲模式,我认为他们是正确的!
【解决方案2】:

简单的解决方案是最好的:

with open('/dev/urandom', 'r') as f:
    for line in f:
        print line.encode('hex')  # Don't mess up my terminal

或者,也可以

with open('/dev/urandom', 'r') as f:
    for line in iter(f.readline, ''):
        print line.encode('hex')  # Don't mess up my terminal

注意事项:

  • 让文件描述符处于阻塞模式,这样操作系统可以在没有可用数据时阻塞您的进程(并节省 CPU 时间)。

  • 在循环中使用迭代器很重要。考虑for line in f.readlines():f.readlines() 读取所有数据,将其全部放入一个列表中,然后返回该列表。由于我们有无限的数据,f.readlines() 永远不会成功返回。相比之下,f 返回一个迭代器——它只获取满足下一次循环迭代所需的尽可能多的数据(对于性能缓冲区来说只是多一点。)

  • 第一个版本提前读取并缓冲足够的数据以打印多行。第二个版本立即返回每一行。如果节省 CPU 是您最关心的问题,请使用第一个版本。如果交互式响应时间是您最关心的问题,请使用第二个。

演示:

$ python x.py  | head -2l
eb99f1b3bf74eead42750c63cb7c16160fa7e21c94b176dc6fd2d6796a1428dc8c5d15f13e3c1d5969cb59317eaba37a97f4719bb3de87919009da013fa06ae738408478bc15c750850744a4edcc27d155749d840680bf3a827aafbe9be84e7c8e2fe5785d2305cbedd76454573ca9261ac9a480f71242baa94e8d4bdf761705a6a0fea1ba2b1502066b2538a62776e9165043e5b7337d45773d009fd06d15ca0d9b51af499c1c9d7684472272a7361751d220848874215bc494456b08910e9815fc533d3545129aad4f3f126dc5341266ca4b85ea949794cacaf16409bcd02263b08613190b3f69caa68a47758345dafb10121cfe6ed6c8098142682aef47d1080bd2e218b571824bf2fa5d0bb5297278be8a9a2f55b554631c99e5f1d9040c5bc2bde9a40c8b6e95fc47be6ea9235243582f2367893d15a1494f732d0346ec6184a366f8035aef9141c638128444b1549a64937697b1a170e648d20f336e352076893fa7265c8fa0f4e2207e87410e53b43a51aa146ac6c2decf274a45a58c4e442aececf28879a3e0b4a1278eac7a4f969b3f74e2f2a2064a55ff112c4c49092366dbaa125703962ec5083d09cdb750c0e1dbe34cadda66709f98ff63faccf0045993137bfaca949686bc395bbafb7cf9b5b3475a0c91bdea8cec4e9ac1a9c96e0b81c1c5f242ae72cdea4c073db0351322f9da31203ea34d1b6f298128435797f4846a53b0733069060680dbc2b44c662c4b685ced5419b65c01df41cc2dd9877dc2a97a965174d508a3c9275d8aee7f2991bbb06ca7e0010b0e5b9468aed12f5d2c9a65091223547b8655211df435ffbf24768d48c7e7cf3cb7225f2c116e94a8602078f2b34dab6852f57708e760f88f4085ec7dade19ed558a539f830adea1b81f46303789224802f1f090ec0ff59e291246f1287672b0035df07c359d2ada48e674622f61c0f456c36d130fb6cf7f529e7c4dfceccc594ba5e812a3250e022eca9576a5a8b31c0be13969841d5a4d52b10a7dc8ddd1cac279500cb66e3b244e7d1e042249fd8adf2a90fa8bee74378d79a3d55c6fcf6cc19aa85ffb078dba23ca88ea6810d4a1c5d98b3b33e68ddd41c881df167c36ab2e1b081849781e08e3a026fbd3755acf9f215e0402cbf1a021300f5c883f86a05d467479172109a8f20f93c8c255915a264463eb113c3e8d07d0cec31aa8c0f978a0e7e65c142e85383befd6679c69edd2c56599f15580bbb356d98cfdf012dbc6d1dd6c0dbcfe6f8235d3d5c015fb94d8cc29afdf5d69e33d0e5078d651782546bc2acccab9f35e595f0951a139526ae5651a3ebbec353e99f9ddd1615ed25529500dabe8bf6f12ee6b21a437caca12a6d9688986d94fb7c103dca1572350900e56276b857630a
9d024ef4454dcd5e35dd605a2d49c26ce44fae87ab33e7a158d328521c7d77969908ec5b67f01bf8e2c330dcb70b5f3def8e6d4b010c6d31e4cbe7478657782f10b6fc2d77e8ff7a2f1e590827827e1037b33b0a
Traceback (most recent call last):
  File "x.py", line 4, in <module>
    print line.encode('hex')  # Don't mess up my terminal
IOError: [Errno 32] Broken pipe

【讨论】:

  • 哦,对不起;我有没有提到我需要解锁它?在到达 EOF 之前一直阻塞。
  • 不,这会阻塞直到到达行尾,然后将控制权返回给for 循环的主体。 for line in f.readlines()for line i f 之间的主要区别在于:前者阻塞直到到达 EOF,后者阻塞直到到达行尾。
  • 是的;我不能让它阻塞。我将从无头计算机上的 gps 冰球读取数据,并且无法“告诉”程序停止。此外,如果您甚至希望能够阅读这些行,您忘记了 try: except (KeyboardInterrupt):
  • 我的程序不需要try: ... except KeyboardInterrupt: ...。如果我想阅读这些行,我会将它们重定向或通过管道将它们发送到less. 至于使程序停止的能力,也许您可​​以在您的问题中添加更多细节,了解您实际想要实现的目标。
  • 1) 要让它与 GPS 一起工作,请尝试第二个示例(我刚刚添加了它)。 2)对于只输出ascii的文件,将print line.encode('hex')替换为print line。 3) 它适用于 urandom,因为 urandom 总是准备好更多数据。 (没有 EOF 字符之类的东西。EOF 是一个条件,而不是字符值。)
【解决方案3】:

我决定使用io。我注意到这甚至比while True: 准确得多。我正在读取的 gps 应该每秒吐出信息,但我注意到它实际上在 0.95 到 1.05 秒之间。那时我正在做我在问题中发布的内容。

但是,当我只是这样做时

#! /usr/bin/env/ python

import io

if __name__ == "__main__":
    f = io.open("/dev/ttyUSB0")
    while True:
        print f.readline().strip()

它不仅临时阻塞(这节省了 cpu 时间,并且有很多好处),而且显然它还使缓冲区保持最新状态,因为它似乎几乎在一秒钟内产生结果分开(这是我的 gps - 像大多数人一样 - 更新的时候)。

班级是一个真正的奇迹——一个真正的奇迹——如果它是这样做的唯一方法。一个人可以只使用open(file, "r"),它工作得很好(这让我很生气,因为我花了整整一天的时间)。

【讨论】:

  • 如果将io.open 替换为open,此程序是否同样有效?
  • 天哪;实际上确实如此。我 99% 确定我已经尝试过了。
  • 确实试过了,但是我用了f = open("/dev/ttyUSB0", "r")fd = f.fileno()fl = fcntl.fcntl(fd, fcntl.F_GETFL)fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK),然后是真的。 .. readline 没有工作。[
  • while True: line = f.readline() 或多或少等同于 for line in iter(f.readline, ''): pass。每个人都在无限循环中调用f.readline()。 (正如您所指出的,区别在于能够将f.readline() 包装在try: except: 中)。
猜你喜欢
  • 2012-08-07
  • 2011-03-15
  • 1970-01-01
  • 2019-07-08
  • 2023-01-27
  • 2012-09-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多