我正在将我的脚趾浸入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”),将“余数”放在另一个缓冲区/列表中,但这需要在队列旁边处理临时缓冲区。 (我想另一种方法是一次只放一行(),但那里的乐趣在哪里?)
是否有一种更优雅的方式一次从队列中读取一行?
答案 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