4

私はPythonスレッドにつま先を浸しています。キューを介して*nix(シリアル)/devから文字/行データを返すサプライヤースレッドを作成しました。

演習として、キューからのデータを一度に1行ずつ消費したいと思います(行末記号として「\ n」を使用)。

私の現在の(単純な)解決策は、一度に1文字だけをキューに入れることです。そのため、コンシューマーは一度に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()すると、間違いなく失敗する可能性があります。

受信ロジックをより汎用的にし、複数文字のput()を処理できるようにしたいと思います。

次のアプローチは、rcv_data.partition( "\ n")を使用して、 "remainder"をさらに別のバッファー/リストに配置することですが、これには、キューと一緒に一時バッファーを調整する必要があります。(別のアプローチは、一度に1行だけput()することだと思いますが、その面白さはどこにありますか?)

キューから一度に1行ずつ読み取るためのよりエレガントな方法はありますか?

4

4 に答える 4

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()

編集:

JFセバスティアンが行区切り文字は複数文字である可能性があると指摘した後、私もそのケースを解決する必要がありました。また、jdiの回答からStringIOを使用しました。繰り返しになりますが、効率について話すことはできませんが、すべての場合(少なくとも私が考えることができるもの)は正しいと思います。これはテストされていないため、実際に実行するにはおそらくいくつかの調整が必要になります。最終的にこれにつながる答えをくれたJFセバスティアンと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)
于 2012-09-05T21:28:34.033 に答える
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
于 2012-09-05T21:20:00.577 に答える
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は複数のフラグメントにまたがってはなりません。

使用法:

for line in getlines(iter(queue.get, None)): # break on queue.put(None)
    process(line)
于 2012-09-05T22:07:39.787 に答える
1

キューに複数の行が存在する可能性があることに注意することが重要です。この関数は、指定されたキューからすべての行を返します(オプションで出力します)。

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
于 2015-10-12T21:12:31.733 に答える