gpt4 book ai didi

go - 在 Golang 中过滤字节流的正确方法?

转载 作者:IT王子 更新时间:2023-10-29 02:03:33 26 4
gpt4 key购买 nike

我想从命令中过滤 STDOUT,这样我只保留任何连续的\r 终止行 block 的第一行和最后一行(在很大程度上忽略进度指示器)。

这是我的尝试(原始代码做的更多,这是一个简化版本,但基本上过滤必须在输入进入时发生,而不是在结束时发生):

package main

import (
"bytes"
"fmt"
"os/exec"
)

var cr = []byte("\r")
var lf = []byte("\n")

func main() {
input1 := []byte("a\nb\n\nprogress 98%\r")
input2 := []byte("progress 99%\r")
input3 := []byte("progress 100%\r")
input4 := []byte("\n\nc\n")

var stream []byte
stream = append(stream, input1...)
stream = append(stream, input2...)
stream = append(stream, input3...)
stream = append(stream, input4...)

fmt.Printf("stream:\n%s\n", stream)

streamer := &myFilter{}
streamer.Write(input1)
streamer.Write(input2)
streamer.Write(input3)
streamer.Write(input4)
final := streamer.Bytes()

fmt.Printf("streamer:\n%s\n\n", final)

cmd := exec.Command("bash", "-c", "perl -e '$|++; print qq[a\nb\n\nprogress: 98%\r]; for (99..100) { print qq[progess: $_%\r]; sleep(1); } print qq[\n\nc\n]'")
cmd.Stdout = &myFilter{}
cmd.Start()
cmd.Wait()
fromCmd := cmd.Stdout.(*myFilter).Bytes()

fmt.Printf("fromCmd:\n%s\n", fromCmd)
}

type myFilter struct {
partialLine []byte
storage []byte
}

func (w *myFilter) Write(p []byte) (n int, err error) {
// in order to filter out all but the first and last line of a set of \r
// terminated lines (a progress bar), we need to collect whole \n terminated
// lines
lines := bytes.SplitAfter(p, lf)

if len(w.partialLine) > 0 || (len(lines) == 1 && !bytes.HasSuffix(p, lf)) {
w.partialLine = append(w.partialLine, lines[0]...)

partialComplete := false
if len(lines) > 1 {
lines = lines[1:]
partialComplete = true

} else {
lines = nil
if bytes.HasSuffix(p, lf) {
partialComplete = true
}
}

if partialComplete {
w.filterCR(w.partialLine)
w.partialLine = nil
}
}

lastLineIndex := len(lines) - 1
if lastLineIndex > -1 && !bytes.HasSuffix(p, lf) {
w.partialLine, lines = lines[lastLineIndex], lines[:lastLineIndex]
}

for _, line := range lines {
w.filterCR(line)
}

return len(p), nil
}

func (w *myFilter) filterCR(p []byte) {
if bytes.Contains(p, cr) {
lines := bytes.Split(p, cr)
w.store(lines[0])
w.store(lf)

if len(lines) > 2 {
w.store(lines[len(lines)-2])
w.store(lf)
}
} else {
w.store(p)
}
}

func (w *myFilter) store(p []byte) {
w.storage = append(w.storage, p...)
}

func (w *myFilter) Bytes() []byte {
if len(w.partialLine) > 0 {
w.filterCR(w.partialLine)
}
return w.storage
}

我的输出是:

stream:
a
b

progress 100%

c

streamer:
a
b

progress 98%
progress 100%

c


fromCmd:
a
b

ss: 100%
progess: 100%

c

我想要的是您从“fromCmd”看到的输出与我从“streamer”获得的输出相匹配。

我做错了什么,为什么我的实际输出看起来“损坏”,为什么实际命令运行的行为与我的“streamer”测试不同,过滤 STDOUT 的更好方法是什么?

最佳答案

您的部分线算法不适用于所有输入。

您可以将 myFilter 替换为 bufio.Scanner ,它将为您正确处理部分行缓冲,以及 []bytebytes.Buffer 来累积输出。

var out bytes.Buffer
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
p := scanner.Bytes()
lines := bytes.Split(p, cr)
out.Write(lines[0])
out.Write(lf)
if len(lines) > 1 {
out.Write(lines[len(lines)-1])
out.Write(lf)
}
}

关于go - 在 Golang 中过滤字节流的正确方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42206739/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com