Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/324.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/visual-studio-2010/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
从Python队列读取行_Python - Fatal编程技术网

从Python队列读取行

从Python队列读取行,python,Python,我正在埋头研究Python线程。我创建了一个供应商线程,它通过队列从*nix(serial)/dev返回字符/行数据 作为练习,我希望每次使用队列中的一行数据(使用“\n”作为行终止符) 我目前(过于简单)的解决方案是每次只将()放入队列中1个字符,因此消费者一次只能得到()一个字符。(这是一个安全的假设吗?) ... return_buffer = [] while True: rcv_data = queue.get(block=True) return_buffer.app

我正在埋头研究Python线程。我创建了一个供应商线程,它通过队列从*nix(serial)/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”),将“余数”放在另一个缓冲区/列表中,但这需要在队列旁边处理临时缓冲区。 (我想另一种方法是一次只放一行,但这其中的乐趣何在?)


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

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

from cStringIO import StringIO
# python3: from io import StringIO

buf = StringIO()
如果文件喜欢对象,则可以
写入它,查找它,并随时调用
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

这可能是发电机的良好用途。它将精确地恢复到屈服后停止的位置,因此减少了您需要的存储和缓冲区交换量(我无法谈论它的性能)

编辑:

一旦J.F.塞巴斯蒂安指出行分隔符可以是多字符的,我也必须解决这个问题。我还使用了jdi答案中的StringIO。同样,我不能谈论效率,但我相信它在所有情况下都是正确的(至少在我能想到的情况下)。这是未经测试的,因此可能需要一些调整才能实际运行。感谢J.F.塞巴斯蒂安(J.F.Sebastian)和jdi(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)

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

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

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不应跨越多个片段

用法:

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

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

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


我之前回答的类似问题是否涵盖了这个问题?是的,正如我提到的,那会有用的。它还将把我与使用数据的方法“捆绑”在一起。这是终端数据,因此在“提示”字符处开始解析输出需要重新写入生产者。我希望保留一个泛型/dev读取器线程,并能够通过改变队列使用者方法来改变检索数据的方式。我知道如何用“艰难”的方式来实现这一点,但如果存在的话,我希望学习一种更优雅的方法。哦,我假设您希望队列有多个工作来处理它。这是否仅允许单个工作者使用字符?是的,只有一个使用者。对于多个消费者来说,我同意每条线都要加上标签。对于这个问题,这可能是一种过火的方法,但我使用这种方法来学习更多关于线程的知识,以便解决潜在的更大问题。rcv_数据可能包含多个字节。至少这是我对OP想要做什么的印象。当他得到字节时,输入字节,而不是一次输入一个字节。啊,是的,我忽略了这一部分。我只是把注意力集中在缓冲区上,我猜是+1,因为它教了我关于StringIO的知识,我以前没有遇到过它。@Grive:我认为StringIO,再加上你的答案就太好了。如果您愿意,可以随意将其添加到您的中。您可能希望在.truncate()而不是.reset()之前使用
return\u buffer.seek(0)
。io.StringIO或StringIO.StringIO中没有.reset()。它只存在于cStringIOBrilliant中!就我而言,性能并不是一个真正的问题。好主意!生成器生成单个字符的列表。列表没有.strip()方法。您可以添加
yield'。加入(返回缓冲区)
以修复it@J.F.Sebastian:我确实按照你上面的建议修改了“yield”。@J.F.Sebastian:如果splitOn超过一个字符,代码将无法正常工作,需要进行更多的更改,使其更加复杂。当我有更多的时间的时候,我可以看看这个。返回一个字符列表。我试图尽可能少地修改原始海报代码。:)@塞巴斯蒂安:刚才看到你在下面回答。这确实是我所想到的额外的复杂性好的,所以队列返回的数据正好有一个放入的数据。很高兴知道。谢谢@JS:我将其概括为:“你需要一个缓冲区”。如果
len(linesep)==1,并且对于大片段来说性能并不重要,那么最好是这样。如果linesep被分割成两个片段呢?还有,我对函数名为lines有点困惑,显然还有一个作用域变量名为lines。@GREEF:捕捉得好。没有递归调用,因此意外使用
可能有效。我已经编辑了答案。我已经添加了关于linesep和多个片段的注释。在queue.get上很好地使用了iter。我偷了它来编辑上面的内容。
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