从Python队列中读取行

时间:2012-09-05 20:54:03

标签: python

我正在将我的脚趾浸入Python线程中。我创建了一个供应商线程,通过队列从* nix(串行)/ dev返回字符/行数据。

作为练习,我想一次一行地使用队列中的数据(使用'\ n'作为行终止符)。

我当前的(简单化)解决方案是一次只将1个字符放入队列中,因此消费者一次只能得到()一个字符。 (这是一个安全的假设吗?)这种方法目前允许我做以下事情:

...
return_buffer = []
while True:
    rcv_data = queue.get(block=True)
    return_buffer.append(rcv_data)        
    if rcv_data == "\n":
        return return_buffer

这似乎有效,但是当我一次放()2个字符时,我肯定会导致它失败。

我想使接收逻辑更通用,并能够处理多字符put()s。

我的下一个方法是rcv_data.partition(“\ n”),将“余数”放在另一个缓冲区/列表中,但这需要在队列旁边处理临时缓冲区。 (我想另一种方法是一次只放一行(),但那里的乐趣在哪里?)

是否有一种更优雅的方式一次从队列中读取一行?

4 个答案:

答案 0 :(得分:3)

这对发电机来说可能很有用。它会在收益后准确地取出它停止的位置,因此减少了所需的存储量和缓冲区交换量(我不能说它的性能)。

def getLineGenerator(queue, splitOn):
    return_buffer = []
    while True:
        rcv_data = queue.get(block=True) # We can pull any number of characters here.
        for c in rcv_data:
            return_buffer.append(c)
            if c == splitOn:
                yield return_buffer
                return_buffer = []


gen = getLineGenerator(myQueue, "\n")
for line in gen:
    print line.strip()

修改

一旦J.F. Sebastian指出行分隔符可能是多字符的,我也必须解决这个问题。我也从jdi的答案中使用了StringIO。我再说不出效率,但我相信它在所有情况下都是正确的(至少我能想到的是这样)。这是未经测试的,因此可能需要进行一些调整才能实际运行。感谢J.F. Sebastian和jdi的答案,最终导致了这一点。

def getlines(chunks, splitOn="\n"):
    r_buffer = StringIO()
    for chunk in chunks
        r_buffer.write(chunk)
        pos = r_buffer.getvalue().find(splitOn) # can't use rfind see the next comment
        while pos != -1: # A single chunk may have more than one separator
            line = r_buffer.getvalue()[:pos + len(splitOn)]
            yield line
            rest = r_buffer.getvalue().split(splitOn, 1)[1]
            r_buffer.seek(0)
            r_buffer.truncate()
            r_buffer.write(rest)
            pos = rest.find(splitOn) # rest and r_buffer are equivalent at this point. Use rest to avoid an extra call to getvalue

    line = r_buffer.getvalue();
    r_buffer.close() # just for completeness
    yield line # whatever is left over.

for line in getlines(iter(queue.get, None)): # break on queue.put(None)
    process(line)

答案 1 :(得分:2)

如果您的特定用例生产者需要逐个字符地放入队列,那么我认为在消费者的循环中获取它们时我看不出有什么问题。但是,通过使用StringIO对象作为缓冲区,您可以获得更好的性能。

from cStringIO import StringIO
# python3: from io import StringIO

buf = StringIO()

该对象如果类似于文件,那么您可以write找到它,并随时调用getvalue()以获取缓冲区中的完整字符串值。这很可能比不断增长列表,将其加入字符串并清除它更能提供更好的性能。

return_buffer = StringIO()
while True:
    rcv_data = queue.get(block=True)
    return_buffer.write(rcv_data)        
    if rcv_data == "\n":
        ret = return_buffer.getvalue()
        return_buffer.seek(0)
        # truncate, unless you are counting bytes and
        # reading the data directly each time
        return_buffer.truncate()

        return ret

答案 2 :(得分:1)

队列完全返回您放入的内容。如果放入碎片就会得到碎片。如果你放线,你会得到线条。

如果允许输入中的部分行并且稍后可以完成,则需要一个显式或隐式缓冲区来存储部分行:

def getlines(fragments, linesep='\n'):
    buff = []
    for fragment in fragments:
        pos = fragment.rfind(linesep)
        if pos != -1: # linesep in fragment
           lines = fragment[:pos].split(linesep)
           if buff: # start of line from previous fragment
              line[0] = ''.join(buff) + line[0] # prepend
              del buff[:] # clear buffer
           rest = fragment[pos+len(linesep):]
           if rest:
              buff.append(rest)
           yield from lines
        elif fragment: # linesep not in fragment, fragment is not empty
           buff.append(fragment)

    if buff:
       yield ''.join(buff) # flush the rest

它允许任意长度的片段,linesep。 linesep不应该跨越几个片段。

用法:

for line in getlines(iter(queue.get, None)): # break on queue.put(None)
    process(line)

答案 3 :(得分:0)

值得注意的是,队列中可能有多行。此函数将返回(并可选地打印)给定队列中的所有行:

def getQueueContents(queue, printContents=True):
    contents = ''
    # get the full queue contents, not just a single line
    while not queue.empty():
        line = queue.get_nowait()
        contents += line
        if printContents:
            # remove the newline at the end
            print line[:-1]
    return contents